using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using DXGfxLib;

namespace Editor
{
    public class Marker
    {
        public Point position;
        public DXGfxLib.Event evt;
    }

    public partial class TimeLineView : UserControl
    {
        // Create font and brush.
        Font drawFont = new Font("Arial", 6);
        SolidBrush drawBrush = new SolidBrush(Color.Black);
        Pen drawPen = new Pen(Color.Black);
        Pen boldPen = new Pen(Color.Black, 2);

        private double startTime = 0;
        private double endTime = 30;

        private int middle;

        private List<Marker> markers;

        private Marker selectedMarker = null;

        private Mediator mediator = null;

        public TimeLineView(Mediator mediator)
        {
            InitializeComponent();

            markers = new List<Marker>();

            this.mediator = mediator;

            mediator.OnSelect += new Select(mediator_OnSelect);
        }

        void mediator_OnSelect(string name, object obj)
        {
            TimeLine timeLine = null;

            SceneObject sObj = obj as SceneObject;
            DXGfxLib.Scene scene = obj as DXGfxLib.Scene;

            if(sObj!=null)
            {
                timeLine = sObj.GetTimeLine();
            }
            else if(scene!=null)
            {
                timeLine = scene.GetTimeLine();
            }
            UpdateTimeLineView(timeLine);

            Refresh();
        }

        private void TimeLineView_Paint(object sender, PaintEventArgs e)
        {
            middle = (int)Math.Floor(0.5f * e.Graphics.VisibleClipBounds.Height);
            UpdateMarkerPositions();

            MakeAxesAndGrid(e.Graphics);
            DisplayMarkers(e.Graphics);
        }

        public void SetStartAndEnd(double startTime, double endTime)
        {
            this.startTime = startTime;
            this.endTime = endTime;
        }

        public void GetStartAndEnd(out double startTime, out double endTime)
        {
            startTime = this.startTime;
            endTime = this.endTime;
        }

        private void MakeAxesAndGrid(Graphics g)
        {
            Point leftMid = new Point(0, (int)Math.Ceiling(0.5f * g.VisibleClipBounds.Height));
            Point rightMid = new Point((int)Math.Ceiling(g.VisibleClipBounds.Width), (int)Math.Ceiling(0.5f * g.VisibleClipBounds.Height));
            g.DrawLine(new Pen(Color.Black), leftMid, rightMid);

            MakeVerticalGraduations(g, startTime, endTime, 5);
        }

        private void MakeVerticalGraduations(Graphics g, double startTime, double endTime, int numberOfGrads)
        {
            Point high = new Point(0, 20);
            Point low = new Point(0, Height-10);

            int pace = (int)Math.Floor((g.VisibleClipBounds.Width - 10) / numberOfGrads);

            double timePace = (endTime - startTime) / numberOfGrads;
            double timeCurrent = startTime;

            for (int i = 0; i < numberOfGrads; i++)
            {
                high.X = i*pace + 10;
                low.X = high.X;

                g.DrawLine(drawPen, high, low);
                g.DrawString(timeCurrent.ToString(), drawFont, drawBrush, new PointF(low.X , low.Y));

                timeCurrent += timePace;
            }
        }

        private void DisplayMarkers(Graphics g)
        {
            foreach (Marker marker in markers)
            {


                Point[] points = new Point[3];
                points[0] = new Point(marker.position.X, middle + 2);
                points[1] = new Point(marker.position.X - 6, middle + 8);
                points[2] = new Point(marker.position.X + 6, middle + 8);

                Pen pen = null;
                if (marker == selectedMarker)
                {
                    pen = boldPen;
                }
                else
                {
                    pen = drawPen;
                }
                g.DrawPolygon(pen, points);

                g.DrawString(marker.evt.name, drawFont, drawBrush, new PointF(marker.position.X - 5, middle + 10));
            }
        }

        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            // Check if we are double clicking an existing triangle, if we are edit that one instead of creating a new one!

            Point position = new Point(e.X, e.Y);

            Marker testMarker = Select(position, 6);

