using UnityEngine;
using System.Collections.Generic;

using LanguageData = System.Collections.Generic.Dictionary<string, float[]>;

public class Language {
	private static int depth = 4;

	public static string normalize(string input) {
		string normalizedString = input.Normalize(System.Text.NormalizationForm.FormD);
		System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();

		foreach (var c in normalizedString) {
			if (System.Globalization.CharUnicodeInfo.GetUnicodeCategory(c) != System.Globalization.UnicodeCategory.NonSpacingMark) {
				stringBuilder.Append(c);
			}
		}

		return stringBuilder.ToString().Normalize(System.Text.NormalizationForm.FormC).ToLower();
	}

	public static Language fromRaw(string file) {
		using (var fileStream = System.IO.File.OpenRead(file))
			using (var streamReader = new  System.IO.StreamReader(fileStream)) {
				int count = 0;
				
				LanguageData data = new LanguageData();
				string line;
				while ((line = streamReader.ReadLine()) != null && count < 1000000) {
					processLine(line, data);
					count ++;
				}

				return new Language(normalizeLanguageData(data));
			}
	}

	private static void processLine(string word, LanguageData data) {
		string normalizedWord = normalize(word);

		System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex("^[a-z']*$");
		if (!r.IsMatch(normalizedWord)) 
			return;

		if (word.Length == 0)
			return;

		for (int i = 0; i < normalizedWord.Length; i++) {
			if (i+1 <= depth) {
				increaseCount(normalizedWord.Substring(0, i), normalizedWord[i], data);
			}
			else {
				increaseCount(normalizedWord.Substring(i-depth, depth), normalizedWord[i], data);
			}
		}
	}

	private static void increaseCount(string root, char c, LanguageData data) {
		if (!data.ContainsKey(root)) {
			data[root] = new float[27];
		}
		data[root][charToIndex(c)] ++;
	}

	/*public static Language fromMatrix(string file) {
		using (var fileStream = System.IO.File.OpenRead(file))
			using (var streamReader = new  System.IO.StreamReader(fileStream)) {
				float[,] matrix = new float[28, 28];
				string[] values = streamReader.ReadLine().Split(',');

				Debug.Assert(values.Length == 28 * 28);

				for (int i = 0; i < values.Length; i++) {
					matrix[i/28, i%28] = float.Parse(values[i]);
				}

				return new Language(matrix);
			}
	}*/



	private static int charToIndex(char key) {
		if (key == '\'') {
			return 0;
		}
		else {
			return 1 + key - 'a';
		}
	}

	private static char indexToChar(int index) {
		if (index == 0) {
			return '\'';
		}
		else {
			return (char)('a' + (index - 1));
		}
	}

	/*private static float[,] normalizeMatrix(float[,] matrix) {
		for (int i = 0; i < 28; i++) {
			float sum = 0;
			for (int j = 0; j < 28; j++) {
				sum += matrix[i, j];
			}
			if (sum > 0) {
				for (int j = 0; j < 28; j++) {
					matrix[i, j] /= sum;
				}

				float cumu = 0;
				for (int j = 0; j < 28; j++) {
					cumu += matrix[i, j];
					matrix[i, j] = cumu;
				}
			}
		}

		return matrix;
	}*/

	private static LanguageData normalizeLanguageData(LanguageData data) {
		foreach(KeyValuePair<string, float[]> entry in data) {
			float sum = 0;
			for (int i = 0; i < 27; i++) {
				sum += entry.Value[i];
			}

			Debug.Assert(sum > 0);

			for (int i = 0; i < 27; i++) {
				entry.Value[i] /= sum;
			}

			float cumu = 0;
			for (int i = 0; i < 27; i++) {
				cumu += entry.Value[i];
				entry.Value[i] = cumu;
			}
		}

		return data;
	}

	private LanguageData data;

	private Language(LanguageData data) {
		this.data = data;
	}

	/*public void saveMatrix(string file) {
		System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
		bool first = true;
		for (int i = 0; i < 28; i++) {
			for (int j = 0; j < 28; j++) {
				stringBuilder.Append(matrix[i, j]);
				if (i < 27 || j < 27) {
					stringBuilder.Append(',');
				}
			}
		}

		System.IO.File.WriteAllText(file, stringBuilder.ToString());
	}*/

	public string genWord() {
		int length = 2 + (int)(8 * Helper.getRandom());
		string word = "";

		for (int i = 0; i < length; i++) {
			if (i+1 <= depth) {
				word += getNextChar(word.Substring(0, i), Helper.getRandom());
			}
			else {
				word += getNextChar(word.Substring(i-depth, depth), Helper.getRandom());
			}
		}

		return word;
	}

	private char getNextChar(string root, float rand) {
		if (!data.ContainsKey(root)) {
			Debug.Assert(root != "");
			return getNextChar(root.Substring(1, root.Length-1), rand);
		}

		for (int i = 0; i < 27; i++) {
			if (data[root][i] >= rand) {
				return indexToChar(i);
			}
		}

		Debug.Assert(false);

		return 'z';
	}
}
