<?php

/************************* THIS MODULE IS DEPRECIATED *************************/
// Use of this module is depreciated. It will be removed (hopefully) in the next version, because it 
// sucks in more ways than one. If not in the next version (alpha 5), then it will be removed by at 
// most alpha 6. I hope to replace it with a very awesome databasing system I am currently calling 
// leandb -- although that name may change.

// This creates the Database Manipulator, which is an adapter class that 
// allows interchangeable database storage methods
$db = new DatabaseManipulatorXML();


$xml_database_path = 'config/xml/';

$xml_database_extension = '.xml';

$xml_new_database_skeleton = "
<xml>
	
</xml>
";

class DatabaseManipulatorXML
{
	var $current_table_path;
	var $current_wrapper;
	var $table_has_been_selected;
	
	function DatabaseManipulator()
	{
		$this->table_has_been_selected = FALSE;
	}
	
	function isNameValid ($name)
	{
		if (preg_match(REGEX_LEGAL_CHAR1, $name) == 0 )
			return TRUE;
		else
			return FALSE;
	}
	
	// Selects the table to manipulate. Returns true if the table exists.
	function selectTable ( $name )
	{
		global $xml_database_path, $xml_database_extension;
		$this->current_table_path = $xml_database_path . $name . $xml_database_extension;
		if ($this->isNameValid($name) !== FALSE && file_exists( $this->current_table_path ) !== FALSE )
		{
			$this->current_wrapper = new XMLDatabaseWrapper();
			$handle = @fopen($this->current_table_path, 'r');
			$contents = fread($handle, filesize($this->current_table_path));
			fclose($handle);
			$this->current_wrapper->loadFromString($contents);
			
			$this->table_has_been_selected = TRUE;
			
			return TRUE;
		}
		else
			return FALSE;
	}
	function add(Entry $entry)
	{
		$this->addEntry($entry);
	}
	function addEntry ( $entry )
	{
		if ( $this->table_has_been_selected === TRUE )
		{
			$this->current_wrapper->addEntry($entry);
			return TRUE;
		}
		else return FALSE;
	}
	function makeTable ( $name )
	{
		if ( $this->isNameValid ($name) !== FALSE )
		{
			global $xml_new_database_skeleton, $xml_database_path, $xml_database_extension;
			$this->current_table_path = $xml_database_path . $name . $xml_database_extension;
			
			$handle = @fopen($this->current_table_path, 'w');	
			@fwrite($handle, $xml_new_database_skeleton );
			@fclose($handle);
			$this->selectTable($name);
			return TRUE;
		}
		else return FALSE;
	}
	
	function search ( $query )
	{
		if ($query === TRUE) $this->getAll();
		else return $this->current_wrapper->findEntries($query->attributes);
	}
	function applyChanges ()
	{
		if ($this->table_has_been_selected === TRUE )
		{
			$handle = @fopen($this->current_table_path, 'w');
			fwrite($handle, $this->current_wrapper->raw_text );
			fclose ($handle);
			return TRUE;
		}
		else return FALSE;
	}
	function getAll ()
	{
		if ($this->table_has_been_selected === TRUE )
		{
			return $this->current_wrapper->getAll();
		}
		else return FALSE;
	}
}



class XMLDatabaseWrapper
{
	var $location_of_main = 'test.xml';
	var $database_location;
	var $parser;
	var $raw_text;
	
	
	function XMLDatabaseWrapper ( )
	{
		$this->parser = new MainXmlToArray();
	}
	function loadFromString ($string)
	{
		global $xml_php_protection;
		$this->raw_text = $string;/*
		if ( strpos($this->raw_text, $xml_php_protection) !== FALSE ) // checks to see if there is a php comment protecter
			$this->raw_text = str_replace($xml_php_protection, '', $this->raw_text ); // removes it*/
		
		$this->parser->parse($this->raw_text); 
		
	}

