using System;
using System.Collections.Generic;
using System.Drawing;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using bbiwarg.Recognition.TouchRecognition;
using bbiwarg.Utility;
using bbiwarg.Input.InputHandling;

namespace bbiwarg.Output.DebugOutput
{
    class TouchEventVisualizer
    {
        private Stopwatch timer;

        private Dictionary<int, List<Vector2D>> activeTouches;
        private Dictionary<int, List<Vector2D>> oldTouches;
        private Dictionary<int, long> lastUpdates;
        private int nextFreeID;
        private int lastUpdated;
        private bool active;

        public TouchEventVisualizer()
        {
            reset();

        }

        public void reset()
        {
            timer = new Stopwatch();
            timer.Start();

            nextFreeID = 1;
            lastUpdated = 0;
            activeTouches = new Dictionary<int,List<Vector2D>>();
            oldTouches = new Dictionary<int,List<Vector2D>>();
            lastUpdates = new Dictionary<int,long>();

        }

        public void handleNewFrameData(InputHandler sender, EventArgs e)
        {
            FrameData frameData = sender.FrameData;
            lock (frameData)
            {
                foreach (TouchEventArgs te in frameData.TouchEvents)
                {
                    switch (te.Type)
                    {
                        case TouchEventType.Down:
                            activeTouches.Add(te.TrackID, new List<Vector2D>());
                            activeTouches[te.TrackID].Add(te.Touch.RelativePosition);
                            break;
                        case TouchEventType.Move:
                            activeTouches[te.TrackID].Add(te.Touch.RelativePosition);
                            break;
                        case TouchEventType.Up:
                            activeTouches[te.TrackID].Add(te.Touch.RelativePosition);

                            oldTouches.Add(nextFreeID, activeTouches[te.TrackID]);
                            lastUpdates.Add(nextFreeID, timer.ElapsedMilliseconds);

                            activeTouches.Remove(te.TrackID);
                            nextFreeID++;
                            break;
                    }
                }
            }

        }
        
        public OutputImage getOutputImage()
        {
            long currentTime = timer.ElapsedMilliseconds;
            removeOldPositions(currentTime - Parameters.TouchEventVisualizerFadeOutTime);

            int width = Parameters.ImageWidth;
            int height = Parameters.ImageHeight;
            OutputImage outputImage = new OutputImage(width, height);

            // border
            outputImage.drawRectangle(0, 0, width - 1, height - 1, Parameters.TouchEventVisualizerGridColor);

            // draw grid
            int numRows = Parameters.PalmGridNumRows;
            int numColumns = Parameters.PalmGridNumColumns;
            int widthPerColumn = width / numColumns;
            int heightPerRow = height / numRows;

            // find active blocks
            bool[,] activeBlocks = new bool[numRows, numColumns];
            if (numRows * numColumns > 1)
            {
                foreach (List<Vector2D> positions in activeTouches.Values)
                {
                    Vector2D lastPosition = positions.Last();
                    int activeRow = (int)Math.Min(lastPosition.Y * Parameters.PalmGridNumRows, numRows-1);
                    int activeCol = (int)Math.Min(lastPosition.X * Parameters.PalmGridNumColumns, numColumns-1);
                    activeBlocks[activeRow, activeCol] = true;
                }
            }

            // draw blocks
            for (int row = 0; row < numRows; row++)
            {
                for (int col = 0; col < numColumns; col++)
                {
                    if (activeBlocks[row, col])
                        outputImage.fillRectangle(col * widthPerColumn, row * heightPerRow, widthPerColumn, heightPerRow, Parameters.TouchEventVisualizerActiveBlockColor);

                    int x = (int)((col + 0.5f) * widthPerColumn) - 5;
                    int y = (int)((row + 0.5f) * heightPerRow) + 5;
                    outputImage.drawText(x, y, (1 + row * numColumns + col).ToString(), Parameters.TouchEventVisualizerTextColor);
                }
            }

            // draw grid
            for (int i = 0; i <= numColumns; i++)
                outputImage.drawLineSegment(new LineSegment2D(new Vector2D(i * widthPerColumn, 0), new Vector2D(i * widthPerColumn, height - 1)), Parameters.TouchEventVisualizerGridColor);
            for (int i = 0; i <= numRows; i++)
                outputImage.drawLineSegment(new LineSegment2D(new Vector2D(0, i * heightPerRow), new Vector2D(width - 1, i * heightPerRow)), Parameters.TouchEventVisualizerGridColor);

            // draw active touches
            foreach (List<Vector2D> positions in activeTouches.Values)
                outputImage.drawTouchGesture(positions);

            // draw old touches (fade out)
            foreach (int id in oldTouches.Keys) {
                List<Vector2D> positions = oldTouches[id];
                long lastUpdate = lastUpdates[id];
                float opacity = 1 - ((currentTime - lastUpdate) / (float)Parameters.TouchEventVisualizerFadeOutTime);
                outputImage.drawTouchGesture(positions, opacity);
            }

            return outputImage;
        }

        private void removeOldPositions(long breakTime)
        {
            long currentTime = timer.ElapsedMilliseconds;

            List<int> ids = new List<int>(lastUpdates.Keys);
            for (int i = ids.Count - 1; i >= 0; i--)
            {
                int id = ids[i];
                if (breakTime > lastUpdates[id])
                {
                    oldTouches.Remove(id);
                    lastUpdates.Remove(id);
                }
            }
        }
    }
}
