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

namespace PinyinModel
{
    internal class LanguageMode
    {
        const double infinitesimal = 1e-100;
        const string unknown = "<unknown>";
        Lexicon lexicon;

        internal LanguageMode(string filename, Lexicon lexicon)
        {
            Console.Write("Loading statistical language model from {0} ... ", filename);
            this.lexicon = lexicon;
            stage = 0;
            using (StreamReader reader = File.OpenText(filename))
            {
                
                string line = null;
                while ((line = reader.ReadLine()) != null)
                {
                    if (ParseLine(line))
                        break;
                }
            }
            Console.WriteLine("Done.");
        }

        private int stage;
        private Dictionary<string, double> unigram = new Dictionary<string, double>();
        private Dictionary<string, Dictionary<string, double>> bigram = new Dictionary<string, Dictionary<string, double>>();

        private bool ParseLine(string line)
        {
            if (stage == 0)
            {
                if (line.Substring(0, 8) == @"\1-gram\")
                {
                    stage++;
                }
            }
            else if (stage == 1)
            {
                if (line.Substring(0, 8) == @"\2-gram\")
                {
                    stage++;
                }
                else
                {
                    //1-gram
                    int sppos = line.IndexOf(' ');
                    string phrase = line.Substring(0, sppos);
                    double probability = Double.Parse(line.Substring(sppos + 3, line.IndexOf(' ', sppos + 3) - (sppos + 3)));
                    if (!this.lexicon.Contains(phrase))
                        return false;

                    unigram.Add(phrase, probability);
                }
            }
            else if (stage == 2)
            {
                if (line.Substring(0, 8) == @"\3-gram\")
                {
                    return true;
                }
                else
                {
                    //2-gram
                    int sppos1 = line.IndexOf(' ');
                    string phrase1 = line.Substring(0, sppos1);
                    int sppos2 = line.IndexOf(' ', sppos1 + 1);
                    string phrase2 = line.Substring(sppos1 + 1, sppos2 - sppos1 - 1);
                    double probability = Double.Parse(line.Substring(sppos2 + 3, line.IndexOf(' ', sppos2 + 3) - (sppos2 + 3)));

                    if (phrase1 != unknown && !this.lexicon.Contains(phrase1))
                        return false;
                    if (phrase2 != unknown && !this.lexicon.Contains(phrase2))
                        return false;

                    Dictionary<string, double> dict;
                    if (!bigram.ContainsKey(phrase1))
                    {
                        dict = new Dictionary<string, double>();
                        bigram.Add(phrase1, dict);
                    }
                    else
                    {
                        dict = bigram[phrase1];
                    }
                    dict.Add(phrase2, probability);

                }
            }
            return false;
        }

        public double GetUnigram(string phrase)
        {
            if (unigram.ContainsKey(phrase))
                return unigram[phrase];
            return infinitesimal;
        }

        public double GetBigram(string phrase1, string phrase2)
        {
            Dictionary<string, double> dict;
            double delta = 0;
            if (bigram.ContainsKey(phrase1))
            {
                dict = bigram[phrase1];
                if (dict.ContainsKey(phrase2))
                    return dict[phrase2];
				else if (dict.ContainsKey(unknown))
					delta = dict[unknown];
            }
			else if (bigram.ContainsKey(unknown))
			{
				dict = bigram[unknown];
				if (dict.ContainsKey(phrase2))
					delta = dict[phrase2];
			}
            return GetUnigram(phrase1) * GetUnigram(phrase2) * (Math.E + delta);
        }
    }
}