            if (testMarker != null)
            {
                EditEvent(testMarker);
            }
            else
            {
                CreateEvent(position);
            }
            this.Refresh();
        }

        protected void EditEvent(Marker marker)
        {
            NewEventDialog dlg = new NewEventDialog();

            dlg.name = marker.evt.name;
            dlg.type = marker.evt.type;
            dlg.value = marker.evt.value;

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                marker.evt.name = dlg.name;
                marker.evt.type = dlg.type;
                marker.evt.value = dlg.value;

                selectedMarker = marker;
            }
            else
            {
                return;
            }

            if (mediator.selectedScriptableObj != null)
            {
                UpdateScriptableObjectTimeLine(mediator.selectedScriptableObj);
            }
            else
            {
                throw new NullReferenceException("This is bad, I don't expect to end up here!");
            }
        }

        protected void CreateEvent(Point p)
        {
            NewEventDialog dlg = new NewEventDialog();

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                Marker marker = new Marker();

                marker.position = p;

                DXGfxLib.Event evt = new DXGfxLib.Event();
                evt.name = dlg.name;
                evt.timeOffset = (float)(p.X * ((endTime - startTime) / this.Width));
                evt.type = dlg.type;
                evt.value = dlg.value;

                marker.evt = evt;

                markers.Add(marker);
                selectedMarker = marker;
            }
            else
            {
                return;
            }

            if (mediator.selectedScriptableObj != null)
            {
                UpdateScriptableObjectTimeLine(mediator.selectedScriptableObj);
            }
            else
            {
                throw new NullReferenceException("This is bad, I don't expect to end up here!");
            }
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            Point click = new Point(e.X, e.Y);
            Marker tmpMarker = Select(click, 15);

            if (tmpMarker == null)
            {
                return;
            }

            selectedMarker = tmpMarker;

            this.Refresh();
        }

        public Marker Select(Point p, double maxDist)
        {
            Marker res = null;

            double dist = maxDist;
            double tmpDist = maxDist;

            foreach (Marker marker in markers)
            {
                tmpDist = Distance(marker.position, p);
                if (tmpDist < dist)
                {
                    dist = tmpDist;
                    res = marker;
                }
            }

            return res;
        }

        public Marker[] Pick(Point p)
        {
            List<Marker> res = new List<Marker>();

            foreach (Marker marker in markers)
            {
                if (Distance(marker.position, p) < 6)
                {
                    res.Add(marker);
                }
            }

            return res.ToArray();
        }

        public double Distance(Point p0, Point p1)
        {
            return Math.Sqrt(Math.Pow(p0.X - p1.X, 2) + Math.Pow(p0.Y - p1.Y, 2));
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                if (selectedMarker != null)
                {
                    SuppresMarker(selectedMarker);
                    selectedMarker = null;
                    Refresh();
                }
            }
        }

        public void SuppresMarker(Marker marker)
        {
            bool success = markers.Remove(marker);
        }

        public void UpdateScriptableObjectTimeLine(IAmScriptable scriptableObj)
        {
            DXGfxLib.TimeLine timeLine = scriptableObj.GetTimeLine();

            timeLine.Clear();

            foreach (Marker marker in markers)
            {
                timeLine.events.Add(marker.evt);
            }
        }

        public void UpdateTimeLineView(TimeLine timeLine)
        {
            markers.Clear();

            if ((timeLine != null) && (timeLine.events.Count > 0))
            {
                foreach (Event evt in timeLine.events)
                {
                    Marker tmpMarker = new Marker();
                    tmpMarker.position.X = PosFromTime(evt.timeOffset);

                    tmpMarker.evt = evt;

                    markers.Add(tmpMarker);
                }
            }
            Refresh();
        }

        public void UpdateMarkerPositions()
        {
            foreach(Marker marker in markers)
            {
                marker.position.X = PosFromTime(marker.evt.timeOffset);
                marker.position.Y = middle + 2;
            }
        }

        public int PosFromTime(float time)
        {
            return (int)Math.Floor((time - startTime) * (Width / (endTime - startTime)));
        }
    }
}
