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

namespace ZionVault.Binder.OCR
{
    public class Page : IDisposable
    {
        private int index;
        private string text;
        private List<Area> areas;
        private List<Barcode> barcodes;
        private string recognitionStart;
        private string recognitionFinish;

        public void Dispose()
        {
            if (this.areas != null)
                foreach (Area a in areas)
                    a.Dispose();

            if (this.barcodes != null)
                foreach (Barcode b in barcodes)
                    b.Dispose();

            GC.SuppressFinalize(this);
        }

        public string RecStart {
            set { recognitionStart = value; }
        }
        public string RecFinish
        {
            set { recognitionFinish = value; }
        }

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

        public string Text
        {
            get { return text; }
            set { text = value; }
        }

        public List<Area> Areas
        {
            get { return areas; }
            set { areas = value; }
        }

        public List<Barcode> Barcodes
        {
            get { return barcodes; }
            set { barcodes = value; }
        }

        public Page(int index)
        {
            this.index = index;
            this.areas = new List<Area>();
            this.barcodes = new List<Barcode>();
            this.text = string.Empty;
        }

        public Page(int _Index, string _Text, List<Area> _Areas, List<Barcode> _Barcodes)
        {
            this.index = _Index;
            this.areas = _Areas;
            this.text = _Text;
            this.barcodes = _Barcodes;
        }

        public Page(System.Xml.XmlElement node)
        {
            this.text = node.GetElementsByTagName(@"text")[0].InnerText;
            this.index = int.Parse(node.GetAttribute(@"index").ToString());
            this.barcodes = new List<Barcode>();
            foreach (XmlElement barcode in node.GetElementsByTagName(@"barcode"))
            {
                this.barcodes.Add(new Barcode(barcode));
            }

            this.areas = new List<Area>();
            foreach (XmlElement area in node.GetElementsByTagName(@"area"))
            {
                this.Areas.Add(new Area(area));
            }
        }

        public Page(string text)
        {
            this.text = text;
            this.index = 0;
            this.barcodes = new List<Barcode>();
            this.areas = new List<Area>();
            List<Paragraph> paragraphs = new List<Paragraph>();
            List<Line> lines = new List<Line>();

            int indexLine = 0;
            text.Split(new string[] { System.Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
                .ToList().ForEach(txtLine =>
                {
                    List<Word> words = new List<Word>();

                    int indexWord = 0;
                    int left = 0;

                    txtLine.Split(' ').ToList().ForEach(word =>
                    {
                        if (word != null && word != string.Empty)
                        {
                            words.Add(new Word(
                                indexWord,
                                100,
                                new Basic.Location(indexLine, left + word.Length, indexLine, left)
                                , word));

                            indexWord++;
                            left += word.Length + 1;
                        }
                    });

                    lines.Add(new Line(
                        indexLine,
                        new Basic.Location(
                            indexLine, 
                            (int)words.Max(w => w.Location.Right), 
                            indexLine,
                            (int)words.Min(w => w.Location.Left)),
                        words));
                    indexLine++;
                });

            if (lines.Count > 0)
            {
                paragraphs.Add(
                    new ZionVault.Binder.OCR.Paragraph(
                        1,
                        new ZionVault.Basic.Location(
                            lines.First().Location.Top,
                            lines.Last().Location.Right,
                            lines.First().Location.Bottom,
                            lines.First().Location.Left),
                        lines
                ));

                areas.Add(new ZionVault.Binder.OCR.Area(
                    1,
                    new ZionVault.Basic.Location(
                        paragraphs.First().Location.Top,
                        paragraphs.Last().Location.Right,
                        paragraphs.First().Location.Bottom,
                        paragraphs.First().Location.Left),
                    paragraphs
                ));
            }
        }

        public void AppendPage(Page page)
        {
            this.RecFinish = DateTime.UtcNow.ToString();

            var areaIndex = this.Areas.Count;
            this.Areas.ForEach(area =>
            {
                area.Index = areaIndex + area.Index;
                this.Areas.Add(area);
            });

            page.Barcodes?.ForEach(barcode =>
            {
                if (this.Barcodes == null)
                {
                    this.Barcodes = new List<ZionVault.Binder.OCR.Barcode>();
                }
                this.Barcodes.Add(barcode);
            });

            this.Text += "\n" + @"-------" + "\n";
            this.Text += page.Text;
        }

        public string TagsAsText()
        {
            return String.Join("\n", 
                        (from a in areas
                           from p in a.Paragraphes
                           from l in p.Lines
                           select l.Text).ToArray());
        }

        public System.Xml.XmlNode Export(System.Xml.XmlDocument context)
        {
            System.Xml.XmlElement root = context.CreateElement(@"page");
            root.SetAttribute(@"index", this.index.ToString());
            root.SetAttribute(@"recStarted", this.recognitionStart);
            root.SetAttribute(@"recFinished", this.recognitionFinish);
            System.Xml.XmlElement fulltext = context.CreateElement(@"text");
            fulltext.InnerText = this.text;

            System.Xml.XmlElement barcodes = context.CreateElement(@"barcodes");
            if (this.barcodes != null)
                foreach (Barcode bar in this.barcodes)
                {
                    barcodes.AppendChild(bar.Export(context));
                }

            System.Xml.XmlElement ocr = context.CreateElement(@"areas");
            foreach (Area area in this.areas)
            {
                ocr.AppendChild(area.Export(context));
            }

            root.AppendChild(barcodes);
            root.AppendChild(ocr);
            root.AppendChild(fulltext);
            return root;
        }
    }
}