	function getAll()
	{
		// First we grab all of the users and put them as parsed XML arrays into an array
		$user_array = $this->parser->arrOutput[0]['childrens'];
		
		// if there weren't any found, no reason to continue.
		if (sizeof($user_array) < 1) return FALSE; 
		$accounts = Array();
		
		foreach($user_array as $user)
		{
			$current_account = new Entry();
			foreach ($user['childrens'] as $property)
			{
				$value = main_replace($property['nodevalue'],  Array ('!`lt^'=> '<', '!`gt^' => '>') );
				//$value = main_replace($property['nodevalue'],  Array ('!`lt^'=> '<', '!`gt^' => '>' , '!*`/' => '* /') );
				$current_account->setAttribute ($property['nodename'], $value);
			}
			array_push($accounts, $current_account);
		}
		return $accounts;
	}
/*

This function returns an array of 'account' objects, showing all the user(s) that match a 
given query. If there are no users that match, then it returns FALSE.


*/
	function findEntries ( $query )
	{
		// First we grab all of the users and put them as parsed XML arrays into an array
		$user_array = $this->parser->arrOutput[0]['childrens'];
		$found_users = Array();
		foreach ($user_array as $user)
		{
			if ( $this->matchAnEntry($user, $query) === TRUE )
			{
				array_push($found_users ,$user);
			}
		}
		
		// if there weren't any found, no reason to continue.
		if (sizeof($found_users) < 1) return FALSE; 
		$accounts = Array();
		
		foreach($found_users as $user)
		{
			$current_account = new Entry();
			foreach ($user['childrens'] as $property)
			{
				$value = main_replace($property['nodevalue'],  Array ('!`lt^'=> '<', '!`gt^' => '>') );
				//$value = main_replace($property['nodevalue'],  Array ('!`lt^'=> '<', '!`gt^' => '>', '!*`/' => '*/') );
				$current_account->setAttribute ($property['nodename'], $value);
			}
			array_push($accounts, $current_account);
		}
		return $accounts;
	}
	
	function matchAnEntry( $user, $query )
	{
		
		// ASSERTATION: that the search queries used are found in all users.
		/*
e.g.
Name	Pass	Email
Joe	foo	f@b.c
Loe	bar	-----
Qux	wot	k@j.c

matchAUser( Joe, 'Pass'=>'foo' & 'name' => Joe) is TRUE
matchAUser( Loe, 'Pass'=>'bar' & 'name' => 'Loe' & 'email'=>'dep@k.com') is also TRUE

In other words, matchAUser returns TRUE if at least one property is a match and no other properties are 
unmatches (i.e. both defined and not matching). It returns FALSE if either no property matches, or any 
property is defined as unmatching.

*/
		// TODO: make the above assertation unnecessary
		
/*
We first split the $query variable into two parallel arrays.
Then, using the foreach statement, we loop through each child in the user array.




*/
		$q_attributes = array_keys($query);
		$q_values = array_values($query);
		$is_match = FALSE;
		foreach ($user['childrens'] as $attribute )
		{
			for ( $i = 0; $i < sizeof($q_attributes); $i++ )
			{
				if ( $attribute['nodename'] === $q_attributes[$i]) // we have a attribute match.
				{
					if ($attribute['nodevalue'] === $q_values[$i] ) // we have a value match!
					{
						$is_match = TRUE; 
					}
					else // Uh oh. It can't be a match--the correct attribute has a different value.
					{
						return FALSE;
					}
				}
			}
		}
		return $is_match;
	}
	function removeBefore ( $text )
	{
		/*global $xml_php_protection;
		$text = stristr($text, $xml_php_protection);	*/
		return $text;
	}
	
	function addEntry ( $entry )
	{
		
		// TODO: the next line is a rather crummy job. It removes ALL </xml> strings, which isn't  necessary.
		$this->raw_text = ereg_replace('</xml>', ' ', $this->raw_text); // removes the closing xml at the bottom
		
		
		
		$this->raw_text = $this->raw_text . $entry->toXML();
		$this->raw_text = $this->raw_text . "</xml> \n\n"; // caps it off again with the closing xml tag
		// and pads it with some newline characters so that it will look nice.
		
		//echo $this->raw_text;
		
		$this->parser = new MainXmlToArray ();
		$this->parser->parse ( $this->raw_text );
		//print_r($this->parser->arrOutput);
	}
	
}



