<?php

/**
 * Provides a global endpoint for access to Zend_Cache
 * 
 * @package Atmail 
 * @subpackage Cache
 * @copyright Copyright (C) 2011 Atmail. All rights reserved.
 * @author Atmail
 */
class Atmail_Cache extends Zend_Cache {

    /**
     * Singleton instance
     * 
     * @var Zend_Cache_Backend
     */
    static $instance;
	static $cacheEnabled = false;
	static $memcacheEnabled = false;
	static $debug = false;
    static $_username = false;
	    
    const TYPE_MAIL 		= 1;
 	const TYPE_GRAPH		= 2;
 	const TYPE_PHOTOS		= 4;
 	const TYPE_DAV 			= 8;
	const TYPE_PLUGIN		= 16;
	const TYPE_VERSION		= 32;
    const TYPE_MAIL_UIDS	= 64;
    const TYPE_MAIL_PREVIEW = 128;
    const TYPE_MAIL_STORE	= 256;
    const TYPE_MAIL_CONTENT	= 512;
	const TYPE_MAIL_COMBINED = 705;
	const TYPE_ALL			= 65279;
	static $TYPE_MAP = 
		array(
			self::TYPE_GRAPH => array('folder' => 'graph', 'lifetime' => 3600),
			self::TYPE_PHOTOS => array('folder' => 'photos', 'lifetime' => 1440),
			self::TYPE_MAIL => array('folder' => 'mail', 'lifetime' => 1200),
			self::TYPE_DAV => array('folder' => 'dav', 'lifetime' => 120),
			self::TYPE_PLUGIN => array('folder' => 'plugin', 'lifetime' => 1440),
			self::TYPE_VERSION => array('folder' => 'version', 'lifetime' => 3600),
			self::TYPE_MAIL_UIDS => array('folder' => 'mail.uids', 'lifetime' => 3600),
			self::TYPE_MAIL_PREVIEW => array('folder' => 'mail.preview', 'lifetime' => 3600),
			self::TYPE_MAIL_STORE => array('folder' => 'mail.store', 'lifetime' => 3600),
			self::TYPE_MAIL_CONTENT => array('folder' => 'mail.content', 'lifetime' => 3600)
		);
	static function log($msg)
	{
		if(self::$debug)
			file_put_contents("php://stderr", $msg . "\n");
	}

	static function getCacheEnabled($cacheType = self::TYPE_ALL)
	{
		if(self::$cacheEnabled == null)
		{
			self::$cacheEnabled = Zend_Registry::get('config')->global['cacheEnabled'];
		}

		if($cacheType == self::TYPE_MAIL_STORE) $cacheType = 0;
		
		self::log("CACHE FOR : " . $cacheType . " : " . ((self::$cacheEnabled & $cacheType) ? 'ENABLED' : 'DISABLED'));
		
		return self::$cacheEnabled & $cacheType;
	}
	static function setUsername($username)
    {
        self::$_username = $username;
    }

	static function getUsername()
	{
		try
		{
            if(self::$_username !== false) return self::$_username;
			$auth = Zend_Auth::getInstance();
			$userData = $auth->getIdentity();
		}
		catch(Exception $e)
		{
			// ignore the error
			return false;
		}
		if($userData == null)
			return false;

		if(is_array($userData))
		{
			$account = $userData['user'];
		}
		else if (is_string($userData))
		{
			$account = $userData;	
		}
		
		if($account == '')
			return false;
	    self::$_username = $account;
		return $account;		
	}
	
	static function getCacheId($type, $idString)
	{
		if(!$account = self::getUsername()) return false;
		self::log("CACHE GENID: " . $type . " : " . $account . ":" . $type . ":" . $idString );

		return md5($account) . md5($type) . md5($idString);
	}
	
