<?php

define("TO_ENTITIES", 0);
define("TO_CHARACTERS", 0);

class Wddx {

	// public
	var $xml;
	var $data;
	
	// private
	var $et = array();
	var $etRev = array();
	var $at = array();
	var $atRev = array();
    var $datas = array();
    var $_isData;
    var $_lastType;
    var $_lastVarName;
    
	function Wddx() {
	 
		for ( $i = 0; $i<256; ++$i ) {
			if ( $i<32 && $i != 9 && $i != 10 && $i != 13 ) {
				$hex = dechex($i);
				if (strlen($hex) == 1) {
					$hex = "0"+$hex;
				}
				$this->et[$i] = "<char code='".$hex."'/>";
				$this->at[$i] = "";
			} 
			else if ($i<128) {
				$this->et[$i] = chr($i);
				$this->at[$i] = chr($i);
			} 
			else {
				$this->et[$i] = "&#x".dechex($i).";";
				$this->etRev["&#x".dechex($i).";"] = chr($i);
				$this->at[$i] = "&#x".dechex($i).";";
				$this->atRev["&#x".dechex($i).";"] = chr($i);
			}
		}
		$this->et[ord("<")] = "&lt;";
		$this->et[ord(">")] = "&gt;";
		$this->et[ord("&")] = "&amp;";
		$this->etRev["&lt;"] = "<";
		$this->etRev["&gt;"] = ">";
		$this->etRev["&amp;"] = "&";
		$this->at[ord("<")] = "&lt;";
		$this->at[ord(">")] = "&gt;";
		$this->at[ord("&")] = "&amp;";
		$this->at[ord("'")] = "&apos;";
		$this->at[ord("\"")] = "&quot;";
		$this->atRev["&lt;"] = "<";
		$this->atRev["&gt;"] = ">";
		$this->atRev["&amp;"] = "&";
		$this->atRev["&apos;"] = "'";
		$this->atRev["&quot;"] = "\"";

	}
	
	// Serialize a Flash object
	function serialize($rootObj) {
		// print_R($rootObj);
		$this->xml =	"<wddxPacket version=\"1.0\"><header/><data>";
		$this->xml .= $this->serializeValue($rootObj);
		$this->xml .= "</data></wddxPacket>";	
		return $this->xml;
	}
	
	// Determine the type of a Flash object and serialize it
	function serializeValue($obj) {
	 	/*echo "\n>>FF>>";
		print_r($obj);
		echo "<<FF<<\n";*/
	//	print(gettype($obj)."\n");
	
		//  null object
		if (is_null($obj)) {
			$xmlString="<null/>";
		} 
		// string object
		else if (is_string($obj)) {
			$xmlString = $this->serializeString($obj);
		} 
		// numeric objects
		else if (is_float($obj) || is_int($obj)) {
			$xmlString="<number>".$obj."</number>";
		} 
		// boolean object
		else if (is_bool($obj)) {
			$xmlString="<boolean value=\"".($obj==true? "true":"false")."\" />";
		} 
		// array
		else if (is_array ($obj) && ((isset($obj[0]) && count($obj)>0) || (!isset($obj[0]) && count($obj)==0)) ) {
			$xmlString="<array length=\"".count($obj)."\">";
			for ( $i = 0; $i<count($obj); $i++ ) {
				$xmlString.= $this->serializeValue($obj[$i]);
			}
			$xmlString.="</array>";
		} 
		else if (is_object ($obj) || is_array ($obj)) {
		
			$xmlString="<struct>";
		//	print_r($obj);
			foreach ($obj as $prop => $value) {
				if (is_object ($obj)) $xmlString.= $this->serializeVariable($prop, $obj->$prop);
				else $xmlString.= $this->serializeVariable($prop, $obj[$prop]);
			}
			$xmlString.="</struct>";
		}
		//  Successful serialization
		return $xmlString;
	}
	
	// Serialize a  varible
	function serializeVariable($name, $obj) {
		$xmlString="<var name=\"".$this->serializeAttr($name)."\">";
		$xmlString.= $this->serializeValue($obj);
		$xmlString.="</var>";
		return $xmlString;
	}
	
	// Serialize a Flash String
	function serializeString($s) {
	 	//echo ("\n\n\n".$s."\n");	 
		//	echo Utf8::to_unicode($s);
		/*$s = str_replace("&", "&amp;",$s);
		  $s = str_replace("<", "&lt;",$s);
		  $s = str_replace(">", "&gt;",$s);*/
		  
		return "<string>".$s."</string>";
	 //	return "<string>".Utf8::to_unicode_and_escape($s)."</string>";
	 	
		/*$s = Utf8::to_unicode($s);

		$max = strlen($s);
		
		$xmlString="<string>";
		for ($i = 0; $i<$max; $i++) {
			$char = substr($s, $i, 1);
			if($char=="&"){
				if(substr($s, $i, 2)=="&#"){
					$tempString .= "&";
				}else{
					$tempString .= ($this->et[ord(substr($s, $i, 1))]);
				}
			}else{
			  $tempString .= ($this->et[ord(substr($s, $i, 1))]);
			}
		}
		$xmlString.=$tempString."</string>";
		return $xmlString;*/
	}
	
