<?php
namespace Engine\Storage;

/**
 * Class used to save data to a persistent storage
 * @author Lucian Sirbu
 */
class Disk implements \Engine\Storage\BaseInterface
{
	/**
	 * The full location where the cache files are stored.
	 * @var String
	 */
	protected $dataPath='';

	/**
	 * The class constructor.
	 */
	public function __construct()
	{
		global $CFG;

		// realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors.
		$this->dataPath = realpath(MODULE_REL_PATH.$CFG['paths']['files']).'/'.$CFG['storage']['disk']['location'];
	}

	/**
	 * Returns some associative array of data determined from the key
	 * @param Array $key
	 */
	private function getFileDetails($key)
	{
		$return=[];
		// rewrite the key
		$key=$key;
		// the key hash
		$return['keyHash'] = md5($key);
		// the subdir for the current key
		$keyDir = substr($return['keyHash'], 0, 2);
		// the full file location
		$return['fileLocation'] = $this->dataPath.$keyDir.'/';
		$return['fileName'] = $return['keyHash'].'.data';
		return $return;
	}

	/**
	 * (non-PHPdoc)
	 * @see \Engine\Storage\BaseInterface::set()
	 */
	public function set($key, $value)
	{
		// if key is not set
		if (!$key) {
			throw new \Exception("The key wasn't specified");
		}

		// do not store 'null' values
		if ($value===null) {
			return false;
		}

		// FILE AND DIRECTORY MANAGEMENT
		$params = $this->getFileDetails($key);

		// attempt to create the directory chain
		if (!\Engine\Dir::create($params['fileLocation'])) {
			throw new \Exception("Cannot create stored data folder");
		}


		// FILE CONTENT ---------------------------------------------------------------------------
		$fileContent = serialize($value);

		// WRITE THE CACHE FILE -------------------------------------------------------------------
		if (!\Engine\File::create($params['fileLocation'].$params['fileName'], $fileContent)) {
			throw new \Exception(sprintf("Cannot set key in storage! key: %s, value: %s", $key, $value));
		}

		return true;
	}

	/**
	 * (non-PHPdoc)
	 * @see \Engine\Storage\BaseInterface::get()
	 */
	public function get($key)
	{
		// if key is not set
		if (!$key) {
			throw new \Exception("The key wasn't specified");
		}

		// get file params
		$params=$this->getFileDetails($key);

		// check the file
		if (!is_file($params['fileLocation'].$params['fileName'])) {
			return null;
		}

		// READ THE FILE --------------------------------------------------------------------------
		$fileContents = \Engine\File::getContents($params['fileLocation'].$params['fileName']);

		return unserialize($fileContents);
	}

	/**
	 * (non-PHPdoc)
	 * @see \Engine\Storage\BaseInterface::delete()
	 */
	public function delete($key)
	{
		// if key is not set
		if (!$key) {
			throw new \Exception("The key wasn't specified");
		}

		// get file params
		$params = $this->getFileDetails($key);

		// check the file
		if (!is_file($params['fileLocation'].$params['fileName'])) {
			return true;
		}

		// delete the file
		$return = \Engine\File::delete($params['fileLocation'].$params['fileName']);

		return (Bool)$return;
	}

	/**
	 * (non-PHPdoc)
	 * @see \Engine\Storage\BaseInterface::flush()
	 */
	public function flush()
	{
		return \Engine\Dir::delete($this->dataPath);
	}

	/**
	 * (non-PHPdoc)
	 * @see \Engine\Storage\BaseInterface::getInfo()
	 */
	public function getInfo($key)
	{
		// if key is not set
		if (!$key) {
			throw new \Exception("The key wasn't specified");
		}

		$return = [];

		// associate the key
		$return['key'] = $key;

		// get more details
		$params = $this->getFileDetails($key);
		$fullFileName = $params['fileLocation'].$params['fileName'];

		// CHECK IF THE FILE EXISTS
		if (!is_file($fullFileName)) {
			$return['fileExists'] = false;
		}
		else {
			// associate some more keys
			$return['fileExists'] = true;

			$return['keyHash'] = $params['keyHash'];
			$return['fileLocation'] = $params['fileLocation'];
			$return['fileName'] = $params['fileName'];
			$return['fileSize'] = filesize($fullFileName);
		}

		return $return;
	}
}
