<?php

namespace CCDN\Helpers;


use FilesystemIterator;

/**
 * Class Cache
 * This class save cache only in file and save only string data
 *
 * @package CCDN\Helpers
 */
class Cache
{
    /**
     * @var string Path to DLE cache folder
     */
    private $folder = ENGINE_DIR.'/cache';

    /**
     * @var string
     */
    private $fileExtension = '.bin';

    /**
     * @var string
     */
    private $prefix;

    /**
     * @var FilesystemIterator
     */
    private $filesystemIterator;

    /**
     * Cache constructor.
     */
    public function __construct()
    {
        $version = str_replace('.', '_', Settings::PLUGIN_VERSION);
        $this->prefix = Settings::PLUGIN_NAME.'_'.$version.'_';
        $this->filesystemIterator = new FilesystemIterator($this->folder);
    }

    /**
     * Fetches a value from the cache.
     *
     * @param  string  $key  The unique key of this item in the cache.
     * @param  mixed  $default  Default value to return if the key does not exist.
     *
     * @return string|null The value of the item from the cache, or $default in case of cache miss.
     */
    public function get($key, $default = null)
    {
        $cacheFile = $this->getFilePath($key);

        if (@filemtime($cacheFile) > time()) {
            $fp = @fopen($cacheFile, 'rb');
            if ($fp !== false) {
                @flock($fp, LOCK_SH);
                $cacheValue = @stream_get_contents($fp);
                @flock($fp, LOCK_UN);
                @fclose($fp);
                return $cacheValue !== false ? $cacheValue : $default;
            }
        }
        return $default;
    }

    /**
     * Return full path to cache file
     *
     * @param  string  $key
     *
     * @return string
     */
    protected function getFilePath($key)
    {
        $key = $this->buildKey($key);

        return $this->folder.'/'.$key.$this->fileExtension;
    }

    /**
     * @param  string  $key
     *
     * @return string
     */
    protected function buildKey($key)
    {
        return $this->prefix.md5($key);
    }

    /**
     * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
     *
     * @param  string  $key  The key of the item to store.
     * @param  string  $value  The value of the item to store. Must be string.
     * @param  int  $ttl  Optional. The TTL value of this item. If no value is sent and
     *                                      the driver supports TTL then the library may set a default value
     *                                      for it or let the driver take care of that.
     *
     * @return bool True on success and false on failure.
     */
    public function set($key, $value, $ttl = 0)
    {
        $this->gc();
        $cacheFile = $this->getFilePath($key);
        if (is_file($cacheFile)) {
            @unlink($cacheFile);
        }
        if (@file_put_contents($cacheFile, $value, LOCK_EX) !== false) {
            @chmod($cacheFile, 0666);
            if ($ttl <= 0) {
                $ttl = 31536000;
            }
            return @touch($cacheFile, $ttl + time());
        }

        return false;
    }

    /**
     * Garbage collector Removing expired cache files under a directory.
     *
     * @return void
     */
    protected function gc()
    {
        $time = time();
        foreach ($this->filesystemIterator as $file) {
            if ($file->isDir() || strpos($file->getFilename(), $this->prefix) === false) {
                continue;
            }
            if ($file->getMTime() < $time) {
                @unlink($file->getRealPath());
            }
        }
    }

    /**
     * Determines whether an item is present in the cache.
     *
     * @param  string  $key  The cache item key.
     *
     * @return bool
     */
    public function has($key)
    {
        return @filemtime($this->getFilePath($key)) > time();
    }

    /**
     * Wipes all cache.
     *
     * @return bool True on success and false on failure.
     */
    public function clear()
    {
        foreach ($this->filesystemIterator as $file) {
            if ($file->isDir() || strpos($file->getFilename(), $this->prefix) === false) {
                continue;
            }
            @unlink($file->getPathname());
        }

        return true;
    }

    /**
     * Delete cache file by key
     *
     * @param $key
     * @return bool
     */
    public function delete($key)
    {
        return @unlink($this->getFilePath($key));
    }

}

