<?php 

//
// Open Web Analytics - An Open Source Web Analytics Framework
//
// Copyright 2008 Peter Adams. All rights reserved.
//
// Licensed under GPL v2.0 http://www.gnu.org/copyleft/gpl.html
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// $Id$
//

/**
 * Service User Class
 * 
 * @author      Peter Adams <peter@openwebanalytics.com>
 * @copyright   Copyright &copy; 2008 Peter Adams <peter@openwebanalytics.com>
 * @license     http://www.gnu.org/copyleft/gpl.html GPL v2.0
 * @category    owa
 * @package     owa
 * @version		$Revision$	      
 * @since		owa 1.0.0
 */


class owa_state {

	var $stores = array();
	var $stores_meta = array();
	var $is_dirty;
	var $dirty_stores;
	var $default_store_type = 'cookie';
	var $stores_with_cdh = array();
	var $initial_state = array();
	
	function __construct() {
	
	}
	
	function __destruct() {
	
		return false;
	}
		
	function registerStore( $name, $expiration, $length = '', $format = 'json', $type = 'cookie', $cdh = null ) {
		
		$this->stores_meta[$name] = array(
			'expiration' 	=> $expiration, 
			'length' 		=> $length, 
			'format' 		=> $format, 
			'type' 			=> $type, 
			'cdh_required' 	=> $cdh
		);
		
		if ( $cdh ) {
			$this->stores_with_cdh[] = $name;
		}
	}
	
	
	
	public function get($store, $name = '') {
	
		owa_coreAPI::debug("Getting state - store: ".$store.' key: '.$name);
		//owa_coreAPI::debug("existing stores: ".print_r($this->stores, true));
		if ( ! isset($this->stores[$store] ) ) {
			$this->loadState($store);
		}
		
		if (array_key_exists($store, $this->stores)) {
		
			if (!empty($name)) {
				// check to ensure this is an array, could be a string.
				if (is_array($this->stores[$store]) && array_key_exists($name, $this->stores[$store])) {	
						
					return $this->stores[$store][$name];
				} else {
					return false;
				}
			} else {
				
				return $this->stores[$store];
			}
		} else {
			
			return false;
		}
	}
	
	function setState($store, $name = '', $value, $store_type = '', $is_perminent = false) {
	
		owa_coreAPI::debug(sprintf('populating state for store: %s, name: %s, value: %s, store type: %s, is_perm: %s', $store, $name, print_r($value, true), $store_type, $is_perminent));
		
		// set values
		if (empty($name)) {
			$this->stores[$store] = $value;
			//owa_coreAPI::debug('setstate: '.print_r($this->stores, true));
		} else {
			//just in case the store was set first as a string instead of as an array.
			if ( array_key_exists($store, $this->stores)) {
			
				if ( ! is_array( $this->stores[$store] ) ) {
					$new_store = array();
					// check to see if we need ot ad a cdh
					if ( $this->isCdhRequired($store) ) {
						$new_store['cdh'] = $this->getCookieDomainHash();
					}
					
					$new_store[$name] = $value;
					$this->stores[$store] = $new_store;
				
				} else {
					$this->stores[$store][$name] = $value;	
				}
			// if the store does not exist then	maybe add a cdh and the value
			} else {
			
				if ( $this->isCdhRequired($store) ) {
					$this->stores[$store]['cdh'] = $this->getCookieDomainHash();
				}
				
				$this->stores[$store][$name] = $value;
			}
			
		}
		
		$this->dirty_stores[] = $store;
		//owa_coreAPI::debug(print_r($this->stores, true));
	}
	
	function isCdhRequired($store_name) {
		
		if ( isset( $this->stores_meta[$store_name] ) ) {
			return $this->stores_meta[$store_name]['cdh_required'];
		}
	}

	function set($store, $name = '', $value, $store_type = '', $is_perminent = false) {
		
		if ( ! isset($this->stores[$store] ) ) {
			$this->loadState($store);
		}
		
		$this->setState($store, $name, $value, $store_type, $is_perminent);
		
		// persist immeadiately if the store type is cookie
		if ($this->stores_meta[$store]['type'] === 'cookie') {
			
			$this->persistState($store);
		}	
	}
	
