<?php
use ScssPhp\ScssPhp\Compiler;
use ScssPhp\ScssPhp\Version;

/**
 * Class for using SCSS files
 *
 * @link https://github.com/leafo/scssphp/
 */
class Minify_ScssCssSource extends Minify_Source
{
    /**
     * @var Minify_CacheInterface
     */
    private $cache;

    /**
     * Parsed SCSS cache object
     *
     * @var array
     */
    private $parsed;

    /**
     * @inheritdoc
     */
    public function __construct(array $spec, Minify_CacheInterface $cache)
    {
        parent::__construct($spec);

        $this->cache = $cache;
    }

    /**
     * Get last modified of all parsed files
     *
     * @return int
     */
    public function getLastModified()
    {
        $cache = $this->getCache();

        return $cache['updated'];
    }

    /**
     * Get content
     *
     * @return string
     */
    public function getContent()
    {
        $cache = $this->getCache();

        return $cache['content'];
    }

    /**
     * Make a unique cache id for for this source.
     *
     * @param string $prefix
     *
     * @return string
     */
    private function getCacheId($prefix = 'minify')
    {
        $md5 = md5($this->filepath);

        return "{$prefix}_scss_{$md5}";
    }

    /**
     * Get SCSS cache object
     *
     * Runs the compilation if needed
     *
     * Implements Leafo\ScssPhp\Server logic because we need to get parsed files without parsing actual content
     *
     * @return array
     */
    private function getCache()
    {
        // cache for single run
        // so that getLastModified and getContent in single request do not add additional cache roundtrips (i.e memcache)
        if (isset($this->parsed)) {
            return $this->parsed;
        }

        // check from cache first
        $cache = null;
        $cacheId = $this->getCacheId();
        if ($this->cache->isValid($cacheId, 0)) {
            if ($cache = $this->cache->fetch($cacheId)) {
                $cache = unserialize($cache);
            }
        }

        $input = $cache ? $cache : $this->filepath;

        if ($this->cacheIsStale($cache)) {
            $cache = $this->compile($this->filepath);
        }

        if (!is_array($input) || $cache['updated'] > $input['updated']) {
            $this->cache->store($cacheId, serialize($cache));
        }

        return $this->parsed = $cache;
    }

    /**
     * Determine whether .scss file needs to be re-compiled.
     *
     * @param array $cache Cache object
     *
     * @return boolean True if compile required.
     */
    private function cacheIsStale($cache)
    {
        if (!$cache) {
            return true;
        }

        $updated = $cache['updated'];
        foreach ($cache['files'] as $import => $mtime) {
            $filemtime = filemtime($import);

            if ($filemtime !== $mtime || $filemtime > $updated) {
                return true;
            }
        }

        return false;
    }

    /**
     * Compile .scss file
     *
     * @param string $filename Input path (.scss)
     *
     * @see Server::compile()
     * @return array meta data result of the compile
     */
    private function compile($filename)
    {
        $start = microtime(true);
        $scss = new Compiler();

        // set import path directory the input filename resides
        // otherwise @import statements will not find the files
        // and will treat the @import line as css import
        $scss->setImportPaths(dirname($filename));

        $result = $scss->compileString(file_get_contents($filename), $filename);
        $css = $result->getCss();
        $elapsed = round((microtime(true) - $start), 4);

        $v = Version::VERSION;
        $ts = date('r', (int) $start);
        $css = "/* compiled by scssphp $v on $ts ({$elapsed}s) */\n\n" . $css;

        // scssphp 2.x removed Compiler::getParsedFiles(), use CompilationResult::getIncludedFiles() instead
        // scssphp 1.x uses Compiler::getParsedFiles() which returns [file => mtime]
        if (method_exists($scss, 'getParsedFiles')) {
            // scssphp 1.x
            $imports = $scss->getParsedFiles();
            $updated = 0;
            foreach ($imports as $mtime) {
                $updated = max($updated, $mtime);
            }
        } else {
            // scssphp 2.x: getIncludedFiles() returns file paths only
            $includedFiles = $result->getIncludedFiles();
            $imports = array();
            $updated = 0;
            foreach ($includedFiles as $file) {
                $mtime = filemtime($file);
                $imports[$file] = $mtime;
                $updated = max($updated, $mtime);
            }
        }

        return array(
            'elapsed' => $elapsed, // statistic, can be dropped
            'updated' => $updated,
            'content' => $css,
            'files' => $imports,
        );
    }
}
