<?
class XMLDB{
	
	// Path and name to the file
	protected $_file;
	
	// Primary key
	protected $_primaryKey;
	
	// Selected table
	protected $_table;
	
	// XPATH
	protected $_xpath;
	
	// Content of the XML DB File
	protected $_doc;
	
	/**
	 * Constructor
	 * @param $file string path to the file to read/create
	 * @param $pk string name of the primary key
	 * @param $createIfNotExist bool create the file if it doesn't exist
	 */
	public function __construct($file, $pk = "id", $createIfNotExist = false){
		ini_set("display_errors", "off"); 
		ini_set("log_errors", "on");
		ini_set('error_log', $_SERVER['DOCUMENT_ROOT'].'/test/XMLDB/XMLDB.log');
		$this->_primaryKey = $pk;
		$this->_file = $file;
		$this->_doc = new DOMDocument;
		$this->_doc->preserveWhiteSpace = false;
		$this->_doc->formatOutput = true; 
		if($this->_doc->load($this->_file)){
			$this->_xpath = new DOMXpath($this->_doc);
		}
		else{
			if($createIfNotExist){
				$this->createDatabase($file);
			}else{
				$this->_file = null;
				$this->_doc = null;
				$this->xpath = null;
			}
		}
	}
	
	public function createDatabase($file){
		$this->_file = $file;
		$this->_doc = DOMDocument::loadXML('<?xml version="1.0" encoding="utf-8"?>
			<Database>
			</Database>');
		$this->_xpath = new DOMXpath($this->_doc);
		return $this->save();
	}
	
	public function createTable($name){
		if($name == '*' || $this->tableAlreadyExists($name))
			return false;
		else 
			return $this->insertNode(array('name'=>'table', 'attributes'=>array('name'=>$name)));
	}
	
	/**
	 * @return bool
	 */
	public function tableAlreadyExists($tableName){
		if($this->selectTable($tableName, 'count') >= 1)
			return true;
		return false;
	}
	
	
	public function selectTable($name, $format = 'node'){
		return $this->select($name, null, null, null, $format);
	}
	
	public function isLoaded(){
		if($this->_doc != null)
			return true;
		else
			return false;
	}
	
	public function count($from, $id = null, $attributes = null, $childs = null){
		return $this->select($from, $id, $attributes, $childs, 'count');
	}
	
	public function setPrimaryKey($pk){
		$this->_primaryKey = $pk;
	}
	
	public function getPrimaryKey(){
		return $this->_primaryKey;
	}
	
	public function getXPath(){
		return $this->_xpath;
	}
	
	/**
	 * Saving the DB file
	 */
	 public function save(){
		if($this->_doc != null && $this->_file != null){
			$this->_doc->preserveWhiteSpace = false;
			$this->_doc->formatOutput = true; 
			$this->_doc->save($this->_file);
			return true;
		}else{
			return false;
		}
	 }
	
	// TODO checking $request
	public function requestToArray($request){
		foreach($request as $element){
			/*if($childName != null && $childValue != null)
			$element = $element->parentNode;*/
			$elementValue = $element->attributes->item(0)->value;
			$return[$elementValue]['attributes'] = array();
			$return[$elementValue]['childs'] = array();
		
			//Retrieving Attributes
			$attributes = $element->attributes;
			$length = $attributes->length;
			for ($i = 1; $i <= $length; $i++) {
				$return[$elementValue]['attributes'][$attributes->item($i)->name] = $attributes->item($i)->value;
			}
		
			// Retrivieving childs
			$nodes = $element->childNodes;
			$length = $nodes->length;
			for ($i = 1; $i <= $length; $i++) {
				$return[$elementValue]['childs'][$nodes->item($i)->nodeName] = $nodes->item($i)->nodeValue;
			}
		}
		return $return;
	}
	
	/**
	 * Shortcuts for select
	 */
	public function selectFromAttribute($table, $attributes, $format = 'array'){
		return $this->select($table, null, $attributes, null, $format);
	}
	
	public function selectFromChilds($table, $childs, $format = 'array'){
		return $this->select($table, null, null, $childs, $format);
	}
	
	public function selectFromPK($table, $pk, $format = "array"){
		return $this->select($table, $pk, null, null, $format);
	}
	
	/**
	 * Allows you to get an array of the node you're looking for based on a "where" search thanks to child or attribute value
	 * @param $from string Name of the table
	 * @param $id string value of the primary key
	 * @param $childs array name/value of the child node
	 * @param $attributes array name/value of the attribute
	 * @return array
	 */
	public function select($from, $id = null, $attributes = null, $childs = null, $format = 'array'){
		if (!$from) {
			throw new Exception('uhoh, no table selected');
		}
		if($id != null && count($id) == 1){
			$attribute .= '[@' . $this->_primaryKey . ' = "' . $id . '"]';
		}
		if($attributes != null){
			foreach($attributes as $attributeName=>$attributeValue)
				$attribute .= '[@' . $attributeName . ' = "' . $attributeValue . '"]';
		}
		if($childs != null){
			foreach($childs as $childName=>$childValue)
				$child .= '[' . $childName . '="' . $childValue . '"]';
				/*$child .= '/' . $childName . '[.="' . $childValue . '"]';
			if(count($childs)>1){
				$child = str_replace('/', '', $child);
				$child = '/'.$child;
			}*/
		}
		if($from == '*')
			$request = $this->_xpath->query('//item'.$attribute.$child);
		else
			$request = $this->_xpath->query('//table[@name = "'.$from.'"]/item'.$attribute.$child);
		
		switch($format){
			case "node": 
				$return = $request;
				break;
			case "count":
				$return = $request->length;
				break;
			case "array":
			default:
				$return = $this->requestToArray($request);
		}
		return $return;
	}
	
	/**
	 * Same as above, but user can build his own xpath
	 */
	public function select_xpath($from, $xpath, $format = 'array'){
		if (!$from || !$xpath) {
			throw new Exception('uhoh, no table selected');
		}
		$request = $this->_xpath->query('//table[@name = "'.$from.'"]/'.$xpath);
		switch($format){
			case "node": 
				$return = $request;
				break;
			case "count":
				$return = $request->length;
				break;
			case "array":
			default:
				$return = $this->requestToArray($request);
		}

		return $return;
	}
	
	/**
	 * Allows you to insert a node into your DB thanks to an array
	 * @param $node array with 'name' 'attributes' and 'childs'
	 * @param $table string in which node you want to put it. By default, the root of the xml file
	 * @param $position string 'before' or 'after'
	 * @return bool
	 */
	
	// TODO other $where and $position
	public function insertNode($node, $table = null, $position = null){
		if(!is_array($node) || !isset($node['name']))
			return false;
			
		// Creating the node from an array
		$element = $this->_doc->createElement($node['name']);
		if(isset($node['attributes'])){
			foreach($node['attributes'] as $attributeName=>$attributeValue){
				$element->setAttribute($attributeName, $attributeValue);
			}		
		}
		if(isset($node['childs'])){
			foreach($node['childs'] as $childName=>$childValue){
				$newElement = $this->_doc->createElement($childName, $childValue);
				$element->appendChild($newElement);
			}	
		}
		
		// Inserting the node into the DB
		// case : creation of a new table
		if($table == null){
			$this->_doc->firstChild->appendChild($element);
		}else{
		// case : insertion into the end of table
		//TODO checking if PK already exists
			$request = $this->_xpath->query('//table[@name = "'.$table.'"]');
			if($request->length != 1){	
				return false;
			}
			$request->item(0)->appendChild($element);
		}
		return $this->save();
	}
	
	/**
	 * 
	 * @param $table string 
	 * @param $oldAttribute string name of the attribute you want to change
	 * @param $newAttribute array name/value of the attribute you want to add
	 * @param $forceInsert bool 
	 * @return bool
	 */
	public function updateNodeAttribute($table, $oldAttribute, $newAttribute, $forceInsert = false){
		$request = $this->select($table, null, array($oldAttribute[0]=>$oldAttribute[1]), null, 'node');
		if($request->length == 1){	
			if(!$forceInsert){
				$request->item(0)->setAttribute($oldAttribute[0],$newAttribute[1]);
			}else{
				$request->item(0)->setAttribute($newAttribute[0],$newAttribute[1]);
			}
			return $this->save();
		}
		else
			return false;
	}
	
	/**
	 * 
	 * @param $table string 
	 * @param $value string new value of the node
	 * @return bool
	 */
	public function updateNodeValue($table, $attribute = null, $child = null, $value){
		$request = $this->select($table, null, array($attribute[0]=>$attribute[1]), null, 'node');
		//$request = $this->_xpath->query('//'.$node.'[@' . $attribute[0] . ' = "' . $attribute[1] . '"]');
		if($request->length == 1){			
			$request = $request->item(0);
			$newText = new DOMText($value);
			$request->removeChild($request->firstChild);
			$request->appendChild($newText);
			return $this->save();
		}
		else
			return false;
	}
	
	
	public function deleteNode($table, $id = null, $attributes = null){
		if($id != null && $attributes != null)
			return false;
		if($id != null)
			$request = $this->select($table, $id, null, null, 'node')->item(0);
		if($attributes != null)
			$request = $this->select($table, null, array($attribute[0]=>$attribute[1]), null, 'node')->item(0);
		$request->parentNode->removeChild($request);
		return $this->save();
	}
	
	public function moveNode($node, $from, $to){
		
	}
	
	
	
	
}