using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Controls.Primitives;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
using System.Windows.Threading;

using FingersDance.Data;
using FingersDance.ViewModels;
using FingersDance.Views;
using FingersDance.Control.Close;
using GestureControl;

namespace FingersDance.Control.TimeLine
{
	public partial class UserControlTimeLine
    {

        #region Variables
        private DispatcherTimer timer;
        private bool isDragging = false;
        private bool finishedDragging = false;
        #endregion
        
        public event EventHandler DragStarted;
        public event EventHandler DragCompleted;
        public event EventHandler TimerTick;
        public event EventHandler AnnotationAdded;
        public event EventHandler AnnotationRemoved;
        public event EventHandler<NewGestureRecognizedEventArg> NewGestureRecognized;
        //SAR
        public event EventHandler OnSuccessAnnotation;
        //ENDSAR

        private double totalmilliseconds;
        
        private float AnnotWidth = 1600 + 15; // The surfaceListBox adds 15 pixels between each item
        private Cutting cut;
        private Boolean AnnotWaiting = false;
        private float AnnotTcBegin;
        private Color CurrentColor;

        private AnnotationViewModel canceledAnnotationVM;

        #region Properties

        public SurfaceSlider Slider
        {
            get
            {
                return slider;
            }
            set
            {
                slider = value;
            }
        }
        
        public bool IsDragging
        {
            get
            {
                return isDragging;
            }
            set
            {
                isDragging = value;
            }
        }

        public bool FinishedDragging
        {
            get
            {
                return finishedDragging;
            }
            set
            {
                finishedDragging = value;
            }
        }

        public DispatcherTimer Timer
        {
            get
            {
                return timer;
            }
            set
            {
                timer = value;
            }
        }
        #endregion


        #region Constructors

        public UserControlTimeLine()
		{
			this.InitializeComponent();
            
			// Insert code required on object creation below this point.

        }
        #endregion

        public void initslider(double totalmillisecondsPar)
        {
            totalmilliseconds = totalmillisecondsPar;
            slider.Maximum = totalmilliseconds;
            // When the timeline is resized, we catch the resize event to define TimelineView's good scale
            this.SizeChanged += new SizeChangedEventHandler(UserControlTimeLine_SizeChanged);
            
            tv.listview.PreviewContactDown += listview_PreviewContactDown;
            SurfaceDragDrop.AddDragCanceledHandler(tv.listview, onDragCanceled);

            UserControlTimeLine_SizeChanged(null, null);

            slider_ContactTapGesture(this, null);

        }

        void UserControlTimeLine_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            // When scaleX = 1, 1 second = 1 pixel. To calculate the new scale, we take the real width in account.
            tv.ScaleX = (this.ActualWidth-30) / (totalmilliseconds / 1000);
            //tv.Width = this.ActualWidth - 30;
            
        }

        #region Timer
        public void initTimer(Cutting cutPar)
        {
            timer = new DispatcherTimer();
            timer.Interval = new TimeSpan(0, 0, 0, 0, 100);
            timer.Tick += new EventHandler(timer_Tick);

            if(cutPar!=null)
                CurrentColor = cutPar.Color;

            // DATA BINDING from the cutting sent in parameter (initialised before by the userPanel with the global project)
            cut = cutPar;
            //cut.AnnotList = new List<Annotation>();
            //cut.AnnotList.Add(new Annotation("s_" + System.Guid.NewGuid(), 0, 10, "Axe Cam 1", CurrentColor));
            //cut.AnnotList.Add(new Annotation("s_" + System.Guid.NewGuid(), 100 - (1 * AnnotWidth), 70, "Mvt Cam 2"));
            //cut.AnnotList.Add(new Annotation("s_" + System.Guid.NewGuid(), 200 - (2 * AnnotWidth), 50, "Saut 3"));
            //cut.AnnotList.Add(new Annotation("s_" + System.Guid.NewGuid(), 100 - (3 * AnnotWidth), 20, "Saut 4"));
            //cut.AnnotList.Add(new Annotation("s_" + System.Guid.NewGuid(), 120 - (4 * AnnotWidth), 50, "Saut 5"));

            if(cut!=null)
                tv.DataContext = new CuttingViewModel(cut, AnnotWidth);

        }

        public void timerStart()
        {
            if (timer != null)
                timer.Start();
        }
        void timer_Tick(object sender, EventArgs e)
        {
            OnTimerTick();
            if (!isDragging)
            {
                //slider.Value = media.Position.TotalMilliseconds;
            }
            if (finishedDragging)
            {
                //int SliderValue = (int)slider.Value;
                //TimeSpan ts = new TimeSpan(0, 0, 0, 0, SliderValue);
               // media.Position = ts;
               // media.Play();
               // isDragging = false;
                //finishedDragging = false;
            }
        }
        protected virtual void OnTimerTick()
        {
            if (TimerTick != null)
                TimerTick(this, new EventArgs());
        }

