<?php
/**
 * ###############################################
 *
 * SWIFT Framework
 * _______________________________________________
 *
 * @author         Abhishek Mittal
 *
 * @package        SWIFT
 * @copyright      Copyright (c) 2001-2013, Kayako
 * @license        http://www.kayako.com/license
 * @link           http://www.kayako.com
 *
 * ###############################################
 */

/**
 * The Main Memcache Management Class
 *
 * @author Abhishek Mittal
 */
class SWIFT_CacheMemcache extends SWIFT_Cache implements SWIFT_Cache_Interface
{
	const COMPRESS_MEMCACHE = 2;
	const DEFAULT_PORT      = 11211;

	/**
	 * @var Memcached
	 */
	private $Memcached;

	private $_hostContainer = array();

	/**
	 * Constructor
	 *
	 * @author Abhishek Mittal
	 *
	 * @param array $_hostContainer
	 *
	 * @throws SWIFT_Exception Invalid Data Provided
	 */
	public function __construct($_hostContainer)
	{
		parent::__construct();

		if (!_is_array($_hostContainer)) {
			throw new SWIFT_Exception(__CLASS__ . ': ' . SWIFT_INVALIDDATA);
		}

		$this->Memcached = new memcached();

		foreach ($_hostContainer as $_host) {
			$this->AddHost($_host['hostname'], $_host['port']);
		}

		if (!$this->Connect()) {
			throw new SWIFT_Exception(__CLASS__ . ': Memcache connection failed');
		}
	}

	/**
	 * Connect to server
	 *
	 * @author Abhishek Mittal
	 *
	 * @return bool "true" on Success, "false" otherwise
	 */
	protected function Connect()
	{
		$_hostContainer = $this->GetHosts();

		$_memcachedHosts = array();

		foreach ($_hostContainer as $_host) {
			$_memcachedHosts[] = array($_host['hostname'], $_host['port']);
		}

		return $this->Memcached->addServers($_memcachedHosts);
	}

	/**
	 * Add server
	 *
	 * @author Abhishek Mittal
	 *
	 * @param string $_hostName
	 * @param int    $_port (OPTIONAL)
	 *
	 * @return SWIFT_CacheMemcache
	 * @throws SWIFT_Exception If Invalid Data Provided
	 */
	public function AddHost($_hostName, $_port = self::DEFAULT_PORT)
	{
		if (empty($_hostName)) {
			throw new SWIFT_Exception(SWIFT_INVALIDDATA);
		}

		$this->_hostContainer[] = array('hostname' => $_hostName, 'port' => intval($_port));

		return $this;
	}

	/**
	 * Retrieve the currently set host container array
	 *
	 * @author Abhishek Mittal
	 *
	 * @return array
	 */
	public function GetHosts()
	{
		return $this->_hostContainer;
	}

	/**
	 * Store data at the server
	 *
	 * @author Abhishek Mittal
	 *
	 * @param string $_keyName
	 * @param mixed  $_keyData
	 * @param int    $_expiry
	 *
	 * @return SWIFT_CacheMemcache
	 * @throws SWIFT_Exception Invalid Data Provided
	 */
	public function Set($_keyName, $_keyData, $_expiry = self::DEFAULT_EXPIRY)
	{
		if (empty($_keyName)) {
			throw new SWIFT_Exception(SWIFT_INVALIDDATA);
		}

		$_keyName = $this->GetProcessedKeyName($_keyName);

		$this->Memcached->set($_keyName, $_keyData, $_expiry);

		return $this;
	}

	/**
	 * Store data at the server
	 *
	 * @author Abhishek Mittal
	 *
	 * @param array  $_keyContainer
	 * @param int    $_expiry		(OPTIONAL)
	 *
	 * @return SWIFT_CacheMemcache
	 * @throws SWIFT_Exception Invalid Data Provided
	 */
	public function SetMultiple($_keyContainer, $_expiry = self::DEFAULT_EXPIRY)
	{
		if (!_is_array($_keyContainer)) {
			throw new SWIFT_Exception(SWIFT_INVALIDDATA);
		}

		$_finalKeyContainer = array();
		foreach ($_keyContainer as $_keyName => $_value) {
			$_finalKeyContainer[$this->GetProcessedKeyName($_keyName)] = $_value;
		}

		$this->Memcached->setMulti($_finalKeyContainer, $_expiry);

		return $this;
	}

