using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.Structure;
using bbiwarg.Images;
using bbiwarg.Recognition.FingerRecognition;
using bbiwarg.Recognition.PalmRecognition;
using bbiwarg.Utility;
using bbiwarg.Graphics;

namespace bbiwarg.Recognition.TouchRecognition
{
    class TouchDetector
    {
        private DepthImage depthImage;
        private OutputImage outputImage;
        private List<Finger> fingers;
        public List<TouchEvent> TouchEvents { get; private set; }

        public TouchDetector(List<Finger> fingers, Quadrangle palmQuad, DepthImage depthImage, OutputImage outputImage)
        {
            this.depthImage = depthImage;
            this.outputImage = outputImage;
            this.fingers = fingers;
            this.TouchEvents = new List<TouchEvent>();

            foreach (Finger finger in fingers)
            {
                Vector2D tipPoint = finger.TipPoint;
                Vector2D direction = finger.TipDirection;
                Vector2D directionInv = direction.getInverse();
                Vector2D tipPointInside = (tipPoint + Constants.TouchEventTipInsideFactor * directionInv).moveInBound(Vector2D.Zero, depthImage.BottomRight, direction);
                Vector2D tipPointOutside = (tipPoint + Constants.TouchEventTipOutsideFactor * direction).moveInBound(Vector2D.Zero, depthImage.BottomRight, directionInv);

                Image<Gray, byte> touchMask = getTouchMask(tipPointInside);

                int touchPixels = touchMask.CountNonzero()[0];
                int numPixels = touchMask.Width * touchMask.Height;
                float touchValue = touchPixels / (float)numPixels;

                if (touchValue > Constants.TouchEventMinTouchValue)
                {
                    outputImage.fillCircle(tipPointOutside.IntX, tipPointOutside.IntY, 5, Constants.TouchEventDetectedColor);

                    TouchEvent touchEvent;
                    if (palmQuad != null && palmQuad.isInside(tipPointOutside))
                        touchEvent = new PalmTouchEvent(tipPointOutside, touchMask, finger, palmQuad);

                    else
                        touchEvent = new TouchEvent(tipPointOutside, touchMask, finger);

                    TouchEvents.Add(touchEvent);
                    finger.setTouchEvent(touchEvent);
                }

                outputImage.fillCircle(tipPoint.IntX, tipPoint.IntY, 3, Constants.TouchEventTipColor);
            }
        }

        private Image<Gray, byte> getTouchMask(Vector2D touchPoint)
        {

            int minX = Math.Max(touchPoint.IntX - 2 * Constants.TouchEventAreaSize / 3, 0);
            int maxX = Math.Min(touchPoint.IntX + Constants.TouchEventAreaSize / 3, depthImage.Width - 1);
            int minY = Math.Max(touchPoint.IntY - 2 * Constants.TouchEventAreaSize / 3, 0);
            int maxY = Math.Min(touchPoint.IntY + Constants.TouchEventAreaSize / 3, depthImage.Height - 1);

            Vector2D relTouchPoint = new Vector2D(touchPoint.IntX - minX, touchPoint.IntY - minY);
            Rectangle rect = new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1);
            Image<Gray, byte> touchArea = depthImage.Image.Copy(rect);
            Image<Gray, byte> touchMask = new Image<Gray, byte>(rect.Width + 2, rect.Height + 2);

            MCvConnectedComp comp = new MCvConnectedComp();
            CvInvoke.cvFloodFill(touchArea, relTouchPoint, new MCvScalar(255), new MCvScalar(Constants.TouchEventFloodfillLowDiff), new MCvScalar(Constants.TouchEventFloodfillHighDiff), out comp, Emgu.CV.CvEnum.CONNECTIVITY.EIGHT_CONNECTED, Emgu.CV.CvEnum.FLOODFILL_FLAG.DEFAULT, touchMask);

            Rectangle cropRect = new Rectangle(1, 1, rect.Width, rect.Height);
            return touchMask.Copy(cropRect);
        }


        /*
        private float getTouchValue(Point touchPoint)
        {
            int floodValue = 255;

            int minX = Math.Max(touchPoint.X - 2*Constants.TouchEventAreaSize / 3, 0);
            int maxX = Math.Min(touchPoint.X + Constants.TouchEventAreaSize / 3, depthImage.Width-1);
            int minY = Math.Max(touchPoint.Y - 2*Constants.TouchEventAreaSize / 3, 0);
            int maxY = Math.Min(touchPoint.Y + Constants.TouchEventAreaSize / 3, depthImage.Height-1);

            Vector2D relTouch = new Vector2D(touchPoint.X - minX, touchPoint.Y - minY);
            Rectangle rect = new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1);
            Image<Gray, byte> touchArea = depthImage.Image.Copy(rect);

            MCvConnectedComp comp = new MCvConnectedComp();
            CvInvoke.cvFloodFill(touchArea, relTouch, new MCvScalar(floodValue), new MCvScalar(Constants.TouchEventFloodfillLowDiff), new MCvScalar(Constants.TouchEventFloodfillHighDiff), out comp, Emgu.CV.CvEnum.CONNECTIVITY.EIGHT_CONNECTED, Emgu.CV.CvEnum.FLOODFILL_FLAG.DEFAULT, IntPtr.Zero);

            int matchedPixels = 0;
            int countedPixels = 0;
            for (int x = minX; x <= maxX; x++)
            {
                for (int y = minY; y <= maxY; y++)
                {
                    Color color = outputImage.getColotAt(x, y);
                    byte depth = touchArea.Data[y - minY, x - minX, 0];
                    Color subtractColor;
                    if(depth == floodValue)
                    {
                        matchedPixels++;
                        subtractColor = Constants.TouchEventAreaMatchedSubtractColor;
                    }
                    else
                    {
                        subtractColor = Constants.TouchEventAreaNonMatchedSubtractColor;
                    }
                    Color newColor = Color.FromArgb(Math.Max(color.R - subtractColor.R, 0), Math.Max(color.G - subtractColor.G, 0), Math.Max(color.B - subtractColor.B, 0));
                    outputImage.drawPixel(x, y, newColor);
                    countedPixels++;
                }
            }

            float rel = (float)matchedPixels / (float)countedPixels;

            outputImage.drawLineSegment(new Utility.LineSegment2D(new Vector2D(minX, maxY), new Vector2D(minX + rel * Constants.TouchEventAreaSize, maxY)), Constants.TouchEventStatusBarColor, 2);
            
            return rel;
        }*/
    }
}