	static function generateCacheIdString($args)
	{
		$cacheIdString = '';
		if(!is_array($args))
		{
			$args = array($args);	
		}
		
		foreach($args as $arg)
		{
			if(is_array($arg))
			{
				$cacheIdString .= implode(",", $arg) . ":";
			}
			else
			{
				$cacheIdString .= $arg . ":";
			}
		}
		return rtrim($cacheIdString, ':');
	}
	
	static function instance($type)
	{
		if(!self::$instance )
		{
			$global = Zend_Registry::get('config')->global;
			$typeObj = Atmail_Cache::$TYPE_MAP[$type];
			self::log("CACHE INSTANCE: " . $type . " - CREATING NEW INSTANCE WITH folder/lifetime: " . $typeObj['folder'] . ":" . $typeObj['lifetime']);
			
			self::$memcacheEnabled = (array_key_exists('cacheType', $global) && $global['cacheType'] == 'memcache');
			
			if(self::$memcacheEnabled)
			{
				$frontend = 'Output';
				$backend = 'Memcached';
				$frontendOptions = array(
			   		'lifetime' => $typeObj['lifetime'],                   // cache lifetime of 30 seconds
			   		'automatic_serialization' => true  // this is the default anyways
				);
			$backendOptions = 
				array(
					'servers' => array(
						0 => array(
							'host'				=> $global['cacheMemcacheServer'], 
							'port'				=> $global['cacheMemcachePort'], 
							'persistent'		=> true, 
							'weight'			=> 1, 
							'timeout'			=> $global['cacheMemcacheTimeout'], 
							'retry_interval'	=> 15, 
							'status'			=> true, 
							'failure_callback'	=> ''
						)
					),
					
					'compatibility' => true,
				);
			}
			else
			{
				$frontend = 'Output';
				$backend = 'File';
				$frontendOptions = array(
			   		'lifetime' => $typeObj['lifetime'],                   // cache lifetime of 30 seconds
			   		'automatic_serialization' => true  // this is the default anyways
				);
                $backendOptions = array('cache_dir' => users::getTmpFolder($typeObj['folder'], self::getUsername()));
            }
			try
			{
				self::$instance = self::factory($frontend, $backend, $frontendOptions, $backendOptions);
			}
			catch(Exception $e)
			{
				// a cache exception should not fail the application.
				// here we must gracefully downgrade functionality
				file_put_contents("php://stderr", "WARNING: Cache system disabled due to error.\n Error details : " . $e->getMessage() . "\n");
				return false;
			}
		}
		return self::$instance;
	}

	static public function fetch($type, $id)
	{
		if(($cache = self::instance($type)) === false) return false;
		if(!self::getCacheEnabled($type)) return false;
		$cacheId = self::getCacheId($type, $id);
		if($cacheId === false)
		{
			self::log("CACHE FETCH: CACHE ID INVALID");
			return false;
		}

		$cacheItem = $cache->load($cacheId);

		self::_clean($type);
		
		self::log("CACHE FETCH: " . $type . ":" . $cacheId . " : " . ($cacheItem === false ? 'NOT FOUND' : 'FOUND'));
		
		return $cacheItem;
	}
	
	static public function store($type, $id, $data, $timeout = false)
	{
		if(($cache = self::instance($type)) === false) return false;
		if(!self::getCacheEnabled($type)) return false;
		$cacheId = self::getCacheId($type, $id);
		if($cacheId === false) return false;
		$cacheItem = $cache->save($data, $cacheId, array(), $timeout);

		self::log("CACHE STORE: " . $type . ":" . $cacheId . " : " . $timeout);
		
		self::_clean($type);
		
		return $cacheItem;
		
	}
	
	static public function _clean($type)
	{
		// 0.01 percent chance of clean up (disable if using memcache)
		if (!self::$memcacheEnabled && mt_rand(0, 10000) === 0)
		{
			self::log(" ***** CACHE CLEANUP TRIGGERED ***** ");
			if(($cache = self::instance($type)) === false) return false;
			$cache->clean(Zend_Cache::CLEANING_MODE_OLD);
		}

		return true;
	}
	
}
