//--------------------------------------
//               PowerUI
//
//        For documentation or 
//    if you have any issues, visit
//        powerUI.kulestar.com
//
//    Copyright  2013 Kulestar Ltd
//          www.kulestar.com
//--------------------------------------

using System;
using Dom;
using System.Text;


namespace Json{

	/// <summary>
	/// Handles parsing of JSON objects.
	/// </summary>

	public static class JSON{
		
		/// <summary>Writes a JSON object to a string.</summary>
		public static string Stringify(JSObject jsonObject){
			return jsonObject.ToJSONString();
		}
		
		/// <summary>Reads a JSON object from the given string.</summary>
		public static JSObject Parse(string json){
			
			// Create a parser (actually using the Nitro parser here):
			CodeLexer parser=new CodeLexer(json);
			
			char start=parser.Peek();
			
			if(start==StringReader.NULL){
				return null;
			}
			
			// Read it now:
			return ReadObject(parser);
			
		}
		
		/// <summary>Reads a JSON array from the given parser.</summary>
		private static JSObject ReadObject(CodeLexer parser){
			
			// Kick it off:
			char current=parser.Peek();
			
			// The read key:
			string key="";
			// And one for the value:
			StringBuilder value=new StringBuilder();
			bool valueString=false;
			bool inString=false;
			char stringChar='\0';
			// Is the current letter in "escaped" mode?
			bool escaped=false;
			
			JSObject result=null;
			
			while(current!=StringReader.NULL){
				
				if(inString){
					
					if(escaped){
						
						if(current=='\\' || current=='"'){
							value.Append(current);
						}else if(current=='n'){
							value.Append("\n");
							
						}else if(current=='r'){
							value.Append("\r");
							
						}else if(current=='t'){
							value.Append("\t");
							
						}else if(current=='u'){
							
							// 4 character code:
							
							// Get the hex code (+1 because we only peeked the u):
							string hexCode=parser.Input.Substring(parser.Position+1,4);
							
							// Parse hex:
							uint charCode=uint.Parse(hexCode,System.Globalization.NumberStyles.HexNumber);
							
							// Append that character:
							value.Append((char)charCode);
							
							// Read it off:
							parser.Position+=4;
							
						}else{
							value.Append(current);
						}
						
						escaped=false;
						
					}else if(current==stringChar){
						
						// Exiting the string:
						parser.Literal=false;
						inString=false;
					
					}else if(current=='\\'){
						
						// Escape:
						escaped=true;
					
					}else{
						
						value.Append(current);
						
					}
					
				}else if(current=='"' || current=='\''){
						
						// Track which it was:
						stringChar=current;
						
						// Entering the string:
						parser.Literal=true;
						inString=true;
						valueString=true;
					
				}else if(current=='{' || current=='['){
					
					if(result!=null){
						
						// Reading an object:
						JSObject arr=ReadObject(parser);
						
						if(key!=""){
							result[key]=arr;
						}else{
							result.push(arr);
						}
						
						// Use up the key:
						key="";
						
					}else if(current=='['){
						result=new JSIndexedArray();
					}else{
						result=new JSArray();
					}
					
				}else if(current=='}' || current==']' || current==','){
					
					// Complete the value if we need to.
					string val=value.ToString();
					value.Length=0;
					
					if(!valueString && val=="null"){
						val=null;
					}
					
					valueString=false;
					
					if(key!=""){
						
						result[key]=new JSValue(val);
						key="";
						
					}else if(val!=""){
						
						result.push(val);
						
					}
					
					if(current!=','){
						break;
					}
				
				}else if(current==':'){
					
					// We just read a key:
					key=value.ToString();
					value.Length=0;
					valueString=false;
					
				}else{
					
					// Add to value:
					value.Append(current);
					
				}
				
				// Read it off:
				parser.Read();
				
				// Peek next one:
				current=parser.Peek();
			}
			
			if(value.Length!=0){
				
				if(result==null){
					
					// Get the value string:
					string val=value.ToString();
					
					if(!valueString && val=="null"){
						// It was the word "null".
						return null;
					}
					
					result=new JSValue(val);
					
				}else{
					// Malformed - missing closing bracket.
					throw new Exception("JSON syntax error: Closing bracket missing.");
				}
			}
			
			return result;
			
		}
		
	}
	
}