using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

public class Grammar {

	private Dictionary<string,string[]> table;
	private int privSeed;
	private int depth;
	public int maxDepth; //Max number of rules contained in each other. To avoid infinite loops
	System.Random random;

	public Grammar(){
		table = new Dictionary<string,string[]>();
		depth = 0;
		maxDepth = 50;
		random = new System.Random ();
		//Debug.Log("Hello");
	}

	// that class uses its own instance of random numbers, so it can be given a seed without altering other
	// random generators.
	public int seed{
		set {
			privSeed = value;
			random = new System.Random(privSeed);
		}
		get {
			return privSeed;
		}
	}

	public void NewRule(string ruleName,string[] strList) {
		if (table.ContainsKey (ruleName))
						Debug.Log ("The rule '" + ruleName + "' has already been defined !!");
		else table.Add(ruleName,strList);
	}

	public string Get(string ruleName){
		if (table.ContainsKey (ruleName)){
			string[] tempRule = table[ruleName];
			return tempRule [random.Next(0,tempRule.Length)];
		}
		else {
			Debug.Log("Rule "+ruleName+" has not been defined !");
			return null;
		}
	}

	public string MakeRule(string ruleName){
		string text;
		int ruleStart;
		int ruleEnd;

		text = this.Get (ruleName);
		//Debug.Log("New rule starts at "+text.IndexOf ('#'));
		// Lets check if is contains references to other rules :
		if (text !=null){
			ruleStart = text.IndexOf ('#');
			while (ruleStart>=0 && depth<maxDepth) {
				// Get end of the rule name
				ruleEnd = ruleStart+1;
				while ( ruleEnd<text.Length && char.IsLetterOrDigit (text[ruleEnd])){
					ruleEnd++;
				}
				string temp;
				if (ruleEnd > ruleStart+1){ // We found another rule !
					depth++;
					temp = text.Substring (ruleStart+1,ruleEnd-ruleStart-1);
					//Debug.Log ("depth : "+depth+" rulename : "+temp+" maxDepth :"+maxDepth);
					if (depth < maxDepth) temp = MakeRule (temp); // The rule is not too deep
					else temp = "TOODEEP_"+temp; // The ruke is too deep
					depth--;
				}
				else temp = "NONAME"; // No rule name was following #
				text = text.Remove (ruleStart,ruleEnd-ruleStart);
				text = text.Insert (ruleStart,temp);

				ruleStart = text.IndexOf ('#');
			}
			return text;
		}
		else return text = "UNDEFINED_"+ruleName;
	}

	// Check if the rule contained in a string have been defined
	public string CheckRuleReferences(string ruleName){
		//string text;
		int ruleStart;
		int ruleEnd;
		string check = "Checking "+ruleName+"\r\n";

		foreach (string text in table[ruleName]){
		
			ruleStart = text.IndexOf ('#');
			while (ruleStart>=0 && ruleStart<text.Length) {
				// Get end of the rule name
				ruleEnd = ruleStart+1;
				while ( ruleEnd<text.Length && char.IsLetterOrDigit (text[ruleEnd])){
					ruleEnd++;
				}
				string temp = text.Substring (ruleStart+1,ruleEnd-ruleStart-1);
				//Debug.Log ("In "+ruleName+" found rule : "+temp);
				if (!table.ContainsKey (temp)){
					string temp2 = "Error in '"+text+"' : rule "+temp+" has not been defined.\r\n";
					Debug.Log(temp2);
					check += temp2;
				}
				ruleStart = text.IndexOf ('#',ruleStart+1);
			}
		}
		return check;
	}

	// that check does not work !
	public void CheckGrammar(){
		StreamWriter writer = new StreamWriter(Application.dataPath + "/GrammarLog.txt");
		foreach (KeyValuePair<string,string[]> rule in this.table) {
			//Debug.Log("Error in rule "+rule.Key);
			//Debug.Log ("Testing "+rule.Key);
			//if (CheckRuleReferences(rule.Key)) check=false;
			writer.WriteLine(CheckRuleReferences(rule.Key));
		}
		writer.Close();
	}

	// Loads the grammar from a file
	public void Load(string fileName){
		string line;
		string ruleName;
		List<string> ruleItems = new List<string>();
		StreamReader reader = new StreamReader(Application.dataPath + "/" + fileName);
		line = reader.ReadLine();
		while (line != null){
			// Searching next rule

			while (line != null && (line.Length==0 || (line.Length>0 && char.IsLetterOrDigit(line[0])==false))){
				line = reader.ReadLine();
			}

			// Found a rule, now get the items
			if (line != null) {
				ruleName = line.Trim ();

				line = reader.ReadLine();
				while (line != null && line.Trim().Length>0 && line[0]=='\t'){
					ruleItems.Add(line.Substring (1,line.Length-1));
					//Debug.Log ("Adding : "+line.Substring (1,line.Length-1));
					line = reader.ReadLine();
				}
				if (ruleItems.Count > 0){
					this.NewRule (ruleName,ruleItems.ToArray ());
					ruleItems.Clear ();
				}
			}
		}
	}

	public void TestRule(int quantity,string ruleName){
		StreamWriter writer = new StreamWriter(Application.dataPath + "/Test_"+ruleName+".txt");
		for (int i =0;i<quantity;i++){
			writer.WriteLine (this.MakeRule (ruleName));
		}
		writer.Close ();
	}

	public void PrintGrammar(string fileName){
		StreamWriter writer = new StreamWriter (Application.dataPath +"/"+fileName);
		foreach (KeyValuePair<string,string[]> rule in this.table) {
			writer.WriteLine (rule.Key);
			for (int i = 0; i < rule.Value.Length;i++){
				writer.WriteLine ("\t"+rule.Value[i]);
			}
			writer.WriteLine ("");
		}
		writer.Close ();
	}
}

public class Text : MonoBehaviour {

	Grammar grammar;

	// Use this for initialization
	void Start () {
		grammar = new Grammar();
		grammar.Load ("grammar.txt");
		/*grammar.NewRule ("Forest",new string[] {"#Adjective #Verb. It is #End."});
		grammar.NewRule ("Adjective",new string[] {"Red", "Globulous", "Tiny", "Thousands of", "Two", "Some"});
		grammar.NewRule ("End",new string[] {"beautifull", "eerie", "scarry", "intriguing", "rather unexpected", "terrifying"});
		grammar.NewRule ("Verb",new string[] {"eyes are #EyesActions you","#Beings #BeingActions"});
		grammar.NewRule ("EyesActions",new string[] {"staring at", "watching", "blinking at"});
		grammar.NewRule ("Beings",new string[] {"moose", "animals", "things", "chimps"});
		grammar.NewRule ("BeingActions",new string[] {"are #EyesActions", "jumping around you", "running away", "escaping", "mating"});*/
		grammar.CheckGrammar ();
		grammar.TestRule (100, "Forest");
		grammar.PrintGrammar ("GrammarPrint.txt");
		guiText.text = "Press 'space' to see new text, or 's' to set seed";
	}
	
	// Update is called once per frame
	void Update () {
		if (Input.GetKeyDown (KeyCode.Space)){
			string ruleName = "Forest";
			guiText.text = ruleName+" : "+grammar.MakeRule(ruleName);
		}
		if (Input.GetKeyDown (KeyCode.S)){
			grammar.seed=999999999;
			guiText.text = "Injected seed : "+grammar.seed;
		}
	
	}
}