/*

This class is based largely on the code by james@mercstudio.com , and bbellwfu@gmail.com , posted on php.net.

*/
class MainXmlToArray
{
   var $arrOutput = array();
   var $resParser;
   var $strXmlData;
  
  
   function xmlToArray($tfile = "")
   {
       if(trim($tfile) != "") { $this->loadFile($tfile);}
   }
  
   function loadFile($tfile)
   {
       $this->thefile = $tfile;
      
       $th = file($tfile);
       $tdata = implode("\n", $th);
      
       return $this->parse($tdata);
   }
	
	
   function parse($strInputXML)
   {
       $this->resParser = xml_parser_create ();
       xml_set_object($this->resParser,$this);
       xml_set_element_handler($this->resParser, "tagOpen", "tagClosed");
      
       xml_set_character_data_handler($this->resParser, "tagData");
      
       $this->strXmlData = xml_parse($this->resParser,$strInputXML );
      
	
       if(!$this->strXmlData)
	  {
			// An error has occured, die with a error message.
			// TODO: Make this much better, and don't make it perish
           die(sprintf("XML error: %s at line %d \n", xml_error_string(xml_get_error_code($this->resParser)), 
				xml_get_current_line_number($this->resParser)));
       }
      
       xml_parser_free($this->resParser);
      
       return $this->arrOutput;
   }
  
   //called on each xml tree
   function tagOpen($parser, $name, $attrs) {
       $tag=array("nodename"=>strtolower($name),"attributes"=>$attrs);
       array_push($this->arrOutput,$tag);
   }
 
  //called on data for xml
   function tagData($parser, $tagData) {
       if(trim($tagData)) {
           if(isset($this->arrOutput[count($this->arrOutput)-1]['nodevalue'])) {
               $this->arrOutput[count($this->arrOutput)-1]['nodevalue'] .= $this->parseXMLValue($tagData);
           }
           else {
               $this->arrOutput[count($this->arrOutput)-1]['nodevalue'] = $this->parseXMLValue($tagData);
           }
       }
   }
 
  //called when finished parsing
   function tagClosed($parser, $name) {
       $this->arrOutput[count($this->arrOutput)-2]['childrens'][] = $this->arrOutput[count($this->arrOutput)-1];
      
       if(count ($this->arrOutput[count($this->arrOutput)-2]['childrens'] ) == 1)
       {
           $this->arrOutput[count($this->arrOutput)-2]['firstchild'] =& $this->arrOutput[count($this->arrOutput)-2]['childrens'][0];
       }
       array_pop($this->arrOutput);
   }
   
   function parseXMLValue($tvalue)
   {
       // $tvalue = htmlentities($tvalue);
       return $tvalue;
   }
  
   function toXML($tob = null)
   {
       //return back xml
       $result = "";
      
       if( $tob == null)
       {
           $tob = $this->arrOutput;
       }
      
       if(!isset($tob))
       {
           echo "XML Array empty...";
           return null;
       }
      
      
       for($c = 0; $c < count($tob); $c++)
       {
           $result .="<" . $tob[$c]["nodename"];
          
           while (list($key, $value) = each($tob[$c]["attributes"]))
           {
               $result .=" " . $key."=\"" . $this->parseXMLValue($value) . "\"";
           }
          
           $result .= ">";
          
           //assign node value
           if( isset($tob[$c]["nodevalue"]) )
           {
               $result .= $tob[$c]["nodevalue"];
           }
          
           if( count($tob[$c]["childrens"]) > 0 )
           {
               $result .= "\r\n" . $this->toXML($tob[$c]["childrens"]) . ""; # dropped the call-by-reference here, before it was &$tob[$c]...
           }

           $result .= "</" . $tob[$c]["nodename"] . ">\r\n";
          
          
       }//end of each array...
      
       return $result;
   }
  
   function displayXML()
   {
       print_r($this->arrOutput);
   }
  
   function getXML($tob = null)
   {
       return "<?xml version='1.0'?>\r\n" . $this->toXML($tob);
   }

}


?>
