using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Threading.Tasks;

namespace Txgy.EWS.Client.FocalMechanism.Model
{
    // summary
    // object for the result of polygon
    public class Polygon :Graph
    {
        public List<FMStation> upperBound { get; set; }
        public List<FMStation> lowerBound { get; set; }

        public Line axis { get; set; }

        public Polygon()
        {
            upperBound = new List<FMStation>();
            lowerBound = new List<FMStation>();
        }

        // summary
        // add point to current polygon
        public void addPoint(FMStation p)
        {
            if (p == null) return;
            //upper bound
            int upIndex = getPoint(upperBound, p);
            if (upIndex == upperBound.Count)
            {
                upperBound.Add(p);
                smoothUppderBound(upperBound, upperBound.Count - 1);
            }
            else if (upIndex == -1)
            {
                upperBound.Insert(0, p);
                smoothUppderBound(upperBound, 0);
            }
            else if (p.X == upperBound[upIndex].X)
            {
                if (p.Y > upperBound[upIndex].Y)
                {
                    upperBound[upIndex] = p;
                    smoothUppderBound(upperBound, upIndex);
                }
            }
            else
            {
                if (isAbove(upperBound[upIndex - 1], upperBound[upIndex], p))
                {
                    upperBound.Insert(upIndex, p);
                    smoothUppderBound(upperBound, upIndex);
                }
            }

            //lower bound
            int downIndex = getPoint(lowerBound, p);
            if (downIndex == lowerBound.Count)
            {
                lowerBound.Add(p);
                smoothLowerBound(lowerBound, lowerBound.Count - 1);
            }
            else if (downIndex == -1)
            {
                lowerBound.Insert(0, p);
                smoothLowerBound(lowerBound, 0);
            }
            else if (p.X == lowerBound[downIndex].X)
            {
                if (p.Y > lowerBound[downIndex].Y)
                {
                    lowerBound[downIndex] = p;
                    smoothLowerBound(lowerBound, downIndex);
                }
            }
            else
            {
                if (isBelow(lowerBound[downIndex - 1], lowerBound[downIndex], p))
                {
                    lowerBound.Insert(downIndex, p);
                    smoothLowerBound(lowerBound, downIndex);
                }
            }
        }

        // summary
        // check if this polygon contains this station
        public bool contains(FMStation p)
        {
            if (p == null) return false;
            //upper bound
            int upIndex = getPoint(upperBound, p);
            if (upIndex == upperBound.Count || upIndex == -1)
            {
                return false;
            }
            else if (p.X == upperBound[upIndex].X)
            {
                if (p.Y > upperBound[upIndex].Y)
                {
                    return false;
                }
            }
            else
            {
                if (isAbove(upperBound[upIndex - 1], upperBound[upIndex], p))
                {
                    return false;
                }
            }

            //lower bound
            int downIndex = getPoint(lowerBound, p);
            if (downIndex == lowerBound.Count || downIndex == -1)
            {
                return false;
            }
            else if (p.X == lowerBound[downIndex].X)
            {
                if (p.Y > lowerBound[downIndex].Y)
                {
                    return false;
                }
            }
            else
            {
                if (isBelow(lowerBound[downIndex - 1], lowerBound[downIndex], p))
                {
                    return false;
                }
            }
            return true;
        }

        private int getPoint(List<FMStation> l, FMStation p)
        {
            if (l.Count == 0 || p.X < l[0].X) return -1;
            for (int i = 0; i < l.Count; i++)
            {
                if (p.X <= l[i].X) return i;
            }
            return l.Count;
        }

        private void smoothUppderBound(List<FMStation> l, int i)
        {
            while (l.Count - i > 2 && isBelow(l[i], l[i + 2], l[i + 1]))
            {
                l.RemoveAt(i + 1);
            }
            while (i > 1 && isBelow(l[i - 2], l[i], l[i - 1]))
            {
                l.RemoveAt(i - 1);
                i--;
            }
        }

        private void smoothLowerBound(List<FMStation> l, int i)
        {
            while (l.Count - i > 2 && isAbove(l[i], l[i + 2], l[i + 1]))
            {
                l.RemoveAt(i + 1);
            }
            while (i > 1 && isAbove(l[i - 2], l[i], l[i - 1]))
            {
                l.RemoveAt(i - 1);
                i--;
            }
        }

        // summary
        // return if target point is above the line from a to b
        private bool isAbove(FMStation a, FMStation b, FMStation target)
        {
            double lineY = (b.Y - a.Y) / (b.X - a.X) * (target.X - a.X) + a.Y;
            return target.Y >= lineY;
        }

        // summary
        // return if target point is below the line from a to b
        private bool isBelow(FMStation a, FMStation b, FMStation target)
        {
            double lineY = (b.Y - a.Y) / (b.X - a.X) * (target.X - a.X) + a.Y;
            return target.Y <= lineY;
        }
    }
}
