using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using bbiwarg.Recognition.Tracking;
using bbiwarg.Utility;

namespace bbiwarg.Recognition.TouchRecognition
{
    public enum TouchEventType
    {
        Down,
        Move,
        Up
    }

    public delegate void TouchEventHandler(TrackedTouch sender, TouchEventArgs e);
    
    public class TouchEventArgs
    {
        public int TrackID { get; private set; }
        public TouchEventType Type { get; private set; }
        public Touch Touch { get; private set; }

        public TouchEventArgs(int trackID, TouchEventType type, Touch touch)
        {
            TrackID = trackID;
            Type = type;
            Touch = touch;
        }
    }

    public class TrackedTouch : TrackedObject<Touch>
    {
        private Kalman2DPositionFilter absolutePositionKalman;
        private int fingerID;

        public Vector2D AbsolutePositionPrediction { get { return absolutePositionKalman.getPrediction(); } }
        public Touch OptimizedTouch { get; private set; }
        public bool IsTouchActive { get; private set; }

        public event TouchEventHandler TouchEvent;

        public TrackedTouch(int id, Touch detectedTouch, int numFramesDetectedUntilTracked, int numFramesLostUntilDeleted)
            : base(id, detectedTouch, numFramesDetectedUntilTracked, numFramesLostUntilDeleted)
        {
            absolutePositionKalman = new Kalman2DPositionFilter(Parameters.TouchmXX, Parameters.TouchmXY, Parameters.TouchmYY);
            absolutePositionKalman.setInitialPosition(detectedTouch.AbsolutePosition);

            fingerID = detectedTouch.Finger.TrackID;
            IsTouchActive = false;

            logStateChange();
        }

        public override void updateFrame(Touch detectedTouch)
        {
            base.updateFrame(detectedTouch);

            if (NumFramesInCurrentState == 1)
                logStateChange();

            if (detectedTouch != null)
            {
                absolutePositionKalman.getCorrectedPosition(detectedTouch.AbsolutePosition);

                updateOptimizedTouch(detectedTouch);

                if (CurrentState == TrackingState.Tracked)
                {
                    if (!IsTouchActive)
                        TriggerTouchDown();
                    else
                        TriggerTouchMove();
                }
            }
            else if (IsTouchActive && CurrentState == TrackingState.Deleted)
                TriggerTouchUp();

        }

        private void updateOptimizedTouch(Touch detectedTouch)
        {
            OptimizedTouch = new Touch(AbsolutePositionPrediction, detectedTouch.Finger, detectedTouch.Palm);
        }

        public override float calculateSimilarity(Touch detectedTouchEvent)
        {
            float fingerSimilarity = (fingerID == detectedTouchEvent.Finger.TrackID) ? 1 : 0;
            //float absolutePositionSimilarity = getPositionSimilarity(AbsolutePositionPrediction, detectedTouchEvent.AbsolutePosition, Parameters.TouchTrackerMaxAbsolutePositionMove);
            //float relativePositionSimilarity = getPositionSimilarity(RelativePositionPrediction, detectedTouchEvent.RelativePosition, Parameters.TouchTrackerMaxRelativePositionMove); 

            float similarity = fingerSimilarity;// *absolutePositionSimilarity * relativePositionSimilarity;

            return similarity;
        }

        private void logStateChange()
        {
            String stateAsString = CurrentState.ToString().ToLower();
            Logger.log(String.Format("TouchEvent #{0} {1}", this.ID, stateAsString), LogSubject.TouchTracker);
        }

        private void TriggerTouchDown()
        {
            if (TouchEvent != null)
            {
                IsTouchActive = true;
                TouchEvent(this, new TouchEventArgs(ID, TouchEventType.Down, OptimizedTouch));
            }
        }

        private void TriggerTouchMove()
        {
            if (TouchEvent != null)
            {
                TouchEvent(this, new TouchEventArgs(ID, TouchEventType.Move, OptimizedTouch));
            }
        }

        private void TriggerTouchUp()
        {
            if (TouchEvent != null)
            {
                IsTouchActive = false;
                TouchEvent(this, new TouchEventArgs(ID, TouchEventType.Up, OptimizedTouch));
            }
        }

    }
}
