<?php
/**
 * memory_table.php - Modle for the memory table hanlding
 *
 * @author $Author: dtong $
 * @version $Id: memory_table_model.php,v 1.3 2011/02/11 09:09:25 dtong Exp $
 * @copyright Copyright (c) 2010, Tiller Software Co., Ltd.
*/

class Memory_table_model extends CI_Model {
private $current_table_number;
private $current_update_time;
private $is_dirty;

	function __construct() {
		parent::__construct();
	}

	//Check to see is the memory table empty or not, if the mem table config is empty then it is assumed empty
	function is_empty($table) {
		$table_number = $this->current_table_number($table);
		if ( $table_number === FALSE )
			return TRUE;

		$table = $this->db->dbprefix($table.$table_number);
		$sql = "select count(*) c from $table";
		$res = $this->db->query($sql);
		if ( $res->num_rows() <= 0 )
			return TRUE;
		$array = $res->result();
		if ( $array[0]->c > 0 )
			return FALSE;
		return TRUE;
	}

	//This function is fine even if the mem tab config is empty
	function set_dirty_flag($table) {
		$table_config = $this->db->dbprefix('memory_table_config');
		$sql = "update $table_config set dirty=1 where name='$table'";
		$this->db->query($sql);
		$this->is_dirty = TRUE;
	}

	function is_dirty($table) {
		$num = $this->current_table_number($table);
		if ( !$num )
			return TRUE;
		return $this->is_dirty;
	}

	//Does the table update and also update the config
	function update($table_orig, $sql) {
		$number = $this->current_table_number($table_orig, FALSE);
		$new_number = 1;
		if ( $number == 1 )
			$new_number = 2;

		$table = $this->db->dbprefix($table_orig.$new_number);
		$table_config = $this->db->dbprefix('memory_table_config');

		$db_date = osa_dbdate();
		$sql = sprintf($sql, $table);
		$sql_delete = "delete from $table";
		$sql_disable_keys = "alter table $table disable keys";
		$sql_enable_keys = "alter table $table enable keys";
		if ( $number === FALSE )
			$sql_update_config = "insert into $table_config (name,number,update_time) values ('{$table_orig}',{$new_number},'{$db_date}')";
		else
			$sql_update_config = "update $table_config set number={$new_number},update_time='{$db_date}',dirty=0 where name='{$table_orig}'";

		if ( $this->db->query($sql_delete) && $this->db->query($sql_disable_keys) &&
			   $this->db->query($sql) && $this->db->query($sql_enable_keys) &&
			  $this->db->query($sql_update_config) ) {
			if ( isset($this->current_table_number) ) {
				//Update the table number cache so we would have the right number in subsequent calls
				//If it is not set then subsequent calls will get it from the DB
				$this->current_table_number = $new_number;
			}
			return TRUE;
		}
		//if any one query fails it is still able to perform searches using the old table
		//and hope the next update will work
		return FALSE;
	}

	//Application lock using mysql so no 2 threads can do the update at the same time
	function lock($name, $timeout) {
		$sql = "select get_lock('$name', $timeout) retval";
		$res = $this->db->query($sql);
		if ( $res->num_rows() <= 0 ) {
			osa_errorlog(__METHOD__ . " - Failed to get lock $name and got no result back from MySQL. This should never happen.");
			return FALSE;
		}
		$array = $res->result();
		$value = $array[0]->retval;
		if ( $value == 1 )
			return TRUE;
		if ( $value === NULL ) {
			osa_errorlog(__METHOD__ . " - Failed to get lock $name (timeout - $timeout seconds) and got NULL, this means MySQL DB error like no memory.", $array);
		}
		else {
			osa_errorlog(__METHOD__ . " - Failed to get lock $name (timeout - $timeout seconds) and got 0, this means someone else has the lock.", $array);
		}
		return FALSE;
	}

	//Unlocks the application lock after update is done
	function unlock($name) {
		$sql = "select release_lock('$name') retval";
		$res = $this->db->query($sql);
		if ( $res->num_rows() <= 0 ) {
			osa_errorlog(__METHOD__ . ' - Failed to release lock and got no result back from MySQL. This should never happen.');
			return FALSE;
		}
		$array = $res->result();
		$value = $array[0]->retval;
		if ( $value == 1 )
			return TRUE;
		if ( $value === NULL ) {
			osa_errorlog(__METHOD__ . " - Failed to release lock $name. Lock did not exist.", $array);
		}
		else {
			osa_errorlog(__METHOD__ . " - Failed to release lock $name. Lock does exist but does not belong to this thread.", $array);
		}
		return FALSE;
	}

	//Gets the time difference between last update and the current time
	function time_diff($table) {
		$update_time = 0;
		if ( $this->current_table_number($table) !== FALSE ) //This will get the update time from the table or cache
			$update_time = strtotime($this->current_update_time);
		return (time() - $update_time);
	}

	//Gets the current active table number, 1 or 2
	function current_table_number($table, $use_cache=TRUE) {
		if ( $use_cache && isset($this->current_table_number) ) {
			return $this->current_table_number;
		}
		$res = $this->db->get_where('memory_table_config', array('name'=>$table));
		if ( $res->num_rows() <= 0 )
			return FALSE;
		$array = $res->result();
		$this->current_table_number = $array[0]->number;
		$this->current_update_time = $array[0]->update_time;
		$this->is_dirty = $array[0]->dirty;
		return $this->current_table_number;
	}
}