package shadersmod.installer;

import java.io.BufferedReader;
import java.io.IOException;

import shadersmod.installer.Json.JsArray;
import shadersmod.installer.Json.JsBoolean;
import shadersmod.installer.Json.JsField;
import shadersmod.installer.Json.JsNumber;
import shadersmod.installer.Json.JsObject;
import shadersmod.installer.Json.JsString;
import shadersmod.installer.Json.JsValue;


public class JsonParser {
	
	protected BufferedReader rd;
	String data;

	public JsonParser(BufferedReader rd) {
		this.rd = rd;
	}

	int peekChar() throws IOException {
		while (data==null || data.length() == 0) {
			data = rd.readLine();
			if (data==null) break;
		}
		if ( data!= null)
			return data.charAt(0);
		else
			return 0;
	}
	void skip(int n) {
		if (data!=null)
			data = data.substring(n);
	}
	void skipWS() throws IOException {
		while (peekChar()<=' ' && data!=null)
			skip(1);
	}
	
	JsValue getValue() throws IOException {
		skipWS();
		int c = peekChar();
		if (c=='"') {
			return getString();
		} else if (c=='[') {
			return getArray();
		} else if (c=='{') {
			return getObject();
		} else if ((c>='0')&&(c<='9') || (c=='-')) {
			return getNumber();
		} else if (c=='t' || c=='f') {
			return getBoolean();
		}
		return null;
	}
	
	JsString getString() {
		int pos = 1;
		int max = data.length();
		char c;
		while (pos<max) {
			c = data.charAt(pos);
			if (c=='"') {
				break;
			} else if (c=='\\') {
				pos += 2;
			} else {
				++pos;
			}
		}
		if (pos<max) {
			JsString jss = new JsString();
			jss.str = data.substring(1,pos);
			skip(pos+1);
			return jss;
		}
		return null;
	}
	
	JsNumber getNumber() {
		int pos = 1;
		int max = data.length();
		while (pos<max) {
			char c = data.charAt(pos);
			if (c=='-' || c=='+' || c=='.' || (c>='0')&&(c<='9') || c=='e' || c=='E') {
				++pos;
			} else {
				break;
			}
		}
		if (pos<=max) {
			JsNumber jsn = new JsNumber();
			jsn.str = data.substring(0,pos);
			skip(pos);
			return jsn;
		}
		return null;
	}
	
	JsBoolean getBoolean() {
		if (data.startsWith("true")) {
			JsBoolean jsb = new JsBoolean();
			jsb.str = "true";
			skip(4);
			return jsb;
		} else if (data.startsWith("false")) {
			JsBoolean jsb = new JsBoolean();
			jsb.str = "false";
			skip(5);
			return jsb;
		}
		return null;
	}
	
	JsArray getArray() throws IOException {
		char c;
		JsArray jsa = new JsArray();
		do {
			skip(1); // first is '[', next is ','
			skipWS();
			c = data.charAt(0);
			if (c==']') // empty array 
				break;
			JsValue jsv = getValue();
			if (jsv!=null) {
				jsa.elements.add(jsv);
			} else {
				break;
			}
			skipWS();
			c = data.charAt(0);
		} while(c==',');
		skip(1); // ']'
		return jsa;
	}
	
	JsField getField() throws IOException {
		JsField jsf = new JsField();
		JsString name = getString();
		skipWS();
		skip(1); // ':'
		JsValue value = getValue();
		jsf.name = name.str;
		jsf.value = value;
		return jsf;
	}
	
	JsObject getObject() throws IOException {
		char c;
		JsObject jso = new JsObject();
		do {
			skipWS();
			skip(1); // first is '{', next is ','
			skipWS();
			c = data.charAt(0);
			if (c=='}') // empty object 
				break;
			JsField jsf = getField();
			if (jsf!=null) {
				jso.members.add(jsf);
				jso.membersMap.put(jsf.name, jsf);
			} else {
				break;
			}
			skipWS();
			c = data.charAt(0);
		} while(c==',');
		skip(1); // '}'
		return jso;
	}
	
}
