using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Surface;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
using System.Windows.Ink;
using System.Threading;
using System.IO;
using System.Xml;
using System.Reflection;
using System.Xml.Linq;

namespace GestureControl
{
    public class GestureControl : ContentControl
    {
        public double time;
        double start = -1;
        private double angle = 0;
        public static readonly DependencyProperty GestureProperty;
        Thread inProgess;
        List<SurfaceGesture> _Gestures = new List<SurfaceGesture>();

        /// <summary>
        /// List of pattern readed in the patterns.xml
        /// </summary>
        public List<SurfaceGesturePattern> Pattern = new List<SurfaceGesturePattern>();

        public String Gesture
        {
            get { return (String)GetValue(GestureControl.GestureProperty); }
            set { SetValue(GestureControl.GestureProperty, value); }
        }

        #region GestureEvent

        public delegate void GestureRoutedEventHandler(object sender, GestureRoutedEventArgs e);

        public static readonly RoutedEvent gestureEvent = EventManager.RegisterRoutedEvent("GestureEvent", RoutingStrategy.Bubble, typeof(GestureRoutedEventHandler), typeof(GestureControl));

        public event GestureRoutedEventHandler GestureEvent
        {
            add { AddHandler(gestureEvent, value); }
            remove { RemoveHandler(gestureEvent, value); }
        }

        public delegate void RaiseGestureEventCallBack(Gesture gesture);

        public void RaiseGestureEvent(Gesture gesture)
        {
            if (this.Dispatcher.CheckAccess())
            {
                try
                {
                    GestureRoutedEventArgs newEventArgs = new GestureRoutedEventArgs(GestureControl.gestureEvent, gesture);
                    RaiseEvent(newEventArgs);
                }
                catch (Exception e) { }
            }
            else
            {
                this.Dispatcher.BeginInvoke(new RaiseGestureEventCallBack(RaiseGestureEvent), gesture);
            }
        }

        #endregion

        static GestureControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(GestureControl), new FrameworkPropertyMetadata(typeof(GestureControl)));

