<?php

class DBObject implements ISavableObject, JsonSerializable {
	protected static $PREPARED_STATEMENTS=array();
	private static $_classFields=array();
	protected $_fields=array();
	protected $_changedFields=array();
	private $_table, $_keys, $_ids;

	public static function VariableToDBField($variableName) {
		$parts=preg_split('/(?=[A-Z])/', $variableName);
		for ($i=0; $i<count($parts); $i++)
			$parts[$i]=strtolower($parts[$i]);
		return trim(implode("_", $parts), "_"); // If the variable name start with upper case then we get an extra blank entry in the array causing an extra _
	}

	public static function DBFieldToVariable($fieldName) {
		$parts=explode("_", $fieldName);
		for ($i=0; $i<count($parts); $i++)
			$parts[$i]=ucfirst($parts[$i]);
		return implode("", $parts);
	}

	private function ReIndexKeyAndIdArrays() {
		$newKeyArray=array();
		foreach ($this->_keys as $index=> $key) {
			$newKeyArray[':'.$index]=$key;
		}
		$this->_keys=$newKeyArray;

		$newIdArray=array();
		foreach ($this->_ids as $index=> $id) {
			$newIdArray[':'.$index]=$id;
		}
		$this->_ids=$newIdArray;
	}

	private function GetWhereClause() {
		$qualifiers=array();
		foreach ($this->_keys as $index=> $key)
			$qualifiers[]=" `$key`=$index";
		return implode(" AND", $qualifiers);
	}

	function __construct($table, $key, $id) {
		$this->_table=$table;
		$this->_keys=array();
		$this->_ids=array();

		if (is_array($key)&&is_array($id)) {
			$this->_keys=$key;
			$this->_ids=$id;
		} elseif ($id!=null && ($id!="" || (int)$id!=0)) {
			$this->_keys=array($key);
			$this->_ids=array($id);
		}

		$this->ReIndexKeyAndIdArrays();
		$this->Load();
	}

	public function __get($name) {
		if (array_key_exists($name, $this->_fields))
			return $this->_fields[$name];
		return null;
	}

	public function __set($name, $value) {
		if (array_key_exists($name, $this->_fields)&&$this->_fields[$name]!==$value) {
			$this->_changedFields[$name]=$name;
			$this->_fields[$name]=$value;
		}
	}

	public function __isset($name) {
		return array_key_exists($name, $this->_fields);
	}

	public function Load() {
		$PDO=BaseRepository::GetPDO();

		$class=get_class($this);

		if (!isset(self::$_classFields[$class])) {
			self::$_classFields[$class]=$PDO->query("DESCRIBE `{$this->_table}`")->fetchAll(PDO::FETCH_COLUMN);
		}
		foreach (self::$_classFields[$class] as $field)
			$this->_fields[self::DBFieldToVariable($field)]=null;
		if (count($this->_keys)>0){
			$statementKey=$class.'_construct_'.$this->_table.implode('', $this->_keys);
			if (!isset(self::$PREPARED_STATEMENTS[$statementKey])) {
				$fields=implode("`, `", self::$_classFields[$class]);
				$sql="SELECT `$fields` FROM `{$this->_table}` WHERE".$this->GetWhereClause();
				self::$PREPARED_STATEMENTS[$statementKey]=$PDO->prepare($sql);
			}
			$prep=self::$PREPARED_STATEMENTS[$statementKey];
			$prep->execute($this->_ids);

			$errorInfo=$prep->errorInfo();
			if ($errorInfo[0]!='00000'){
				Utils::var_dump_email($prep);
				throw new Exception($errorInfo[2]);
			}

			$record=$prep->fetch();
			if ($record!==false)
				foreach ($record as $key=> $value)
					$this->_fields[self::DBFieldToVariable($key)]=$value;
			else {
				$this->_keys=array();
				$this->_ids=array();
			}
		}
	}

	protected function HasChangedFields() {
		return count($this->_changedFields)>0;
	}

	public function Save() {
		$PDO=BaseRepository::GetPDO();

		if (!$this->HasChangedFields())
			return;

		$fields=array();
		$execData=array();
		foreach ($this->_changedFields as $field) {
			$fields[]='`'.self::VariableToDBField($field).'`=:'.$field;
			$execData[':'.$field]=$this->_fields[$field];
		}

		if (count($this->_ids)!==0) {
			$sql="UPDATE `{$this->_table}` SET ".implode(", ", $fields)." WHERE".$this->GetWhereClause();
			$execData=array_merge($execData, $this->_ids);
		} else {
			$sql="INSERT INTO `{$this->_table}` SET ".implode(", ", $fields);
		}
		$prep=$PDO->prepare($sql);
		$prep->execute($execData);

		$errorInfo=$prep->errorInfo();
		if ($errorInfo[0]!='00000')
			throw new Exception($errorInfo[2]);

		if (count($this->_ids)===0) { // If this is a new object we want to reload fromt he DB to make sure all fields are correct.
			// In order to do so we need to find the value for the key we're using
			$id=$PDO->lastInsertId();
			if ($id!=0 && count($this->_keys)==0) {
				$this->_keys=$this->_ids=array();
				$key=$PDO->query("SHOW INDEX FROM `{$this->_table}` WHERE Key_name='PRIMARY'")->fetch()['Column_name'];
				$this->_keys[]=$key;
				$this->_ids[]=$PDO->query("SELECT `$key` FROM `{$this->_table}` WHERE `$key`=$id")->fetchColumn();
				$this->ReIndexKeyAndIdArrays();
			}
			$this->Load();
		}
	}

	public function jsonSerialize() {
		$obj=new stdClass();
		foreach ($this->_fields as $key=> $value) {
			$obj->$key=$value;
		}
		return $obj;
	}

}
