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

namespace ZionVault.Binder.OCR
{
    public class Document : ZionVault.Util.Xml, IDisposable
    {
        private const string MAINTAG = @"Document";

        private string path;

        private string fileName;

        public string FileName { get => fileName; set => fileName = value; }

        private List<Page> pages;

        public List<Page> Pages
        {
            get { return pages; }
            set { pages = value; }
        }

        public Page PageAt(int index)
        {
            return Pages.ElementAtOrDefault(index);
        }

        public void Dispose()
        {
            if (this.pages != null)
                foreach (Page p in pages)
                    p.Dispose();

            GC.SuppressFinalize(this);
        }

        public Document()
        {
            this.pages = new List<Page>();
        }

        public Document(List<string> htmlDocument, List<string> txtDocument, List<Barcode> barcodes)
        {
            this.pages = new List<Page>();

            foreach (string html in htmlDocument)
            {
                HtmlDocument doc = new HtmlDocument();
                doc.LoadHtml(html);

                HtmlNode body = doc.DocumentNode.SelectSingleNode(@"//body");
                int i = 0;
                foreach (HtmlNode page in body.SelectNodes(@"div"))
                {
                    pages.Add(loadPage(i, page, txtDocument[htmlDocument.IndexOf(html)], barcodes));
                    i++;
                }
            }
        }

        public Document(string htmlDocument, string txtDocument, List<Barcode> barcodes)
        {
            this.pages = new List<Page>();

            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(htmlDocument);
            HtmlNodeCollection bodies = doc.DocumentNode.SelectNodes(@"//body");
            int i = 0;
            foreach (HtmlNode body in bodies)
            {
                foreach (HtmlNode content in body.SelectNodes(@"div"))
                {
                    pages.Add(loadPage(i, content, txtDocument, barcodes));
                }
            }
            i++;
        }

        private void LoadXmlDoc(string path)
        {
            try
            {
                this.Load(path, MAINTAG);
            }
            catch
            {
                System.Threading.Thread.Sleep(500);
                this.LoadXmlDoc(path);
            }
        }

        public Document(string xmlPath, bool encrypted = true)
        {
            this.Load(xmlPath, MAINTAG, encrypted ? true : false, encrypted);
            this.pages = new List<Page>();
            foreach (XmlElement xmlPage in this.GetElementsByTagName(@"page"))
            {
                this.Pages.Add(new Page(xmlPage));
            }
        }

        public void Save(string path, bool encripty = true, bool hide = true)
        {
            try
            {
                string current = System.DateTime.Now.ToString();
                try
                {
                    this.AppendChild(this.CreateXmlDeclaration("1.0", @"UTF-8", @"yes"));
                }
                catch { }

                XmlNode root =
                    this.GetElementsByTagName(MAINTAG).Item(0) ??
                    this.CreateElement(MAINTAG);

                root.RemoveAll();

                this.pages.OrderBy(page => page.Index).ToList().ForEach(page =>
                {
                    root.AppendChild(page.Export(this));
                });

                this.AppendChild(root);

                base.Save(path, MAINTAG, encripty, hide);
            }
            catch(Exception ex){
                Console.WriteLine(ex.Message);
            }
        }
        public void Save(bool encripty = true, bool hide = true)
        {
            string current = System.DateTime.Now.ToString();
            try
            {
                this.AppendChild(this.CreateXmlDeclaration("1.0", @"UTF-8", @"yes"));
            }
            catch { }

            XmlNode root =
                this.GetElementsByTagName(MAINTAG).Item(0) ??
                this.CreateElement(MAINTAG);

            root.RemoveAll();

            this.pages.OrderBy(page => page.Index).ToList().ForEach(page =>
            {
                root.AppendChild(page.Export(this));
            });

            this.AppendChild(root);

            base.Save(this.path, MAINTAG, encripty, hide);
        }

        private Page loadPage(int index, HtmlNode info, string text, List<Barcode> barcodes)
        {
            string[] attr = info.Attributes[@"title"].Value.ToString().Split(';');
            List<Area> areas = new List<Area>();
            int i = 0;
            try
            {
                foreach (HtmlNode area in info.SelectNodes(@"div"))
                {
                    areas.Add(loadArea(i, area));
                    i++;
                }
            }
            catch { }

            return new Page(index, text, areas, barcodes);
        }

        private Area loadArea(int index, HtmlNode info)
        {
            string[] attr = info.Attributes[@"title"].Value.ToString().Split(';');
            List<Paragraph> paragraphes = new List<Paragraph>();
            int i = 0;
            foreach (HtmlNode paragraph in info.SelectNodes(@"p"))
            {
                paragraphes.Add(loadParagraph(i, paragraph));
                i++;
            }

            return new Area(index,
                    new Location(
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[1]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[2]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[3]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[0])
                            ),
                            paragraphes);
        }

        private Paragraph loadParagraph(int index, HtmlNode info)
        {
            string[] attr = info.Attributes[@"title"].Value.ToString().Split(';');
            List<Line> lines = new List<Line>();
            int i = 0;
            foreach (HtmlNode line in info.SelectNodes(@"span"))
            {
                lines.Add(loadLine(i, line));
                i++;
            }

            return new Paragraph(index,
                    new Location(
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[1]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[2]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[3]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[0])
                            ),
                            lines);
        }

        private Line loadLine(int index, HtmlNode info)
        {
            string[] attr = info.Attributes[@"title"].Value.ToString().Split(';');
            List<Word> words = new List<Word>();
            int i = 0;
            foreach (HtmlNode word in info.SelectNodes(@"span"))
            {
                words.Add(loadWord(i, word));
                i++;
            }

            return new Line(index,
                    new Location(
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[1]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[2]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[3]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[0])
                            ),
                            words);
        }

        private Word loadWord(int index, HtmlNode info)
        {
            string[] attr = info.Attributes[@"title"].Value.ToString().Split(';');
            return new Word(index,
                    int.Parse(attr[1].Replace("x_wconf", string.Empty)),
                    new Location(
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[1]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[2]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[3]),
                            int.Parse(attr[0].Replace(@"bbox", string.Empty).Trim().Split(' ')[0])
                            ),
                    System.Web.HttpUtility.HtmlDecode(info.InnerText)
           );
        }

    }
}