            GestureControl.GestureProperty = DependencyProperty.Register("Gesture", typeof(String), typeof(GestureControl),
                    new FrameworkPropertyMetadata("None", new PropertyChangedCallback(OnGestureChanged)));
        }

        private static void OnGestureChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            //Debug.WriteLine("changed", "Gesture");
        }

        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            this.InitializeControl();
        }

        protected void InitializeControl()
        {
            base.AddHandler(SurfaceControl.PreviewContactDownEvent, new RoutedEventHandler(AreaDown));
            base.AddHandler(SurfaceInkCanvas.StrokeCollectedEvent, new RoutedEventHandler(AreaStrokeCollected));
        }

        // get angle from the first contact of the stroke
        protected void AreaDown(object source, RoutedEventArgs e)
        {
            if (start == -1)
                start = time;
            ContactEventArgs c = (ContactEventArgs)e;
            this.angle = c.Contact.GetOrientation(this) - 270;
        }

        protected void AreaStrokeCollected(object source, RoutedEventArgs e)
        {
            try
            {
                if (inProgess == null)
                {
                    inProgess = new Thread(RunCapture);
                    inProgess.Start();
                }
                else
                {
                    inProgess.Abort();
                    inProgess = null;
                    inProgess = new Thread(RunCapture);
                    inProgess.Start();
                }
                Debug.WriteLine("collected", "Stroke");
                System.Windows.Controls.InkCanvasStrokeCollectedEventArgs tmp = e as System.Windows.Controls.InkCanvasStrokeCollectedEventArgs;
                // Apply a rotation with the angle of the first contact
                Matrix rot = Matrix.Identity;
                rot.Rotate(-this.angle);
                Stroke s = tmp.Stroke;
                s.Transform(rot, true);
                // Get a list of point from a Bezier curve
                StylusPointCollection tmp2 = s.GetBezierStylusPoints();
                // Generate a list of SurfaceGesturePoint, just X and Y but can be improve
                List<SurfaceGesturePoint> pointList = new List<SurfaceGesturePoint>();
                foreach (StylusPoint p in s.StylusPoints)
                    pointList.Add(new SurfaceGesturePoint { X = p.X, Y = p.Y });
                // create the gesture analyser and set the list
                SurfaceGesture gesture = new SurfaceGesture(pointList, 1);
                // try to get a pattern from the list, if one, raise a event
                _Gestures.Add(gesture);
                SurfaceInkCanvas ink = e.OriginalSource as SurfaceInkCanvas;
                ink.Strokes.Clear();
            }
            catch { }
        }

        
        void RunCapture()
        {
            try
            {
                Thread.Sleep(1000);
                CommonPattern();
                string gesture = GetPattern(_Gestures);
                if (gesture != "None")
                {
                    this.RaiseGestureEvent(new Gesture{Start=start, End=time, Name=gesture});
                }
                start = -1;
                _Gestures.Clear();
                inProgess = null;
            }
            catch (Exception)
            {
                inProgess = null;
            }
        }

        #region Pattern
        /// <summary>
        /// return a String with the recognized pattern, "None" if no pattern
        /// </summary>
        public String GetPattern(List<SurfaceGesture> gestures)
        {            
            int found = 0;
            List<SurfaceGesture> tmp = new List<SurfaceGesture>();
            foreach (SurfaceGesturePattern p in Pattern)
            {
                if (p.Count == gestures.Count)
                {
                    int directions = 0;
                    for (directions = 0; directions < p.Count; directions++)
                    {
                        if (p[directions].Count == gestures[directions].Count)
                        {
                            int i;
                            for (i = 0; i < p[directions].Count; i++)
                            {
                                if (gestures[directions][i].Direction != p[directions][i].Direction)
                                {
                                    found = 0;
                                    i = p[directions].Count + 2;
                                }
                            }
                            if (i == p[directions].Count)
                                found++;
                        }
                        else
                        {
                            directions = p.Count + 1;
                            found = 0;
                        }
                    }
                }
                if (found == p.Count)
                    return p.Name;
            }
            return "None";
        }

        /// <summary>
        /// Load know patterns from the Resources/Patterns.xml file
        /// </summary>
        private void CommonPattern()
        {
            try
            {
                #region Load Patterns
                Pattern.Clear();
                System.IO.Stream file = Assembly.GetExecutingAssembly().GetManifestResourceStream("GestureControl.Resources.Patterns.xml");
                XmlDocument xml = new XmlDocument();
                xml.Load(file);
                XmlElement root = xml.DocumentElement;
                XmlNodeList nodes = root.SelectNodes("//Pattern");
                SurfaceGesturePattern item;
                foreach (XmlNode nod in nodes)
                {
                    string name = nod["Name"].InnerText;
                    if (nod.ChildNodes.Count < 2)
                        continue;
                    item = new SurfaceGesturePattern() { Name = name };
                    List<XmlNode> childs = new List<XmlNode>();
                    foreach (XmlNode elt in nod.ChildNodes)
                        if (elt.Name.Equals("Childs"))
                            foreach (XmlNode c in elt.ChildNodes)
                                childs.Add(c);

                    SurfaceGesturePatternItem p;
                    int j = 0;
                    foreach (XmlNode node in childs)
                    {
                        string childname = node["Name"].InnerText;
                        List<XmlNode> subNodes = new List<XmlNode>();
                        foreach (XmlNode elt in node.ChildNodes)
                            if (elt.Name.Equals("Directions"))
                                foreach (XmlNode c in elt.ChildNodes)
                                    subNodes.Add(c);
                        if (subNodes.Count == 0)
                            continue;

                        p = new SurfaceGesturePatternItem() { Name = childname };

                        foreach (XmlNode subNode in subNodes)
                        {
                            switch (subNode.InnerText)
                            {
                                case "Up":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.UP });
                                    break;
                                case "Down":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.DOWN });
                                    break;
                                case "Left":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.LEFT });
                                    break;
                                case "Right":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.RIGHT });
                                    break;
                                case "Downright":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.DOWNRIGHT });
                                    break;
                                case "Downleft":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.DOWNLEFT });
                                    break;
                                case "Upright":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.UPRIGHT });
                                    break;
                                case "Upleft":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.UPLEFT });
                                    break;
                                case "TAP":
                                    p.Add(new SurfaceGestureVector() { Direction = SurfaceGestureVectorDirection.TAP });
                                    break;
                                default:
                                    break;
                            }
                        }
                        item.Add(p);
                    }
                    this.Pattern.Add(item);
                }
                #endregion
            }
            catch
            {
                throw new Exception("Error loading Patterns.xml");
            }
        }

        #endregion
    }
}