        #endregion

        #region SliderDrag
        private void sliderPosition_DragStarted(object sender, DragStartedEventArgs e)
        {
            isDragging = true;
            OnDragStarted();
           // media.Pause();
        }
        protected virtual void OnDragStarted()
        {
            if (DragStarted != null)
                DragStarted(this, new EventArgs());
        }

        private void sliderPosition_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            finishedDragging = true;
            OnDragCompleted();
        }
        protected virtual void OnDragCompleted()
        {
            if (DragCompleted != null)
                DragCompleted(this, new EventArgs());
        }

        private void slider_ContactTapGesture(object sender, Microsoft.Surface.Presentation.ContactEventArgs e)
        {
            //startOrEndAnnotation();
        }
        #endregion

        public void thumbRotation(bool isplaying)
        { 
            //Templateslider.Style.Setters["Template"]
            if(isplaying)
                slider.Style = FindResource("FingersDance.Control.Slider") as Style;
            else
                slider.Style = FindResource("SurfaceSliderStylePause") as Style;
        }

        public Boolean isAnnotationAccepted(AnnotationViewModel avm)
        {
            Boolean annotOk = true;
            // We check if the annotation's begin and end timecodes allow a new annotation creation
            if (cut != null && AnnotWaiting == false)
            {
                foreach (Annotation a in cut.AnnotList)
                {
                    //Console.WriteLine("a.TcBegin = " + a.TcBegin + ", avm.TcBegin = " + avm.TcBegin + ", tcOut = " + (a.TcBegin + a.Dur));
                    // Check begin TC
                    if (a.TcBegin <= avm.TcBegin && avm.TcBegin <= (a.TcBegin + a.Dur))
                    {
                        annotOk = false;
                    }
                    // Check end tc and if the new annotation will not cover any other
                    float endTC = avm.TcBegin + avm.Dur;
                    //Console.WriteLine("a.TcBegin = " + a.TcBegin + ", tcOut = " + (a.TcBegin + a.Dur) + ", AnnotTcBegin = " + AnnotTcBegin + ", currentTC = " + currentTC);
                    if (a.TcBegin <= endTC && endTC <= (a.TcBegin + a.Dur) || (avm.TcBegin < a.TcBegin && (a.TcBegin + a.Dur) < endTC))
                    {
                        annotOk = false;
                    }
                }
            }
            else
            {
                annotOk = false;
            }
            return annotOk;

        }

        public void addAnnotation(AnnotationViewModel avm)
        {
            //Console.WriteLine("addAnnotation = " + avm.TcBegin + ", " + avm.Dur + ", " + avm.GestureType);
            Boolean annotOk = true;
            // We check if the annotation's begin and end timecodes allow a new annotation creation
            if (cut != null && AnnotWaiting == false)
            {
                foreach (Annotation a in cut.AnnotList)
                {
                    //Console.WriteLine("a.TcBegin = " + a.TcBegin + ", avm.TcBegin = " + avm.TcBegin + ", tcOut = " + (a.TcBegin + a.Dur));
                    // Check begin TC
                    if (a.TcBegin <= avm.TcBegin && avm.TcBegin <= (a.TcBegin + a.Dur))
                    {
                        annotOk = false;
                    }
                    // Check end tc and if the new annotation will not cover any other
                    float endTC = avm.TcBegin + avm.Dur;
                    //Console.WriteLine("a.TcBegin = " + a.TcBegin + ", tcOut = " + (a.TcBegin + a.Dur) + ", AnnotTcBegin = " + AnnotTcBegin + ", currentTC = " + currentTC);
                    if (a.TcBegin <= endTC && endTC <= (a.TcBegin + a.Dur) || (avm.TcBegin < a.TcBegin && (a.TcBegin + a.Dur) < endTC))
                    {
                        annotOk = false;
                    }
                }
                // If everything's fine, we create the new one
                if (annotOk == true)
                {
                    Annotation annotation = new Annotation("s_" + System.Guid.NewGuid(), avm.TcBegin, avm.Dur, avm.GestureType, avm.Color);
                    cut.AnnotList.Add(annotation);
                    tv.DataContext = new CuttingViewModel(cut, AnnotWidth, tv.ScaleX);
                    if (AnnotationAdded != null)
                        AnnotationAdded(annotation, null);
                    AnnotWaiting = false;
                }

            }
            //Raise Event to display Annotation in all Openned UserPanels
            if (annotOk)
                OnSuccessAnnotation(this, new EventArgs());

        }

        public void addAnnotation(float start, float dur, List<String> gestureType)
        {
            addAnnotation(new AnnotationViewModel(new Annotation("temp", start, dur, gestureType, CurrentColor), 0));
        }

        public void startOrEndAnnotation(String gestureType)
        {
            Boolean annotOk = true;
            // We open a new annotation
            if (cut != null && AnnotWaiting == false)
            {
                AnnotTcBegin = (float)slider.Value / 1000;

                // First we check if the new annotation will not cover any
                foreach (Annotation a in cut.AnnotList)
                {
                    //Console.WriteLine("a.TcBegin = " + a.TcBegin + ", AnnotTcBegin = " + AnnotTcBegin + ", tcOut = " + (a.TcBegin + a.Dur));
                    if (a.TcBegin <= AnnotTcBegin && AnnotTcBegin <= (a.TcBegin + a.Dur))
                    {
                        annotOk = false;
                    }
                }
                // if not, we mark the beginning
                if (annotOk == true)
                {
                    List<String> ls = new List<String>();
                    ls.Add(gestureType);
                    Annotation annotation = new Annotation("s_" + System.Guid.NewGuid(), AnnotTcBegin, 0, ls, CurrentColor);
                    cut.AnnotList.Add(annotation);
                    tv.DataContext = new CuttingViewModel(cut, AnnotWidth, tv.ScaleX);
                    AnnotWaiting = true;
                }
            }
            // We close the current opened annotation...
            else if (cut != null && AnnotWaiting == true && (((float)slider.Value/1000)>AnnotTcBegin))
            {
                // ... by setting setting the good beginning and the good duration
                float currentTC = (float)slider.Value / 1000;
                float currentDuration = currentTC - AnnotTcBegin;

                // First we check if the new annotation will not cover any (AnnotTcBegin was already checked)
                foreach (Annotation a in cut.AnnotList)
                {
                    //Console.WriteLine("a.TcBegin = " + a.TcBegin + ", tcOut = " + (a.TcBegin + a.Dur) + ", AnnotTcBegin = " + AnnotTcBegin + ", currentTC = " + currentTC);
                    if (a.TcBegin <= currentTC && currentTC <= (a.TcBegin + a.Dur) || (AnnotTcBegin < a.TcBegin && (a.TcBegin + a.Dur) < currentTC))
                    {
                        annotOk = false;
                    }
                }

                if (annotOk == true)
                {
                    cut.AnnotList.RemoveAt(cut.AnnotList.Count - 1);
                    List<String> ls = new List<String>();
                    ls.Add(gestureType);
                    Annotation annotation = new Annotation("s_" + System.Guid.NewGuid(), AnnotTcBegin, currentDuration, ls, CurrentColor);
                    cut.AnnotList.Add(annotation);
                    //Console.WriteLine("currentTimecode = " + AnnotTcBegin + ", curDur = " + currentDuration + ", nb = " + cut.AnnotList.Count + ", res = " + (AnnotTcBegin - (cut.AnnotList.Count * AnnotWidth)));
                    tv.DataContext = new CuttingViewModel(cut, AnnotWidth, tv.ScaleX);
                    AnnotWaiting = false;
                    if (AnnotationAdded != null)
                        AnnotationAdded(annotation, null);
                }
                //Raise Event to display Annotation in all Openned UserPanels
                if (annotOk)
                    OnSuccessAnnotation(this, new EventArgs());
            }
        }

        private void listview_PreviewContactDown(object sender, Microsoft.Surface.Presentation.ContactEventArgs e)
        {
            FrameworkElement findSource = e.OriginalSource as FrameworkElement;
            SurfaceListBoxItem draggedElement = null;

            // Find the touched SurfaceListBoxItem object.
            while (draggedElement == null && findSource != null)
            {
                if ((draggedElement = findSource as SurfaceListBoxItem) == null)
                {
                    findSource = VisualTreeHelper.GetParent(findSource) as FrameworkElement;
                }
            }

            if (draggedElement == null)
            {
                return;
            }

            // Create the cursor visual.
            ContentControl cursorVisual = new ContentControl()
            {
                Content = new TimelineAnnotationView()
                {
                    DataContext = findSource.DataContext
                }
            };

            // We apply the current scale to the dragged annotation
            ((TimelineAnnotationView)cursorVisual.Content).RenderTransform = new TransformGroup();
            ((TransformGroup)((TimelineAnnotationView)cursorVisual.Content).RenderTransform).Children.Add(tv.listview.RenderTransform);
            // We apply a translate transform to place correctly the dragged annotation
            ((TransformGroup)((TimelineAnnotationView)cursorVisual.Content).RenderTransform).Children.Add(new TranslateTransform((AnnotWidth / 2) * (1 - ((ScaleTransform)tv.listview.RenderTransform).ScaleX), 0));
            
            // Add a handler. This will enable the application to change the visual cues. (NOT USEFUL IN FINGERSDANCE CASE)
            //SurfaceDragDrop.AddTargetChangedHandler(cursorVisual, OnTargetChanged);

            // Create a list of input devices. Add the contacts that
            // are currently captured within the dragged element and
            // the current contact (if it isn't already in the list).
            List<InputDevice> devices = new List<InputDevice>();
            devices.Add(e.Contact);
            foreach (Contact contact in draggedElement.ContactsCapturedWithin)
            {
                if (contact != e.Contact)
                {
                    devices.Add(contact);
                }
            }

            // Get the drag source object
            ItemsControl dragSource = ItemsControl.ItemsControlFromItemContainer(draggedElement);

            bool startDragOkay =
                SurfaceDragDrop.BeginDragDrop(
                  dragSource,                 // The SurfaceListBox object that the cursor is dragged out from.
                  draggedElement,             // The SurfaceListBoxItem object that is dragged from the drag source.
                  cursorVisual,               // The visual element of the cursor.
                  draggedElement.DataContext, // The data associated with the cursor.
                  devices,                    // The input devices that start dragging the cursor.
                  DragDropEffects.Move);      // The allowed drag-and-drop effects of the operation.

            // If the drag began successfully, set e.Handled to true. 
            // Otherwise SurfaceListBoxItem captures the contact 
            // and causes the drag operation to fail.
            e.Handled = startDragOkay;
        }

        private void onDragCanceled(Object sender, SurfaceDragDropEventArgs e)
        {
            // We check if the annotation is well one of the current annotations
            if (cut != null)
            {
                // e.Cursor.Visual is the ContentControl, so its Content is the dragged TimelineAnnotationView
                canceledAnnotationVM = (AnnotationViewModel)((TimelineAnnotationView)((ContentControl)e.Cursor.Visual).Content).DataContext;
                confirmCancelPopup.Visibility = Visibility.Visible;
            }
        }

        private void confirmCancelPopup_ConfirmYesOrNo(object sender, ConfirmEventArgs e)
        {
            // We check if the annotation is well one of the current annotations
            if (cut != null && canceledAnnotationVM != null && e.Confirmed == true)
            {
                foreach (Annotation a in cut.AnnotList)
                {
                    // Check begin TcBegin, duration and gesture type
                    if (a.TcBegin == canceledAnnotationVM.TcBegin && a.Dur == canceledAnnotationVM.Dur && a.GestureType == canceledAnnotationVM.GestureType)
                    {
                        // We found the good annotation so we can remove it
                        cut.AnnotList.Remove(a);
                        // We redisplay the timeline
                        //CuttingVM.setListFromAnnotations(cut.AnnotList, AnnotWidth, tv.ScaleX);
                        tv.DataContext = null;
                        tv.DataContext = new CuttingViewModel(cut, AnnotWidth, tv.ScaleX);
                        // We close the popup and return as soon as we removed the annotation to avoid an error on cut.AnnotList, which is just modified
                        confirmCancelPopup.Visibility = Visibility.Hidden;
                        canceledAnnotationVM = null;
                        if (AnnotationRemoved != null)
                            AnnotationRemoved(a, null);
                        return;
                    }
                }
            }
            confirmCancelPopup.Visibility = Visibility.Hidden;
            canceledAnnotationVM = null;
        }

        //
        // Is search mode, function of the search gesture, we have to be able to update frequently the current list of annotation
        //
        public void updateAnnotationList(List<Annotation> la)
        {
            tv.DataContext = new CuttingViewModel(new Cutting("temp", "search", la), AnnotWidth, tv.ScaleX);
        }

        //
        // Is search mode, we need the timeline to send the NewGestureEvent
        //
        public void sendNewGesture(String gestureName)
        {
            NewGestureRecognized(this, new NewGestureRecognizedEventArg(gestureName));
        }
    }


    /// <summary>
    /// AnnotationOrSearchEventArg
    /// </summary>
    public class NewGestureRecognizedEventArg : EventArgs
    {
        public String GestureName
        {
            get;
            set;
        }

        public NewGestureRecognizedEventArg(String gestureName)
        {

            GestureName = gestureName;

        }
    }
}