using ZionExtractor.Models;
using System.Collections.Generic;
using System.Linq;

namespace ZionExtractor.Controllers.LegworkFilters
{
    class Immediate
    {
        private static double Distance(double a, double b)
        {
            double distance = a - b;

            if (distance < 0)
                return -distance;
            else
                return distance;
        }

        private static double Closest(double a, double b)
        {
            if (a < b)
                return a;
            else
                return b;
        }

        private static bool Exists(List<Hypothesis> hypothesis, Hypothesis element)
        {
            try
            {
                return (from hypothesi in hypothesis
                        where
                            element.Location.Left >= hypothesi.Location.Left
                            && element.Location.Right <= hypothesi.Location.Right
                            && element.Location.Top >= hypothesi.Location.Top
                            && element.Location.Bottom <= hypothesi.Location.Bottom
                        select hypothesi).Count() > 0;
            }
            catch
            {
                return false;
            }
        }

        private static List<Hypothesis> UsableHypothesis(List<Hypothesis> hypothesis)
        {
            List<Hypothesis> result = new List<Hypothesis>();

            foreach (Hypothesis h in (from hypotesi in hypothesis
                                      orderby hypotesi.Location.Top, hypotesi.Location.Left
                                      select hypotesi).ToList())
            {
                if (result.Count == 0 || !Exists(result, h)
                    || h.Location.Top > result.Last().Location.Bottom || h.Location.Bottom < result.Last().Location.Top)
                    result.Add(h);
            }

            return result;
        }

        public static Hypothesis Go(ZionSchematics.Models.SearchNearestTypes.INearest reference,
                                    List<ZionVault.Binder.Output.Field> found,
                                    List<Hypothesis> possibilities)
        {
            double distance = 0;
            switch (reference.Type)
            {
                case "PageBottomEdge":
                    distance = (from p in possibilities
                                select p.Location.Bottom).Max();

                    return Quality.Higher((from p in possibilities
                                           where p.Location.Bottom == distance
                                           select p).ToList());
                case "PageTopEdge":
                    distance = (from p in possibilities
                                select p.Location.Top).Min();

                    return Quality.Higher((from p in possibilities
                                           where p.Location.Top == distance
                                           select p).ToList());
                case "PageLeftEdge":
                    distance = (from p in possibilities
                                select p.Location.Left).Min();

                    return Quality.Higher((from p in possibilities
                                           where p.Location.Left == distance
                                           select p).ToList());
                case "PageRightEdge":
                    distance = (from p in possibilities
                                select p.Location.Right).Max();

                    return Quality.Higher((from p in possibilities
                                           where p.Location.Right == distance
                                           select p).ToList());
                case "YCoordinate":
                    distance = (from p in possibilities
                                select Closest(Distance(p.Location.Top, reference.Y), Distance(p.Location.Bottom, reference.Y))).Min();
                    return Quality.Higher((from p in possibilities
                                           where Closest(Distance(p.Location.Top, reference.Y), Distance(p.Location.Bottom, reference.Y)) == distance
                                           select p).ToList());
                case "XCoordinate":
                    distance = (from p in possibilities
                                select Closest(Distance(p.Location.Left, reference.X), Distance(p.Location.Right, reference.X))).Min();
                    return Quality.Higher((from p in possibilities
                                           where Closest(Distance(p.Location.Left, reference.X), Distance(p.Location.Right, reference.X)) == distance
                                           select p).ToList());
                case "Point":
                    distance = (from p in possibilities
                                select
                                     Closest(Distance(p.Location.Top, reference.Y), Distance(p.Location.Bottom, reference.Y))
                                     + Closest(Distance(p.Location.Left, reference.X), Distance(p.Location.Right, reference.X))
                                ).Min();
                    return Quality.Higher((from p in possibilities
                                           where distance == (Closest(Distance(p.Location.Top, reference.Y), Distance(p.Location.Bottom, reference.Y))
                                                                 + Closest(Distance(p.Location.Left, reference.X), Distance(p.Location.Right, reference.X)))
                                           select p).ToList());
                case "Element":
                    ZionVault.Binder.Output.Field element = Title.Go(found, reference.Reference, true);
                    if (element == null)
                        return Quality.Higher(possibilities);

                    List<Hypothesis> CleanHypotesis = UsableHypothesis(possibilities);

                    distance = (from p in CleanHypotesis
                                select
                                Closest(
                                    Closest(Distance(p.Location.Top, element.Position.Top), Distance(p.Location.Top, element.Position.Bottom)),
                                    Closest(Distance(p.Location.Bottom, element.Position.Top), Distance(p.Location.Bottom, element.Position.Bottom))
                                    )
                                    +
                                Closest(
                                    Closest(Distance(p.Location.Left, element.Position.Left), Distance(p.Location.Left, element.Position.Right)),
                                    Closest(Distance(p.Location.Right, element.Position.Left), Distance(p.Location.Right, element.Position.Right))
                                    )
                                ).Min();

                    return Quality.Higher((from p in CleanHypotesis
                                           where distance == (Closest(
                                                     Closest(Distance(p.Location.Top, element.Position.Top), Distance(p.Location.Top, element.Position.Bottom)),
                                                     Closest(Distance(p.Location.Bottom, element.Position.Top), Distance(p.Location.Bottom, element.Position.Bottom))
                                                 )
                                                 +
                                                 Closest(
                                                     Closest(Distance(p.Location.Left, element.Position.Left), Distance(p.Location.Left, element.Position.Right)),
                                                     Closest(Distance(p.Location.Right, element.Position.Left), Distance(p.Location.Right, element.Position.Right))
                                                 ))
                                           select p).ToList());
                default:
                    return Quality.Higher(possibilities);

            }
        }
    }
}