	function serializeAttr($s) {
		$tempString = "";
		$max = strlen($s);
		for ($i = 0; $i<$max; $i++) {
			$tempString .= ($this->at[ord(substr($s, $i, 1))]);
		}
		return $tempString;
	}

	// wddx deserializer
	function deserialize($xml) {
	// print_r($xml);
		$xml = utf8_encode ($xml);
		$this->data = array();
		$this->_isData=false;
		$this->datas = array();
   		$this->_lastType="";
     	$this->_lastVarName="";
      
		$xml_parser = xml_parser_create();
		xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false);
		xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 1);
		xml_parser_set_option($xml_parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
		xml_set_object ( $xml_parser, $this );
		xml_set_element_handler($xml_parser, "startElement", "endElement");
		xml_set_character_data_handler($xml_parser, 'cdata');
		
		if (!xml_parse($xml_parser, $xml)) {
		    die(sprintf("XML error: %s at line %d",
		                xml_error_string(xml_get_error_code($xml_parser)),
		                xml_get_current_line_number($xml_parser)));
		}
		
		xml_parser_free($xml_parser);
		
		/*print_r("---------------");
		print_r($this->data);
		print_r("---------------");*/
		
		return $this->data;
		
	}
	
	function startElement($parser, $tag, $attrs){	
	 	if($this->_isData==true){
		 	
		 	$this->_lastType = $tag;
		 	
			 //	print_R($tag);
				 	
			// object or array
		 	if($tag=="struct" || $tag=="array"){
		 	    
		 		$tag=="struct" ? $d = (object) array() : $d = array();
		 		
			 	if(count($this->datas["struct"]["ref"])>0){
			 		$d_parent = &$this->datas["struct"]["ref"][count($this->datas["struct"]["ref"])-1]; 
			 		if(is_object($d_parent)){
			 	 		$d_parent_name = $this->_lastVarName;
			 			$d_parent->$d_parent_name = &$d;
			 		}
			 		if(is_array($d_parent)){
			 		   $d_parent[] = &$d;
			 		}
		 		}
		 		
		 		$this->datas["struct"]["ref"][] = &$d;
		 	//	$this->datas["struct"]["name"][] = $this->_lastVarName;
		 		$this->data = &$d;

		 	}
		 	// property of object
		 	else if($tag=="var"){
		 		$this->_lastVarName = $attrs['name'];
		 	}
		 	// null value
		 	else if($tag=="null"){
		 		 $this->cdata($parser, "null");
		 	//	$this->data = null;
		 	}
		 	else if($tag=="boolean"){
		 		 $this->cdata($parser, $attrs['value']);
		 	//	$this->data = ($attrs['value']=="true");
		 	}
		 	// type

		 	 
	 	}
	 	if( $tag=="data")$this->_isData = true;
	 	

	}
	
	function cdata($parser, $_cdata){ 
	 
    	global $cdata;
    	$cdata.=$_cdata;
    	
     	//print_R($cdata);
		if($this->_lastType=="null")$val = null;
		else if($this->_lastType=="boolean") $val = ($cdata=="true");
		else if($this->_lastType=="number") $val = (int) $cdata;
		else if($this->_lastType=="string"){
		// echo ( "|||||||".$cdata);
		  $val = (string) $cdata;
		  /**/
		  
		  //$val =  Utf8::from_unicode($cdata);
		  }
		else $val = $cdata;
		
	 	if(is_object($this->data)){
	 	  	$name = $this->_lastVarName;
			$this->data->$name = $val;
	 	}
		else if(is_array($this->data)){
			$this->data[] = $val;
		}
		else{
			$this->data = $val;
		}

    }
    
	function endElement($parser, $tag){
		 
	 	if( $tag=="data")$this->_isData = false;
 		else if($tag=="struct" || $tag=="array"){
	 	
		 	$min = 2;
	 		if(count($this->datas["struct"]["ref"])<2){
	 		 	$min = 1;
	 		}
	    	$this->data = &$this->datas["struct"]["ref"][count($this->datas["struct"]["ref"])-$min];
       	 	array_pop($this->datas["struct"]["ref"]);
       	 
       	}
       	unset($GLOBALS['cdata']);
	}
	
}