<?php

namespace OfficeBrain\Bundle\CacheBundle\Manager;

use Symfony\Component\HttpFoundation\RequestStack;

use OfficeBrain\Bundle\CacheBundle\Setting\ServerSideCacheSetting;
use OfficeBrain\Bundle\CacheBundle\Setting\WebClientResponseCacheSetting;
use OfficeBrain\Bundle\CacheBundle\Setting\ApiResponseCacheSetting;

class RedisCache extends \Exception 
{
	private $port;
	private $host;
	private $redisClient = null;
	private $ttl = 3600;
	private $db = 0;
	
	function __construct($kernel, RequestStack $requestStack, $host, $port) 
	{
		if(ServerSideCacheSetting::$IS_CACHE_ENABLED) {

			if(extension_loaded('redis')) {
	
				$this->host = $host;
				$this->port = $port;
				$this->ttl = 3600;
				$this->db = 0;
				$this->kernel = $kernel;
				$this->request = $requestStack->getCurrentRequest();
				$this->init();
			}
			else {
				//throw new \Exception('Redis PHP extention not loaded');
			}
		}
		unset($host);
		unset($port);
		unset($kernel);
		unset($request);
		unset($cacheKeyPrefix);
		unset($cacheKeySeparator);
	}
	
	function __destruct() 
	{
		unset($this->host);
		unset($this->port);
		unset($this->ttl);
		unset($this->db);
		unset($this->kernel);
		unset($this->request);
		unset($this->hostName);
		unset($this->environment);
		unset($this->cacheKeyPrefix);
		unset($this->cacheKeyPrefix);
		unset($this->cacheKeySeparator);
		unset($this->redisClient);
	}

	public function isCacheEnabled($cacheType)
	{
		if(!$this->redisClient) {
			return false;
		}

		switch ($cacheType) {

			case 'API_RESPONSE':
				return ApiResponseCacheSetting::$IS_CACHE_ENABLED && in_array($this->host, ApiResponseCacheSetting::$CACHE_ENABLED_FOR_HOST_ARRAY) && in_array($this->environment, ApiResponseCacheSetting::$ENABLED_FOR_ENVIRONMENT_ARRAY);
				break;
				
			case 'WEB_CLIENT_RESPONSE':
				return WebClientResponseCacheSetting::$IS_CACHE_ENABLED && in_array($this->host, WebClientResponseCacheSetting::$CACHE_ENABLED_FOR_HOST_ARRAY) && in_array($this->environment, WebClientResponseCacheSetting::$ENABLED_FOR_ENVIRONMENT_ARRAY);
				break;
		}
		
	}
	
	private function init()
	{
		$this->environment = $this->kernel->getEnvironment();
		$this->cacheKeySeparator = ServerSideCacheSetting::$CACHE_KEY_SEPARATOR;
		$this->cacheKeyPrefix = ServerSideCacheSetting::$CACHE_KEY_PREFIX.$this->cacheKeySeparator;
		$this->hostName = (isset($_SERVER['HTTP_X_FORWARDED_HOST']) && $_SERVER['HTTP_X_FORWARDED_HOST']!='')?$_SERVER['HTTP_X_FORWARDED_HOST']:$this->request->getHost();
		$this->redisClient = new \Redis();
		$this->connect();
	}

	private function connect()
	{
		try {
			$this->redisClient->connect($this->host, $this->port);
			$this->redisClient->setOption(\Redis::OPT_PREFIX, $this->cacheKeyPrefix); // use custom prefix on all keys
			
			//$this->redisClient->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_NONE);   // don't serialize data
			//$this->redisClient->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);    // use built-in serialize/unserialize
			//$this->redisClient->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY);   // use igBinary serialize/unserialize
		}
		catch( Exception $e ) {
			throw $e;
		}
	}

	public function selectDb($databaseType) 
	{
		switch ($databaseType) {

			case 'edit':
				$this->db = 0;
				$this->redisClient->select($this->db);
				break;

			case 'view':
				$this->db = 1;
				$this->redisClient->select($this->db);
				break;

			case 'page':
				$this->db = 2;
				$this->redisClient->select($this->db);
				break;
		}
	}

	public function generateCacheKey($cacheKeyElementArray) 
	{
		return implode($this->cacheKeySeparator, $cacheKeyElementArray);	
	}
	
	public function set($key, $value, $ttl = null) 
	{
		try{ 
			$ttl = ($ttl && is_int($ttl))?$ttl:$this->ttl;
			$backupKey = $key.-'-backup-key';
			$this->redisClient->setex($backupKey, $ttl, $value);
			$this->renameKey($backupKey, $key);
		}
		catch( Exception $e ) {
			throw $e;
		}
	}
	
	public function get($key)
	{
		return $this->redisClient->get($key);
	}

	public function exist($key) 
	{
		try {
			return $this->redisClient->exists($key);
		}
		catch( Exception $e ) {
			throw $e;
		}
	}
 
	public function getKeysWithPrefix($keyPrefix) 
	{
		try {
			return $this->redisClient->keys($keyPrefix.'*');
		}
		catch( Exception $e ) {
			throw $e;
		}
	}
 
	public function renameKey($fromKey, $toKey) 
	{
		try {
			if($this->exist($fromKey)) {
				return $this->redisClient->rename($fromKey, $toKey);
			}
		}
		catch( Exception $e ) {
			throw $e;
		}
	}
 
	public function setKeyTimeout($key, $timeout) 
	{
		try {
			if($this->exist($toKey)) {
				return $this->redisClient->setTimeout($key, $timeout);
			}
		}
		catch( Exception $e ) {
			throw $e;
		}
	}
 
	public function delete($key) 
	{
		try{ 
			$this->redisClient->del( $key);
		}
		catch( Exception $e ) {
			throw $e;
		}
	}
	
	public function deleteAll() 
	{
		try{ 
			$this->redisClient->flushAll();
		}
		catch( Exception $e ) {
			throw $e;
		}
	}
	
	public function deleteAllWithPrefix($keyPrefixArray, $cacheType) 
	{
		try {
			$keyPrefix = implode(':', $keyPrefixArray);
			$this->selectDb($cacheType);
			$keyToDelete = $this->getKeysWithPrefix($keyPrefix);
			if($keyToDelete) {
				$command = 'redis-cli -h '.$this->host.' -p '.$this->port.' -n '.$this->db.' KEYS "'.$this->cacheKeyPrefix.$keyPrefix.'*" | xargs redis-cli -h '.$this->host.' -p '.$this->port.' -n '.$this->db.' DEL'; 
				exec($command);
			}
		}
		catch( Exception $e ) {
			throw $e;
		}
	}
	
	public function deleteAllFromCurrentDatabase()
	{
		try{ 
			$this->redisClient->flushDB();
		}
		catch( Exception $e ) {
			throw $e;
		}
	}
	
	
	public function deleteAllFromDatabase($databaseType)
	{
		try{ 
			$this->selectDb($databaseType);
			$this->redisClient->flushDB();
		}
		catch( Exception $e ) {
			throw $e;
		}
	}
	
	public function info() 
	{
		try{ 
			return $this->redisClient->info();
		}
		catch( Exception $e ) {
			throw $e;
		}
	}
}