using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WordNetClasses;
using System.Xml;
using System.Collections;

namespace WordNetTools
{
    public class BasicTools
    {
        string resPath = "";
        private const string dictionaryPath = @"e:\Program Files\WordNet\2.1\dict\";

        public BasicTools(string resourcePath)
        {
            resPath = resourcePath;
        }

        public void SearchWord()
        {
            SearchWordInWordNet("dog", "noun", "overview");
        }

        public Wnlib.Search SearchWordInWordNet(string word, string partOfSpeech, string type)
        {
            string dictPath = dictionaryPath;
            Wnlib.WNCommon.path = dictPath;

            Wnlib.PartOfSpeech pos = Wnlib.PartOfSpeech.of(partOfSpeech);
            Wnlib.Search myOverviewSearch = new Wnlib.Search(word, false, pos, new Wnlib.SearchType(false, "OVERVIEW"), 0); //Search Types are defined in util.cs:107

            return myOverviewSearch;
        }

        public string AnalyseMeaningsInText(string[] question)
        {
            List<List<AnalysedWord>> myText = TextToAW(question);
            XiaobinAlgorithm alg = new XiaobinAlgorithm();
            myText = alg.DisambiguateText(myText);
            string response = String.Empty;
            int i = 0;
            foreach (List<AnalysedWord> sentence in myText) //dla każdego zdania
            {
                response += "Sentence " + (i + 1).ToString() + ":" + Environment.NewLine;
                foreach (AnalysedWord word in sentence)
                {
                    response += word.Word + " - " + (word.Sense == null ? "unknown/not important" : word.Sense.defn) + Environment.NewLine;
                }
            }
            return response;
        }

        public string ParaphraseText(string[] question)
        {
            List<List<AnalysedWord>> myText = TextToAW(question);
            XiaobinAlgorithm alg = new XiaobinAlgorithm();
            myText = alg.DisambiguateText(myText);
            string response = String.Empty;
            int i = 0;
            foreach (List<AnalysedWord> sentence in myText) //dla każdego zdania
            {
                foreach (AnalysedWord word in sentence)
                {
                    if (word.Sense != null)
                    {
                        response += GetRandomSynonym(word) + " ";
                    }
                    else
                    {
                        response += word.Word + " ";
                    }
                }
            }
            return response;
        }

        public string AnswerSomething(string[] question)
        {
            List<List<AnalysedWord>> myText = TextToAW(question);
            foreach (List<AnalysedWord> sentence in myText)
            {
                foreach (AnalysedWord word in sentence)
                {
                    if (!word.Meaningless.HasValue)
                        word.Meaningless = IsMeaninglessWord(word.Word);
                }
            }
            XiaobinAlgorithm alg = new XiaobinAlgorithm();
            myText = alg.DisambiguateText(myText);
            string response = String.Empty;
            List<string> responseCollection = new List<string>();
            
            foreach (List<AnalysedWord> sentence in myText)
            {
                foreach (AnalysedWord word in sentence)
                {
                    if (word.Sense != null)
                    {
                        //try some meronyms
                        string relatedMeronym = GetRandomRelatedWord(word, "MERONYM");
                        if (relatedMeronym != String.Empty)
                        {
                            responseCollection.Add(String.Format(GetRandomResponse("Meronym"), word.Word, relatedMeronym));
                        }
                        //try some hyponyms
                        string relatedHyponym = GetRandomRelatedWord(word, "HYPOPTR");
                        if (relatedHyponym != String.Empty)
                        {
                            responseCollection.Add(String.Format(GetRandomResponse("Hyponym"), word.Word, relatedHyponym, MakePlural(word.Word), MakePlural(relatedHyponym), AddIndefiniteArticle(word.Word), AddIndefiniteArticle(relatedHyponym)));
                        }
                        //try a quote
                        if (word.Sense.defn.Contains('"'))
                        {
                            int start = word.Sense.defn.IndexOf("\"") + 1;
                            int end = word.Sense.defn.IndexOf("\"", start);
                            string quote = word.Sense.defn.Substring(start, end - start);
                            responseCollection.Add(String.Format(GetRandomResponse("Quote"), word.Word, quote, MakePlural(word.Word)));
                        }
                        //try definition
                        int defEnd = word.Sense.defn.IndexOf(";");
                        string actualDefinition;
                        if (defEnd == -1)
                        {
                            actualDefinition = word.Sense.defn;
                        }
                        else
                        {
                            actualDefinition = word.Sense.defn.Substring(0, defEnd);
                        }
                        responseCollection.Add(String.Format(GetRandomResponse("Definition"), word.Word, actualDefinition, MakePlural(word.Word), String.Empty, AddIndefiniteArticle(word.Word)));
                    }
                }
            }
            if (responseCollection.Count == 0)
            {
                response = GetRandomResponse("Random");
            }
            else
            {
                Random rnd = new Random(DateTime.Now.Millisecond);
                int num = rnd.Next(responseCollection.Count);
                response = responseCollection[num];
            }
            return response;
        }

