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

namespace bbiwarg.Recognition.TouchRecognition
{
    public class PalmTouchEventArgs : EventArgs
    {
        public int TrackID { get; private set; }
        public Vector2D Position { get; private set; }

        public PalmTouchEventArgs(int trackID, Vector2D position)
        {
            TrackID = trackID;
            Position = position;
        }
    }

    public delegate void PalmTouchEventHandler(object sender, PalmTouchEventArgs e);

    class TouchTracker : Tracker<TouchEvent>
    {
        public List<TouchEvent> TouchEvents { get { return TrackedObjects; } }
        public Dictionary<int, Kalman2DPositionFilter> kalmanFilters;
        public event PalmTouchEventHandler PalmTouchDown;
        public event PalmTouchEventHandler PalmTouchMove;
        public event PalmTouchEventHandler PalmTouchUp;

        protected virtual void OnPalmTouchDown(PalmTouchEventArgs e) { if (PalmTouchDown != null) PalmTouchDown(this, e); }
        protected virtual void OnPalmTouchMove(PalmTouchEventArgs e) { if (PalmTouchMove != null) PalmTouchMove(this, e); }
        protected virtual void OnPalmTouchUp(PalmTouchEventArgs e) { if (PalmTouchUp != null) PalmTouchUp(this, e); }

        public TouchTracker()
            : base(Constants.TouchEventNumFramesDetectedUntilTracked, Constants.TouchEventNumFramesLostUntilDeleted, Constants.TouchEventMinSimilarityForTracking)
        {
            kalmanFilters = new Dictionary<int, Kalman2DPositionFilter>();
        }

        public new void reset() {
            foreach (int id in kalmanFilters.Keys) {
                OnPalmTouchUp(new PalmTouchEventArgs(id, kalmanFilters[id].getPrediction()));
            }
            kalmanFilters.Clear();
            base.reset();
        }

        protected override void onDetect(object sender, EventArgs e)
        {
            TrackableObjectHistory<TouchEvent> history = (TrackableObjectHistory<TouchEvent>)sender;
            if (history.NumFramesInCurrentState == 1)
                Logger.log("TouchEvent #" + history.ID.ToString() + " detected", LogSubject.TouchTracker);
        }

        protected override void onTrack(object sender, EventArgs e)
        {
            TrackableObjectHistory<TouchEvent> history = (TrackableObjectHistory<TouchEvent>)sender;
            if (history.NumFramesInCurrentState == 1)
                Logger.log("TouchEvent #" + history.ID.ToString() + " tracked", LogSubject.TouchTracker);

            if (history.LastObject is PalmTouchEvent)
            {
                PalmTouchEvent pte = (PalmTouchEvent)history.LastObject;
                if (history.NumFramesInCurrentState == 1)
                    palmTouchDown(history.ID, pte.RelativePalmPosition);
                else
                    palmTouchMove(history.ID, pte.RelativePalmPosition);
            }
        }

        protected override void onRetrack(object sender, EventArgs e)
        {
            TrackableObjectHistory<TouchEvent> history = (TrackableObjectHistory<TouchEvent>)sender;
            if (history.NumFramesInCurrentState == 1)
                Logger.log("TouchEvent #" + history.ID.ToString() + " retracked", LogSubject.TouchTracker);

            if (history.LastObject is PalmTouchEvent)
            {
                PalmTouchEvent pte = (PalmTouchEvent)history.LastObject;
                palmTouchMove(history.ID, pte.RelativePalmPosition);
            }
            else throw new ArgumentException();
        }

        protected override void onLoose(object sender, EventArgs e)
        {
            TrackableObjectHistory<TouchEvent> history = (TrackableObjectHistory<TouchEvent>)sender;
            if (history.NumFramesInCurrentState == 1)
                Logger.log("TouchEvent #" + history.ID.ToString() + " lost", LogSubject.TouchTracker);
        }

        protected override void onDelete(object sender, EventArgs e)
        {
            TrackableObjectHistory<TouchEvent> history = (TrackableObjectHistory<TouchEvent>)sender;
            if (history.NumFramesInCurrentState == 1)
                Logger.log("TouchEvent #" + history.ID.ToString() + " deleted", LogSubject.TouchTracker);

            if (history.LastObject is PalmTouchEvent)
            {
                PalmTouchEvent pte = (PalmTouchEvent)history.LastObject;
                palmTouchUp(history.ID, pte.RelativePalmPosition);
            }
            else throw new ArgumentException();
        }


        private void palmTouchDown(int id, Vector2D relPos) {
            Kalman2DPositionFilter kalmanFilter = new Kalman2DPositionFilter(Constants.TouchmXX, Constants.TouchmXY, Constants.TouchmYY, Constants.TouchProcessNoise);
            kalmanFilter.setInitialPosition(relPos);
            kalmanFilters.Add(id, kalmanFilter);
            OnPalmTouchDown(new PalmTouchEventArgs(id, relPos));
            Logger.log("TouchEvent #" + id.ToString() + " touchDown at (" + relPos.X + ", " + relPos.Y + ")", LogSubject.TouchEvents);
        }

        private void palmTouchMove(int id, Vector2D relPos) {
            Vector2D correctedRelPos = kalmanFilters[id].getCorrectedPosition(relPos);
            OnPalmTouchMove(new PalmTouchEventArgs(id, correctedRelPos));
            Logger.log("TouchEvent #" + id.ToString() + " touchMove to (" + relPos.X + ", " + relPos.Y + ")", LogSubject.TouchEvents);
        }

        private void palmTouchUp(int id, Vector2D relPos) {
            Kalman2DPositionFilter kalmanFilter;
            if (kalmanFilters.TryGetValue(id, out kalmanFilter))
            {
                Vector2D correctedRelPos = kalmanFilter.getCorrectedPosition(relPos);
                OnPalmTouchUp(new PalmTouchEventArgs(id, correctedRelPos));
                kalmanFilters.Remove(id);
                Logger.log("TouchEvent #" + id.ToString() + " touchUp at (" + relPos.X + ", " + relPos.Y + ")", LogSubject.TouchEvents);
            }
        }
    }
}
