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

namespace bbiwarg.Recognition.Tracking
{
    public enum TrackingState
    {
        Undefined = 0,
        Detected = 1,
        Tracked = 2,
        Lost = 3,
        Deleted = 4
    }

    public abstract class TrackedObject<T> where T : TrackableObject
    {
        private int numFramesDetectedUntilTracked;
        private int numFramesLostUntilDeleted;
        private bool wasTrackedBefore;

        public int ID { get; private set; }
        public T CurrentObject { get; private set; }
        public T LastObject { get; private set; }
        public TrackingState CurrentState { get; private set; }
        public TrackingState PreviousState { get; private set; }
        public int NumFramesInCurrentState { get; private set; }

        public TrackedObject(int id, T detectedObject, int numFramesDetectedUntilTracked, int numFramesLostUntilDeleted)
        {
            ID = id;
            this.numFramesDetectedUntilTracked = numFramesDetectedUntilTracked;
            this.numFramesLostUntilDeleted = numFramesLostUntilDeleted;
            wasTrackedBefore = false;

            CurrentObject = detectedObject;
            LastObject = detectedObject;
            CurrentState = TrackingState.Detected;
            PreviousState = TrackingState.Undefined;
            NumFramesInCurrentState = 1;
        }

        public abstract float calculateSimilarity(T detectedObject);

        public virtual void updateFrame(T detectedObject)
        {
            if (detectedObject != null)
            {
                detectedObject.setTracked(ID);
                LastObject = detectedObject;
            }

            TrackingState newState = getNewState(detectedObject);

            if (!wasTrackedBefore && newState == TrackingState.Tracked)
                wasTrackedBefore = true;

            PreviousState = CurrentState;
            CurrentState = newState;
            CurrentObject = detectedObject;

            if (PreviousState == newState)
                NumFramesInCurrentState++;
            else
                NumFramesInCurrentState = 1;
        }

        private TrackingState getNewState(T detectedObject)
        {
            TrackingState newState = TrackingState.Undefined;

            if (detectedObject != null)
            {
                switch (CurrentState)
                {
                    case TrackingState.Lost:
                        if (wasTrackedBefore)
                            newState = TrackingState.Tracked;
                        else
                            newState = TrackingState.Detected;
                        break;
                    case TrackingState.Tracked:
                        newState = TrackingState.Tracked;
                        break;
                    case TrackingState.Detected:
                        if (NumFramesInCurrentState >= numFramesDetectedUntilTracked)
                            newState = TrackingState.Tracked;
                        else
                            newState = TrackingState.Detected;
                        break;
                }
            }
            else if (CurrentState == TrackingState.Lost && NumFramesInCurrentState >= numFramesLostUntilDeleted)
                newState = TrackingState.Deleted;
            else
                newState = TrackingState.Lost;

            return newState;
        }

        protected float getPositionSimilarity(Vector2D p1, Vector2D p2, float maxRelativeDistance)
        {
            float distance = p1.getDistanceTo(p2);
            float similarity = Math.Max(1 - distance / (maxRelativeDistance * Parameters.ImageDiagonalLength), 0);
            return similarity;
        }
    }
}