        public string MakePlural(string word)
        {
            try
            {
                if (word.EndsWith("man"))
                    return word.Substring(0, word.Length - 3) + "men";
                if (word.EndsWith("s") || word.EndsWith("sh"))
                    return word + "es";
                if (word.EndsWith("y") && word[word.Length - 2] == word[word.Length - 3])
                    return word.Substring(0, word.Length - 1) + "ies";
                return word + "s";
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public string AddIndefiniteArticle(string word)
        {
            if (word.StartsWith("eu") || word.StartsWith("uni"))
                return "a " + word;
            if (word.StartsWith("a") || word.StartsWith("e") || word.StartsWith("i") || word.StartsWith("o") || word.StartsWith("u"))
                return "an " + word;
            return "a " + word;
        }

        public string GetRandomRelatedWord(AnalysedWord word, string relationType)
        {
            Wnlib.WNCommon.path = dictionaryPath;
            Wnlib.Search myRelatedWords = new Wnlib.Search(word.Word, false, word.Sense.pos, new Wnlib.SearchType(false, relationType), word.Sense.sense);
            Random random = new Random((Int32)DateTime.Now.Ticks);
            string result = String.Empty;
            if (myRelatedWords.lexemes.Count > 0)
            {
                int rnd = random.Next(myRelatedWords.lexemes.Count);
                int i = 0;
                foreach (DictionaryEntry lexeme in myRelatedWords.lexemes)
                {
                    if (i == rnd)
                    {
                        result = ((Wnlib.Lexeme)lexeme.Key).word.Replace("_", " ");
                        return result;
                    }
                    i++;
                }
            }
            return result;
        }

        public string GetRandomSynonym(AnalysedWord word)
        {
            Wnlib.WNCommon.path = dictionaryPath;
            //Wnlib.Search mySynonyms = new Wnlib.Search(word.Word, false, word.Sense.pos, new Wnlib.SearchType(false, "HYPERPTR"), word.Sense.sense);
            Random random = new Random((Int32)DateTime.Now.Ticks);
            string result = String.Empty;
            //while (result == String.Empty || result == word.Word)
            //{
            //    int ran = random.Next(mySynonyms.lexemes.Count);
            //    int i = 0;
            //    foreach (DictionaryEntry entry in mySynonyms.lexemes)
            //    {
            //        if (i == ran)
            //        {
            //            result = ((Wnlib.Lexeme)entry.Key).word;
            //            break;
            //        }
            //        i++;
            //    }
            //}
            
            if (word.Sense.words.Count() < 2)
            {
                return word.Word;
            }
            int maxCount = 0;
            while (result == String.Empty || result == word.Word)
            {
                int ran = random.Next(word.Sense.words.Count());
                int i = 0;
                foreach (Wnlib.Lexeme lexeme in word.Sense.words)
                {
                    if (i == ran)
                    {
                        result = lexeme.word;
                        break;
                    }
                    i++;
                }
                maxCount++;
                if (maxCount == 20)
                {
                    return word.Word;
                }
            }
            return result.Replace('_', ' ');
        }

        /// <summary>
        /// Divides an array of strings into sentences. Gets rid of punctuation.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public List<List<AnalysedWord>> TextToAW(string[] text)
        {
            List<List<AnalysedWord>> result = new List<List<AnalysedWord>>();
            string[] endOfSentence = {".","!","?"};
            string[] punctuation = {".","!","?", ",", ";", ":", "-"};
            List<AnalysedWord> tmpSentence = new List<AnalysedWord>();
            foreach (string word in text)
            {
                if (endOfSentence.Contains(word))   //koniec zdania
                {
                    if (tmpSentence.Count > 0)
                    {
                        result.Add(tmpSentence);
                        tmpSentence.Clear();
                    }
                }
                if (punctuation.Contains(word)) //jeśli to znak interpunkcyjny to go pomijamy
                {
                    continue;
                }
                AnalysedWord tmpWord = new AnalysedWord(word);
                tmpSentence.Add(tmpWord);
            }
            if (tmpSentence.Count > 0) result.Add(tmpSentence);


            NLPlib nlp = new NLPlib();
            NLPlib tagger = new NLPlib();
            //ArrayList t = tagger.tag(text);
            foreach (List<AnalysedWord> sentence in result)
            {
                ArrayList t = tagger.tag(sentence);
                for (int i = 0; i < sentence.Count; i++)
                {
                    sentence[i].POS = (string)t[i];
                }
            }

            return result;
        }

        public void LeskAlgorithm(string[] ambiguousWords)
        {
            List<LeskWord> tmpWords = new List<LeskWord>();
            int i = 0;
            foreach (string ambiguousWord in ambiguousWords)
            {
                tmpWords.Add(new LeskWord());
                tmpWords[i].Results.Add(SearchWordInWordNet(ambiguousWord, "noun", "OVERVIEW"));
                //tmpWords[i].Results.Add(SearchWordInWordNet(ambiguousWord, "verb", "GREP"));
                //tmpWords[i].Results.Add(SearchWordInWordNet(ambiguousWord, "adj"));
                //jeszcze inne POSy
                //tmpWords[i].MakeMyList();
                MakeSensesList(tmpWords[i]);
                i++;
            }

            //porównywanie
            for (int k = 0; k < tmpWords.Count; k++)    //dla każdego słowa k
            {
                for (int l = 0; l < tmpWords[k].Senses.Count; l++)  //dla każdego znaczenia l słowa k
                {
                    for (int m = k + 1; m < tmpWords.Count; m++)    //dla każdego k+1 słowa m
                    {
                        foreach (LeskSense targetSense in tmpWords[m].Senses)   //dla każdego znaczenia słowa m
                        {
                            int newValue = MatchDefinitions(tmpWords[k].Senses[l].definition, targetSense.definition);
                            tmpWords[k].Senses[l].value += newValue;
                            targetSense.value += newValue;
                        }
                    }
                }
            }

            //sortowanie
            foreach (LeskWord wrd in tmpWords)
            {
                wrd.Senses.Sort();
            }


        }

        public int MatchDefinitions(List<string> def1, List<string> def2)
        {
            int result = 0;
            foreach (string word1 in def1)
            {
                foreach (string word2 in def2)
                {
                    if (word1 == word2) result++;
                }
            }
            return result;
        }

        public string GetRandomResponse(string type)
        {
            XmlDocument document = new XmlDocument();
            document.Load(resPath + "\\Responses.xml");
            XmlNodeList allResponses = document.GetElementsByTagName(type)[0].ChildNodes;
            Random rnd = new Random(DateTime.Now.Millisecond);
            int number = rnd.Next(allResponses.Count);
            return allResponses[number].InnerText;
        }

        public List<string> GetRidOfMeaninglessWords(List<string> words)
        {
            List<string> result = new List<string>();
            XmlDocument document = new XmlDocument();
            document.Load(resPath + "\\Words.xml");
            XmlNodeList wordSets = document.GetElementsByTagName("MeaninglessWords");
            List<string> meaninglessWords = new List<string>();
            foreach (XmlNode wordset in wordSets)
            {
                foreach (XmlNode word in wordset.ChildNodes)
                {
                    meaninglessWords.Add(word.InnerText);
                }
            }

            for (int j =0; j<words.Count;j++)
            {
                if(words[j].EndsWith("'s"))   //usuwam saxon genitive
                {
                    words[j] = words[j].Substring(0, words[j].LastIndexOf("'s"));
                }
                if (!meaninglessWords.Contains(words[j].ToLower()))   //usuwam nieznaczące słowa
                {
                    result.Add(words[j].ToLower());
                }
            }
            return result;
        }

        public bool IsMeaninglessWord(string word)
        {
            XmlDocument document = new XmlDocument();
            document.Load(resPath + "\\Words.xml");
            XmlNodeList wordSets = document.GetElementsByTagName("MeaninglessWords");
            List<string> meaninglessWords = new List<string>();
            foreach (XmlNode wordset in wordSets)
            {
                foreach (XmlNode wrd in wordset.ChildNodes)
                {
                    meaninglessWords.Add(wrd.InnerText);
                }
            }

            if (meaninglessWords.Contains(word.ToLower()))
            {
                return true;
            }
            return false;
        }

        public void MakeSensesList(LeskWord word)
        {
            foreach (Wnlib.Search srch in word.Results)
            {
                int count = 0;
                foreach (Wnlib.SynSet syn in srch.senses)
                {
                    word.Senses.Add(new LeskSense(syn));
                    word.Senses[count].definition = PrepareDefinition(word.Senses[count].Sense.defn);
                    word.Senses[count].definition = GetRidOfMeaninglessWords(word.Senses[count].definition);
                    count++;
                }
            }
        }

        private List<string> PrepareDefinition(string def)
        {
            List<string> res = new List<string>(def.Split(' '));
            char[] badChars = {'.','?','!',',',';','\"','(',')'}; 
            for (int i = 0; i < res.Count; )
            {
                res[i] = res[i].Trim(badChars);
                if (res[i].Length < 3)
                {
                    res.RemoveAt(i);
                }
                else
                {
                    i++;
                }
            }             
            return res;
        }
    }

    public class LeskSense : IComparable
    {
        public LeskSense(Wnlib.SynSet result)
        {
            Sense = result;
            value = 0;
            //definition = PrepareDefinition(result.defn);
        }

        public Wnlib.SynSet Sense;
        public int value;
        public List<string> definition;

        //private string[] PrepareDefinition(string def)
        //{
        //    List<string> res = new List<string>(def.Split(' '));
        //    for(int i = 0; i< res.Count;)
        //    {
        //        if (res[i].Length < 3)
        //        {
        //            res.RemoveAt(i);
        //        }
        //        else
        //        {
        //            i++;
        //        }
        //    }
        //    //BasicTools BT = new BasicTools(res)
        //    //res =             
        //    return res.ToArray();
        //}

        #region IComparable Members

        public int CompareTo(object obj)
        {
            return ((LeskSense)obj).value.CompareTo(this.value);
        }

        #endregion
    }

    public class LeskWord
    {
        public List<Wnlib.Search> Results = new List<Wnlib.Search>();
        public List<LeskSense> Senses = new List<LeskSense>();

        //public void MakeMyList()
        //{
        //    foreach(Wnlib.Search srch in Results)
        //    {
        //        foreach (Wnlib.SynSet syn in srch.senses)
        //        {
        //            Senses.Add(new LeskSense(syn));
        //        }
        //    }
        //}
    }

    //enum POS {noun = "noun", verb = "verb", adjective = "adj" }
}
