/**
 * phpserialize.java
 *
 * Created on April 20, 2004, 4:41 PM
 * This is a quick hack i came up with today to unserialize a PHP-serialzied string
 * into something somewhat useable by java.
 *
 * @author Scott Hurring [scott at hurring dot com]
 * @version v0.01a ALPHA!!
 * @licence GNU GPL
 */

//package com.hurring.phpserialize;
import java.util.regex.*;
import java.util.*;

/**
* @author Scott Hurring
*/
public class PHPSerialize 
{
	/**
	* To store partially decomposed serialized string
	*/
	private StringBuffer sb;

	/** Creates a new instance of phpserialize */
	public PHPSerialize() {}

	/**
	 * Unserializes a single value into a PHPValue
	 *
	 *
	 *
	 * @param String s String to unserialize
	 * @return PHPValue
	 */ 
	public PHPValue unserialize(String s) {
		sb = new StringBuffer(s);
		PHPValue phpvalue = getNext();
		println("value = " + phpvalue.toString());
		return phpvalue;
	}

	/**
	* Get the next token in the serialized string
	*
	*
	*/
	private PHPValue getNext() 
	{
		//println("getNext = "+ sb.toString());
		PHPValue phpvalue;
		Object value = new Object();

		// Current character
		String c;
		// What type of value is this
		String what = "";
		// Consume & throw away characters
		int consumeChars = 0;
		// Length of the string/array
		int len = -1;
		// Buffer to read in string
		StringBuffer tmp = new StringBuffer();

		// length of sb is constantly changing.
		while (sb.length() > 0) {
			c = sb.substring(0, 1);
			sb = sb.deleteCharAt(0);

			//println(c +"\twhat="+what+"\ttmp="+tmp.toString());

			// Ignore chars
			if (consumeChars-- > 0) {
				continue;
			}

			/**
			* This is a hackish pseudo state machine
			* 'what' is the state
			*
			*/
			
			if (what.equals("array_length")) {
				if (c.equals(":")) {
					len = Integer.parseInt(tmp.toString());
					tmp.delete(0, tmp.length());
					//println("\tarray_length="+ len);
					what = "array_value";
				}
				if (len == -1) {
					tmp.append(c);
				}
			}
			else if (what.equals("array_value")) {
				//value = new PHPValue( new HashMap() );
				HashMap hash = new HashMap();

				for (int i = 0; i < len; i++) {
					PHPValue hashKey = getNext();
					PHPValue hashValue = getNext();

					// Force the keys of the HashMap to be type String
					hash.put(hashKey.value, hashValue);
				}

				value = hash;
				//return new PHPValue(value);
				what = "set";
			}
			else if (what.equals("string_length")) {
				if (c.equals(":")) {
					len = Integer.parseInt(tmp.toString());
					tmp.delete(0, tmp.length());
					//println("\tstring_length="+ len);
					what = "string_value";
					consumeChars = 1;
				}
				if (len == -1) {
					tmp.append(c);
				}
			}
			else if (what.equals("string_value")) {
				if (len-- > 0) {
					tmp.append(c);
				} else {
					value = tmp.toString();
					what = "set";
					//println("\tstring_value="+ value);
				}
			}

			else if (what.equals("integer")) {
				if (c.equals(";")) {
					value = tmp.toString();
					what = "set";
				} else {
					tmp.append(c);
				}
			}
			else if (what.equals("double")) {
				if (c.equals(";")) {
					value = tmp.toString();
					what = "set";
				} else {
					tmp.append(c);
				}
			}
			else if (what.equals("null")) {
				if (c.equals(";")) {
					value = null;
					what = "set";
				} else {
					// Bad
				}
			}

			// What kind of variable is coming up next
			else {

				if (c.equals("a")) {
					what = "array_length";
				} else if (c.equals("s")) {
					what = "string_length";
				} else if (c.equals("i")) {
					what = "integer";
				} else if (c.equals("d")) {
					what = "double";
				} else if (c.equals("n")) {
					what = "null";
					// do NOT consume the next char ";"
					continue;
				} else {
					continue;
				}

				// Consume the ":" after variable type declaration
				consumeChars = 1;
				continue;
			}

			if (what.equals("set")) {
				return new PHPValue(value);
			}

		}

		return new PHPValue(value);
	}


	private void println(String text) {
		System.out.println(text);
	}







	/********************************************************************
	*/

	public static void main(String args[]) {
		test();
	}

	public static void test() {
		PHPSerialize p = new PHPSerialize();
		p.unserialize("s:4:\"Test\";");
		p.unserialize("i:200;");

		PHPValue a = p.unserialize("i:0;");
		//int n = PHPObject.getInteger(i);
		double n = a.toDouble() + 5.7;
		System.out.println(n);
		System.out.println(a.toString());

		Object d = p.unserialize("d:10.5;");

		PHPValue c = p.unserialize("a:1:{s:3:\"key\";s:5:\"neinx\";}");
		System.out.println("HashMap = " + c.toString());
		System.out.println("Size = " + c.toHashMap().size());
		System.out.println("contains = " + c.toHashMap().containsKey("key"));
		System.out.println("Value = " + c.toHashMap().get("key"));

		// Test iteration of keys and values
		Set keys = c.toHashMap().keySet();
		System.out.println("\tKeys = " + keys.toString());
		Iterator iterator = keys.iterator();
		while ( iterator.hasNext() ) {
			Object x = iterator.next();
			System.out.println("\tkey:" + x.toString() );
			System.out.println("\tvalue:" + c.toHashMap().get("x") );
			System.out.println("\tclass:" + x.getClass() );
		}

		PHPValue b = p.unserialize("n;");
		if (b.isNull) {
			System.out.println("! Null");
		}

		PHPValue h = p.unserialize("a:3:{i:0;s:3:\"one\";i:1;a:1:{s:1:\"X\";s:6:\"inside\";}i:2;s:3:\"two\";}");

		System.out.println( ((PHPValue)h.toHashMap().get("1")).toHashMap().get("X") );

		p.unserialize("a:2:{i:0;s:9:\"onlyvalue\";i:1;s:8:\"twovalue\";}");
	}

}