	function persistState( $store ) {
		
		//check to see that store exists.
		if ( isset( $this->stores[ $store ] ) ) {
			owa_coreAPI::debug('Persisting state store: '. $store . ' with: '. print_r($this->stores[ $store ], true));	
			// transform state array into a string using proper format
			if ( is_array( $this->stores[$store] ) ) {
				switch ( $this->stores_meta[$store]['type'] ) {
				
					case 'cookie':
						
						// check for old style assoc format
						if ( $this->stores_meta[$store]['format'] === 'assoc' ) {
							$cookie_value = owa_lib::implode_assoc('=>', '|||', $this->stores[ $store ] );
						} else {				
							$cookie_value = json_encode( $this->stores[ $store ] );
						}
						
						break;
					
					default:
						
				}
			} else {
				$cookie_value = $this->stores[ $store ];	
			}
			// get expiration time
			$time = $this->stores_meta[$store]['expiration'];
			//set cookie
			owa_coreAPI::createCookie( $store, $cookie_value, $time, "/", owa_coreAPI::getSetting( 'base', 'cookie_domain' ) );
			
		} else {
			
			owa_coreAPI::debug("Cannot persist state. No store registered with name $store");
		}
	}

	function setInitialState($store, $value, $store_type = '') {
		
		if ($value) {
			$this->initial_state[$store] = $value;
		}
	}
	
	function loadState($store, $name = '', $value = '', $store_type = 'cookie') {
	
		//get possible values
		if ( ! $value && isset( $this->initial_state[$store] ) ) {
			$possible_values = $this->initial_state[$store];
		} else {
			return;
		}
		
		
		//count values
		$count = count($possible_values);
		// loop throught values looking for a domain hash match or just using the last value.
		foreach ($possible_values as $k => $value) {
			// check format of value
			
			if ( strpos( $value, "|||" ) ) {
				$value = owa_lib::assocFromString($value);
			} elseif ( strpos( $value, ":" ) ) {
				$value = json_decode($value);
				$value = (array) $value;
			} else {
				$value = $value;
			}
			
			if ( in_array( $store, $this->stores_with_cdh ) ) {
				
				if ( is_array( $value ) && isset( $value['cdh'] ) ) {
					
					$runtime_cdh = $this->getCookieDomainHash();
					$cdh_from_state = $value['cdh'];
					
					// return as the cdh's do not match
					if ( $cdh_from_state === $runtime_cdh ) {
						owa_coreAPI::debug("cdh match:  $cdh_from_state and $runtime_cdh");
						return $this->setState($store, $name, $value, $store_type);
					} else {
						// cookie domains do not match so we need to delete the cookie in the offending domain
						// which is always likely to be a sub.domain.com and thus HTTP_HOST.
						// if cookie is not deleted then new cookies set on .domain.com will never be seen by PHP
						// as only the sub domain cookies are available.
						owa_coreAPI::debug("Not loading state store: $store. Domain hashes do not match - runtime: $runtime_cdh, cookie: $cdh_from_state");
						//owa_coreAPI::debug("deleting cookie: owa_$store");
						//owa_coreAPI::deleteCookie($store,'/', $_SERVER['HTTP_HOST']);
						//unset($this->initial_state[$store]);
						//return;
					}
				} else {
					
					owa_coreAPI::debug("Not loading state store: $store. No domain hash found.");
					return;
				}
				
			} else {
				// just set the state with the last value
				if ( $k === $count - 1 ) {
					owa_coreAPI::debug("loading last value in initial state container for store: $store");
					return $this->setState($store, $name, $value, $store_type);
				}
			}
		}
	}
		
	function clear($store, $name = '') {
	
		if ( ! isset($this->stores[$store] ) ) {
			$this->loadState($store);
		}
		
		if ( array_key_exists( $store, $this->stores ) ) {
			
			if ( ! $name ) {
			
				unset( $this->stores[ $store ] );
				
				if ($this->stores_meta[$store]['type'] === 'cookie') {
				
					return owa_coreAPI::deleteCookie($store);	
				}
			
			} else {
				
				if ( array_key_exists( $name, $this->stores[ $store ] ) ) {
					unset( $this->stores[ $store ][ $name ] );
					
					if ($this->stores_meta[$store]['type'] === 'cookie') {
					
						return $this->persistState( $store );	
					}
				}
			}	
		}		
	}
	
	function getPermExpiration() {
	
		$time = time()+3600*24*365*15;
		return $time;
	}
	
	function addStores($array) {
		
		$this->stores = array_merge($this->stores, $array);
		return;
	}
	
	function getCookieDomainHash($domain = '') {
		
		if ( ! $domain ) {
			$domain = owa_coreAPI::getSetting( 'base', 'cookie_domain' );
		}
		
		return owa_lib::crc32AsHex($domain);
	}
}

?>