<?php

namespace xmvc;

use xmvc\MVC;
use xmvc\libCache\CacheApcu;
use xmvc\libCache\CacheFile;
use xmvc\libCache\CacheRedis;
use xmvc\interface\ICache;

/**
* 缓存
*/
class Cache
{
    public static $netObjectList =  []; //远程连接对象缓存列表
    /** 缓存文件的缓存目录 */
    public $cacheFileDir =  DIR_CACHE . '/files/default';
    public \Closure $customCacheFileKey;
    public \Closure $customCacheFileDir;

    /**
     * 创建一个缓存类
     *
     * @param string $type 缓存类型 file, apcu, redis
     * @param string $groupName 缓存组名称(变量格式), 默认 default, 如果是 file 类型刚一个组一个缓存文件
     * @param string $dns 配置项, 缓存类中专用于 redis
     * @return ICache
     */
    public static function create(string $type, string $groupName = 'default', string $dns = ''): ICache
    {
        $types = ['file', 'apcu', 'redis'];
        if (!in_array($type, $types)) {
            error(lg('未知的缓存类型  {:1} 只支持 {:2}', $type, implode(',', $types)), null, [[__FILE__, __LINE__]]);
        }
        if (!preg_match('/^[a-zA-z0-9\_]+$/', $groupName)) {
            error('[ ' . $groupName . ' ] ' . lg('非法的缓存组名称') . ', ' . lg('仅允许字母数字与下划线'), null, [[__FILE__, __LINE__]]);
        }

        if ($type == 'file') {
            $cacheObject = new CacheFile($groupName);
        } elseif ($type == 'apcu') {
            $cacheObject = new CacheApcu($groupName);
        } elseif ($type == 'redis') {
            $cacheObject = new CacheRedis($groupName, $dns ? cfg($dns) : []);
        }
        return $cacheObject;
    }

    /**
     * 缓存是否存在
     */
    public static function exists(string $type, string $groupName = 'default'): bool
    {
        if ($type == 'file') {
            return CacheFile::groupExists($groupName);
        } elseif ($type == 'apcu') {
            return CacheApcu::groupExists($groupName);
        } elseif ($type == 'redis') {
            return CacheRedis::groupExists($groupName);
        }
    }

    /**
     * 获取 $groupName 的 唯一标识
     */
    public static function groupNameGUID(string $groupName): string
    {
        return SITE_GUID . '_' . $groupName;
    }

    /**
     * 设置缓存文件子目录, 相对于 DIR_CAHCE . '/files/'
     *
     * @param string $dir 相对目录
     */
    public function setCacheFileDir(string $dir): self
    {
        $this->cacheFileDir = fdir(DIR_CACHE . '/files/' . $dir);
        mkdirs($this->cacheFileDir);
        return $this;
    }

    /**
     * 设置缓存绝对路径目录, 默认是 '/home/wwwoor/test'
     *
     * @param string $absDir 绝对目录
     */
    public function setCacheFileAbsDir(string $absDir): self
    {
        $this->cacheFileDir = fdir($absDir);
        mkdirs($this->cacheFileDir);
        return $this;
    }

    public function getCacheFileKey(string $key): string
    {
        if (!isset($this->customCacheFileKey)) {
            return md5($key);
        } else {
            return ($this->customCacheFileKey)($key, $this);
        }
    }

    public function getCacheFileDir(string $key): string
    {
        if (!isset($this->customCacheFileDir)) {
            return $this->cacheFileDir . '/' . substr($key, 0, 2) . '/' . substr($key, 2, 2);
        } else {
            return ($this->customCacheFileDir)($key, $this);
        }
    }

    public function getCacheFileName(string $key): string
    {
        $key = $this->getCacheFileKey($key);
        $dir = $this->getCacheFileDir($key);
        $filename = '';
        if ($dir) {
            $filename .= $dir . '/';
        }
        $filename .= $key;
        if (substr($key, -4) != '.php') {
            $filename .= '.php';
        }
        return $filename;
    }

    public function getCacheFile(string $key)
    {
        $filename = $this->getCacheFileName($key);
        $tag = '';
        $tmp = is_file($filename) ? require($filename) : null;
        if (is_null($tmp)) {
            return null;
        } elseif (is_array($tmp)) {
            return $tmp;
        } elseif ($tag == 'SERIALIZE') {
            return unserialize($str);
        } elseif ($tag == 'STRING') {
            return $str;
        } else {
            return null;
        }
    }

    public function getCacheFileTime(string $key): int
    {
        $filename = $this->getCacheFileName($key);
        return is_file($filename) ? filemtime($filename) : 0;
    }

    public function saveCacheFile(string $key, $data): self
    {
        $key = $this->getCacheFileKey($key);
        $dir = $this->getCacheFileDir($key);
        $filename = $dir . '/' . $key . '.php';
        mkdirs($dir);
        $type = gettype($data);
        switch ($type) {
            case 'array':
                arrayToFile($filename, $data, 0777);
                break;
            case 'object':
                file_put_contents_lock(
                    $filename,
                    "<?php\r\n\$tag='SERIALIZE';\r\n\$str=<<<EOF\r\n" . serialize($data) . "\r\nEOF;\r\n?>"
                );
                chmod($filename, 0777);
                break;
            default:
                file_put_contents_lock(
                    $filename,
                    "<?php\r\n\$tag='STRING';\r\n\$str=<<<EOF\r\n" . $data . "\r\nEOF;\r\n?>"
                );
                chmod($filename, 0777);
        }
        return $this;
    }

    public function deleteCacheFile(string $key): self
    {
        $filename = $this->getCacheFileName($key);
        if (is_file($filename)) {
            unlink($filename);
        }
        return $this;
    }

    /**
     * 删除所有缓存目录与文件
     * @param bool $deleteSelf 是否删除自已, 如果为 false 则只删除目录下的子目录及文件
     */
    public function deleteAllCacheFile($deleteSelf = true): self
    {
        if (is_dir($this->cacheFileDir)) {
            rmdirs($this->cacheFileDir, $deleteSelf);
        }
        return $this;
    }
}
