using iTextSharp.text;
using iTextSharp.text.pdf.parser;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZionGraphics.Pdf.Text
{
    class ZionExtractionStrategy : ITextExtractionStrategy
    {

        private float PageHeight { get; }
        private Vector lastStart;
        private Vector lastEnd;

        //Store each line individually. A SortedDictionary will automatically shuffle things around based on the key
        private SortedDictionary<int, LineContent> results = new SortedDictionary<int, LineContent>();

        //Constructor and some methods that aren't used
        public ZionExtractionStrategy(float height) { this.PageHeight = height; }
        public virtual void BeginTextBlock() { }
        public virtual void EndTextBlock() { }
        public virtual void RenderImage(ImageRenderInfo renderInfo) { }

        public virtual String GetResultantText()
        {
            StringBuilder buf = new StringBuilder();
            foreach (var s in results)
            {
                buf.AppendLine(s.Value.stringBuilder.ToString());
            }
            return buf.ToString();
        }
        public ZionVault.Binder.OCR.Page Content
        {
            get
            {
               int index = 1;

               List<ZionVault.Binder.OCR.Line> Lines = new List<ZionVault.Binder.OCR.Line>();
               
                var Y = (from content in results
                         select content.Key).Distinct();

                foreach (var result in results)
               {
                    var tmp = result.Value.Words.Where(w => w.Text != string.Empty).OrderBy(x => x.Location.Left);

                    if (tmp != null && tmp.Count() > 0)
                    {
                        Lines.Add(new ZionVault.Binder.OCR.Line(index,
                            new ZionVault.Basic.Location(
                                (int)(result.Value.Words.Select(w => w.Location.Top).Min()),
                                (int)(result.Value.Words.Select(w => w.Location.Right).Max()),
                               (int)(result.Value.Words.Select(w => w.Location.Bottom).Max()),
                               (int)(result.Value.Words.Select(w => w.Location.Left).Min())),
                           tmp.ToList()
                        ));
                        index++;
                    }
                }

                List<ZionVault.Binder.OCR.Paragraph> p = new List<ZionVault.Binder.OCR.Paragraph>();
                
                if (Lines.Count > 0)
                {
                    p.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.OrderBy(l => l.Index).ToList()
                    ));
                }


                List<ZionVault.Binder.OCR.Area> a = new List<ZionVault.Binder.OCR.Area>();

                if (p.Count > 0)
                {
                    a.Add(new ZionVault.Binder.OCR.Area(
                        1, new ZionVault.Basic.Location(
                                p.First().Location.Top,
                                p.Last().Location.Right,
                                p.First().Location.Bottom,
                                p.First().Location.Left),
                        p
                    ));
                }

                return new ZionVault.Binder.OCR.Page(0, "", a, null);

            }
        }

        public virtual void RenderText(TextRenderInfo renderInfo)
        {
            LineSegment segment = renderInfo.GetBaseline();
            Vector left_bottom = segment.GetStartPoint();
            Vector right_top = renderInfo.GetAscentLine().GetEndPoint();

            int currentLineKey = (int)left_bottom[1];
            
            if (results.Count != 0)
            {
                Vector x0 = left_bottom;
                Vector x1 = lastStart;
                Vector x2 = lastEnd;

                float dist = (x2.Subtract(x1)).Cross((x1.Subtract(x0))).LengthSquared
                    / x2.Subtract(x1).LengthSquared;

                float sameLineThreshold = 1f;

                if (dist <= sameLineThreshold)
                {
                    currentLineKey = (int)lastStart[1];
                }
            }

            currentLineKey = currentLineKey * -1;

            if (!results.ContainsKey(currentLineKey))
            {
                results.Add(currentLineKey, new LineContent());
            }

            string text = renderInfo.GetText();

            var infos = renderInfo.GetCharacterRenderInfos();

            results[currentLineKey].stringBuilder.Append(text);

            lastStart = left_bottom;
            lastEnd = right_top;

            float charWidth = ((float)(right_top[Vector.I1] - left_bottom[Vector.I1])) / text.Length;

            string currentWord = string.Empty;

            float left = left_bottom[Vector.I1];

            float top, bottom;

            top = PageHeight - right_top[Vector.I2];
            bottom = PageHeight - left_bottom[Vector.I2];

            if(top == bottom) { bottom += 1; }

            foreach (char c in text)
            {
                if (ShouldAdd(c.ToString())){
                    currentWord += c;
                    continue;
                }                 

                if (currentWord == string.Empty && !ShouldAdd(c.ToString()))
                {
                    left += charWidth;
                    continue;
                }

                results[currentLineKey].Add( new StringAndRect(
                            currentWord,
                            new ZionVault.Basic.Location(
                                top,
                                left + (currentWord.Length * charWidth),
                                bottom,
                                left
                            )
                    ));

                left += (charWidth * currentWord.Length);
                currentWord = string.Empty;
            }

            if (currentWord != string.Empty)
            {
                results[currentLineKey].Add(new StringAndRect(
                            currentWord,
                            new ZionVault.Basic.Location(
                                top,
                                left + (currentWord.Length * charWidth),
                                bottom,
                                left
                            )
                    ));
            }

        }

        private bool ShouldAdd(string txt)
        {
            return (txt != " " && txt != " " && txt.Trim() != string.Empty);
        }
    }
}
