//>>> _using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Windows;
//<<< _using
using System.Dynamic;
using Microsoft.Kinect;
using Framefield.Core.Inputs;

namespace Framefield.Core.IDb1952dc9_ad2b_4e86_bfb8_d0f1ac5b2caa
{
    public class Class_KinectBodyFor2 : OperatorPart.Function, Framefield.Core.OperatorPartTraits.IAsyncDependend
    {
        public bool AsyncChanged { get { return _asyncChanged; } }

        //>>> _outputids
        private enum OutputId
        {
            PositionX = 0,
            PositionY = 1,
            PositionZ = 2,
            RotationX = 3,
            RotationY = 4,
            RotationZ = 5,
            RotationW = 6,
            Tracked = 7
        }
        //<<< _outputids

        //>>> _inputids
        private enum InputId
        {
            Skeletons = 0,
            Player = 1,
            Joint = 2,
            InputRangeXMin = 3,
            InputRangeXMax = 4,
            InputRangeYMin = 5,
            InputRangeYMax = 6,
            InputRangeZMin = 7,
            InputRangeZMax = 8,
            FallbackPositionX = 9,
            FallbackPositionY = 10,
            FallbackPositionZ = 11,
            EnableMouse = 12
        }
        //<<< _inputids

        public override void Dispose()
        {
            Input.Mouse.MouseInputEvent -= HandleMouseInputEvent;
        }

        void HandleMouseInputEvent(object o, MouseInput.MouseEventArgs e)
        {
            _lastEventArgs = e;
            _asyncChanged = true;

            if (e.ButtonFlags.HasFlag(MouseInput.ButtonFlags.MiddleButtonDown))
            {
                _dragX = 0;
                _dragY = 0;
            }
            if (e.ButtonFlags.HasFlag(MouseInput.ButtonFlags.LeftButtonDown))
            {
                _pressed = true;
            }
            if (e.ButtonFlags.HasFlag(MouseInput.ButtonFlags.LeftButtonUp))
            {
                _pressed = false;
                _dragX = 0;
                _dragY = 0;
            }
            if (_pressed)
            {
                _dragX+= e.X;
                _dragY+= e.Y;
            }            
        }        

        public Class_KinectBodyFor2()
        {
            Input.Mouse.MouseInputEvent += HandleMouseInputEvent;   
        
            //player 1 (right side from kinect point of view: x < 0)
            //player 2 (left side from kinect point of view: x > 0)
            _playerCondition[0] = skeleton => 
                                  {
                                      return skeleton.TrackingState == SkeletonTrackingState.Tracked &&
                                             skeleton.Position.X <= 0.0f;
                                  };
            _playerCondition[1] = skeleton => 
                                  {
                                      return skeleton.TrackingState == SkeletonTrackingState.Tracked &&
                                             skeleton.Position.X > 0.0f;
                                  };
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            // params
            var Skeletons = inputs[(int)InputId.Skeletons].Eval(context).Dynamic; 
            var Player = inputs[(int)InputId.Player].Eval(context).Value;
            var Joint = (int) inputs[(int)InputId.Joint].Eval(context).Value;
            var InputRangeXMin = inputs[(int)InputId.InputRangeXMin].Eval(context).Value;
            var InputRangeXMax = inputs[(int)InputId.InputRangeXMax].Eval(context).Value;
            var InputRangeX = new Vector2(InputRangeXMin, InputRangeXMax);
            var InputRangeYMin = inputs[(int)InputId.InputRangeYMin].Eval(context).Value;
            var InputRangeYMax = inputs[(int)InputId.InputRangeYMax].Eval(context).Value;
            var InputRangeY = new Vector2(InputRangeYMin, InputRangeYMax);
            var InputRangeZMin = inputs[(int)InputId.InputRangeZMin].Eval(context).Value;
            var InputRangeZMax = inputs[(int)InputId.InputRangeZMax].Eval(context).Value;
            var InputRangeZ = new Vector2(InputRangeZMin, InputRangeZMax);
            var FallbackPositionX = inputs[(int)InputId.FallbackPositionX].Eval(context).Value;
            var FallbackPositionY = inputs[(int)InputId.FallbackPositionY].Eval(context).Value;
            var FallbackPositionZ = inputs[(int)InputId.FallbackPositionZ].Eval(context).Value;
            var FallbackPosition = new Vector3(FallbackPositionX, FallbackPositionY, FallbackPositionZ);
            var EnableMouse = inputs[(int)InputId.EnableMouse].Eval(context).Value;
            // params
            
            

            SkeletonPoint position = new SkeletonPoint {X = FallbackPosition.X, Y=FallbackPosition.Y, Z = FallbackPosition.Z};
            Microsoft.Kinect.Vector4 rotation = new Microsoft.Kinect.Vector4 {X = 0.0f, Y = 0.0f, Z = 0.0f, W = 1.0f};
            bool isTracked = false;

            var skeletonMembers = Skeletons as IDictionary<String, object>;
            if (skeletonMembers != null && skeletonMembers.ContainsKey("TrackedSkeletons"))
            {
                Skeleton skeleton = null;
                float minDistance = 99999;
                foreach (var s in Skeletons.TrackedSkeletons)
                {
                    if (_playerCondition[(int)Player](s))
                    {
                        if( s.Position.Z < minDistance)
                        {
                            skeleton = s;
                            minDistance = s.Position.Z;
                        }
                    }
                }
    
                if (skeleton != null)
                {

                    BoneOrientation orientation = skeleton.BoneOrientations[(JointType)Joint];
                    Joint startJoint = skeleton.Joints[orientation.StartJoint];
                    Joint endJoint = skeleton.Joints[orientation.EndJoint];
    
                    //uncomment the lines to only use position and orientation if at least on joint
                    //was tracked.
                    if (startJoint.TrackingState != JointTrackingState.NotTracked && endJoint.TrackingState != JointTrackingState.NotTracked)// &&
                        //(startJoint.TrackingState != JointTrackingState.Inferred || endJoint.TrackingState != JointTrackingState.Inferred))
                    {
                        position = endJoint.Position;
                        rotation = orientation.AbsoluteRotation.Quaternion;
                        isTracked = true;
                    }
                }
            }

            context.Value = 0;
            if (EnableMouse < 0.5)
            {
                _dragX = 0.0;
                _dragY = 0.0;
            }
            
            switch (outputIdx)
            {
                case (int)OutputId.PositionX: context.Value = ((position.X + (float)_dragX*0.001f) - InputRangeXMin) / (InputRangeXMax-InputRangeXMin); break;
                case (int)OutputId.PositionY: context.Value = ((position.Y - (float)_dragY*0.001f) - InputRangeYMin) / (InputRangeYMax-InputRangeYMin);  break;
                case (int)OutputId.PositionZ: context.Value = ( position.Z                         - InputRangeZMin) / (InputRangeZMax-InputRangeZMin); break;
                case (int)OutputId.RotationX: context.Value = rotation.X; break;
                case (int)OutputId.RotationY: context.Value = rotation.Y; break;
                case (int)OutputId.RotationZ: context.Value = rotation.Z; break;
                case (int)OutputId.RotationW: context.Value = rotation.W; break;
                case (int)OutputId.Tracked: context.Value = isTracked ? 1.0f : 0.0f; break;
            }

            _asyncChanged = false;
            return context;
        }

        bool _asyncChanged = false;
        MouseInput.MouseEventArgs _lastEventArgs;
        bool _pressed = false;
        double _dragX;
        double _dragY;
        Dictionary<int, System.Func<Skeleton, bool>> _playerCondition = new Dictionary<int, System.Func<Skeleton, bool>>();
    }
}

