using System;
using System.Collections.Generic;
using System.Linq;
using ZionVault.Basic;

namespace ZionVault.Binder.OCR
{
    public class Line : IDisposable
    {
        private int index;
        private Location position;
        private List<Word> words;
        List<Word> alternativesNonStrict;
        List<Word> alternativesStrict;

        public string Text
        {
            get
            {
                return String.Join(" ", 
                    (from word in words
                    select word.Text).ToArray());
            }
        }

        public void Dispose()
        {
            if (this.words != null)
                foreach (Word w in words)
                    w.Dispose();
            GC.SuppressFinalize(this);
        }

        public int Index
        {
            get { return index; }
            set { index = value; }
        }

        public Location Location
        {
            get { return position; }
            set { position = value; }
        }

        public List<Word> Words
        {
            get { return words; }
            set { words = value; }
        }

        public Line(int _Index, Location _Posision, List<Word> _Word)
        {
            this.index = _Index;
            this.position = _Posision;
            this.words = _Word;
        }

        public Line(System.Xml.XmlElement node)
        {
            this.words = new List<Word>();

            this.index = int.Parse(node.GetAttribute(@"index"));
            this.position = new Location(node.GetAttribute(@"location"));
            foreach (System.Xml.XmlElement word in node.GetElementsByTagName(@"word"))
            {
                this.words.Add(new Word(word));
            }
        }

        public List<Word> Alternative(bool strict = false)
        {
            if (strict)
            {
                if (alternativesStrict == null)
                {
                    alternativesStrict = GetAlternative(true);
                }
                return alternativesStrict;
            }

            if (alternativesNonStrict == null)
            {
                alternativesNonStrict = GetAlternative(false);
            }
            return alternativesNonStrict;
        }


        private List<Word> GetAlternative(bool strict = false)
        {
            List<Word> alternatives = new List<Word>();
            bool strictFalseFindAlternative = false;


            for (int i = 0; i < words.Count; i++)
            {
                strictFalseFindAlternative = false;
                for (int j = i; j <= words.Count; j++)
                {
                    if (strictFalseFindAlternative)
                        break;

                    if (alternatives.Count() == 10)
                        break;

                    Word word;
                    if (strict)
                        word = Parse(i, j);
                    else
                    {
                        //word = Parse(i, words.Count);
                        word = Parse(i, words.Count - 1);
                    }

                    //if (words.Count > 25 && alternatives.Count() == 25)
                    if (word != null )
                    {
                        if ((from alternative in alternatives
                             where alternative.Text == word.Text && alternative.Location.ToString() == word.Location.ToString()
                             select alternative).Count() == 0)
                        {
                            alternatives.Add(word);

                            if(!strict)
                                strictFalseFindAlternative = true;
                        }
                    }
                }

                if (alternatives.Count() == 10)
                    break;
            }
            return alternatives;
        }


        public List<Word> all;
        public List<Word> All(bool strict = false)
        {
            //if (all == null)
            //{
            List<Word> all = Alternative(strict);

            if (all == null)
                all = new List<Word>();

            foreach (Word word in words)
            {
                if ((from alternative in all
                     where alternative.Text == word.Text && alternative.Location.ToString() == word.Location.ToString()
                     select alternative).Count() == 0)
                    if (word.Text.Trim() != string.Empty)
                        all.Add(word);
            }
            // }
            return all;


            /*alternatives.ForEach(op =>
            {
                System.IO.File.AppendAllText(@"D:\Projetos\ImageOne\Exames\Task\Entrada\alternativas.txt", op.Text + "\n");
            });*/

        }

        private Word Parse(int begin, int end)
        {
            List<Word> Basic = (from word in words
                                where word.Index >= begin
                                      && word.Index <= end
                                select word).ToList();

            if (Basic.Count == 0)
                return null;

            var w = Basic.OrderByDescending(x => x.Location.Width).First();
            int average = (int)((w.Location.Right - w.Location.Left) / w.Text.Length);

            /*int average = (int)((from word in Basic 
                                 select (word.Location.Right - word.Location.Left) / word.Text.Length).Max());*/

            List<Word> ToUse = new List<Word>();
            for (int i = 0; i < Basic.Count; i++)
            {
                if (ToUse.Count != 0 && ((Basic[i].Location.Left - ToUse.Last().Location.Right) > (average * 2)))
                    break;
                ToUse.Add(Basic[i]);
            }

            var alternative = new Word(-1,
                        Convert.ToInt32(
                            (from word in ToUse
                             select word.Quality).Average()
                        ),
                        new Location(
                             (from word in ToUse
                              select this.Location.Top).Min(),
                             (from word in ToUse
                              select word.Location.Right).Max(),
                             (from word in ToUse
                              select this.Location.Bottom).Max(),
                             (from word in ToUse
                              where word.Index >= begin
                              && word.Index <= end
                              select word.Location.Left).Min()
                        ),
                        String.Join(" ", (from word in ToUse
                                          select word.Text).ToArray()));

            alternative.Location.Top = this.position.Top;
            alternative.Location.Bottom = this.position.Bottom;

            return alternative;
        }

        public System.Xml.XmlNode Export(System.Xml.XmlDocument context)
        {
            System.Xml.XmlElement root = context.CreateElement(@"line");
            root.SetAttribute(@"index", this.index.ToString());
            root.SetAttribute(@"location", this.position.ToString());

            foreach (Word element in this.words)
            {
                root.AppendChild(element.Export(context));
            }

            return root;
        }
    }
}