	/**
	 * Returns the value stored in the memory by it's key
	 *
	 * @author Abhishek Mittal
	 *
	 * @param string $_keyName
	 *
	 * @return mixed
	 * @throws SWIFT_Exception Invalid Data Provided
	 */
	public function Get($_keyName)
	{
		if (empty($_keyName)) {
			throw new SWIFT_Exception(SWIFT_INVALIDDATA);
		}

		$_keyNameList = func_get_args();
		if (count($_keyNameList) > 1) {
			return $this->GetMultiple($_keyNameList);
		}

		$_keyName = $this->GetProcessedKeyName($_keyName);

		return $this->Memcached->get($_keyName);
	}

	/**
	 * Retrive multiple values provided in the keys
	 *
	 * @author Abhishek Mittal
	 *
	 * @param array $_keyNameList
	 *
	 * @return array|bool
	 */
	public function GetMultiple($_keyNameList)
	{
		if (!_is_array($_keyNameList)) {
			return false;
		}

		$_finalKeyNameList = array();
		foreach ($_keyNameList as $_keyName) {
			$_finalKeyNameList[] = $this->GetProcessedKeyName($_keyName);
		}

		$_cacheMemcacheResultContainer = $this->Memcached->getMulti($_finalKeyNameList);

		$_finalCacheMemcacheResult = array();

		foreach ($_cacheMemcacheResultContainer as $_keyName => $_cacheMemcacheResultList) {

			$_splitKeyName = $this->SplitKeyName($_keyName);

			$_finalCacheMemcacheResult[$_splitKeyName] = $_cacheMemcacheResultList;
		}

		if (empty($_finalCacheMemcacheResult)) {
			return false;
		}

		return $_finalCacheMemcacheResult;
	}

	/**
	 * Flush the Cache
	 *
	 * @author Abhishek Mittal
	 *
	 * @return SWIFT_CacheMemcache
	 */
	public function Flush()
	{
		$this->Memcached->flush();

		return $this;
	}

	/**
	 * Delete the Cache for a given key
	 *
	 * @author Abhishek Mittal
	 *
	 * @param string $_keyName
	 *
	 * @return SWIFT_CacheMemcache
	 * @throws SWIFT_Exception Invalid Data Provided
	 */
	public function Delete($_keyName)
	{
		if (empty($_keyName)) {
			throw new SWIFT_Exception(SWIFT_INVALIDDATA);
		}

		$_keyNameList = func_get_args();
		if (count($_keyNameList) > 1) {
			return $this->DeleteMultiple($_keyNameList);
		}

		$_keyName = $this->GetProcessedKeyName($_keyName);

		$this->Memcached->delete($_keyName);

		return $this;
	}

	/**
	 * Delete multiple keys from cache in one go
	 *
	 * @author Abhishek Mittal
	 *
	 * @param array $_keyNameList
	 *
	 * @return SWIFT_CacheMemcache
	 */
	public function DeleteMultiple($_keyNameList)
	{
		if (!_is_array($_keyNameList)) {
			return false;
		}

		$_finalKeyNameList = array();
		foreach ($_keyNameList as $_keyName) {
			$_finalKeyNameList[] = $this->GetProcessedKeyName($_keyName);
		}

		$this->Memcached->deleteMulti($_finalKeyNameList);

		return $this;
	}

	/**
	 * Split CRC code from key name
	 *
	 * @author Abhishek Mittal
	 *
	 * @param string $_keyName
	 *
	 * @return string $_keyName
	 */
	public function SplitKeyName($_keyName)
	{
		return substr($_keyName, strlen($this->GetHash().SWIFT_Cache::KEY_SEPARATOR));
	}
}