//>>> _using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Windows;
//<<< _using

using Midi;


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

        //>>> _inputids
        private enum InputId
        {
            TeachTrigger = 0,
            Device = 1,
            Channel = 2,
            Control = 3,
            OutputRangeMin = 4,
            OutputRangeMax = 5,
            DefaultMidiValue = 6
        }
        //<<< _inputids 

        public Class_MidiInput()
        {
            RegisterAndTryOpenAtInputDevices();
        }

        public override void Dispose()
        {
            UnregisterAndTryCloseInputDevices();
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            //>>> _params
            var TeachTrigger = inputs[(int)InputId.TeachTrigger].Eval(context).Value;
            var Device = inputs[(int)InputId.Device].Eval(context).Text;
            var Channel = inputs[(int)InputId.Channel].Eval(context).Value;
            var Control = inputs[(int)InputId.Control].Eval(context).Value;
            var OutputRangeMin = inputs[(int)InputId.OutputRangeMin].Eval(context).Value;
            var OutputRangeMax = inputs[(int)InputId.OutputRangeMax].Eval(context).Value;
            var OutputRange = new Vector2(OutputRangeMin, OutputRangeMax);
            var DefaultMidiValue = inputs[(int)InputId.DefaultMidiValue].Eval(context).Value;
            //<<< _params

            _asyncChanged = false;
            _teachedDeviceName = Device;
            _teachedChannel = (int)Channel;
            _teachedControl = (int)Control;

            bool teachTriggerFlankUp = _oldTeachTrigger < 0.5f && TeachTrigger > 0.5f;
            _oldTeachTrigger = TeachTrigger;
            if (teachTriggerFlankUp)
            {
                _teachingActive = true;
                _lastMessage = null;
                _currentValue = 0;
                _valueHasBeenChanged = false;
            }

            if (_lastMessage != null)
            {
                lock (this)
                {
                    if (_teachingActive)
                    {
                        inputs[(int)InputId.Device].Func = Utilities.CreateValueFunction(new Text(_lastMessage.Device.Name));
                        _teachedDeviceName = _lastMessage.Device.Name;
                        inputs[(int)InputId.Channel].Func = Utilities.CreateValueFunction(new Float((float)_lastMessage.Channel));
                        _teachedChannel = (int)_lastMessage.Channel;
                        inputs[(int)InputId.Control].Func = Utilities.CreateValueFunction(new Float((float)_lastMessage.Control));
                        _teachedControl = (int)_lastMessage.Control;
                        _teachingActive = false;
                        _valueHasBeenChanged = false;
                    }

                    _currentValue = _lastMessage.Value;
                    _valueHasBeenChanged = true;
                }
            }
            
            var v = _valueHasBeenChanged ? _currentValue : DefaultMidiValue;
            //if( OutputRangeMin > OutputRangeMax) {
            //    var t = OutputRangeMin;
            //    OutputRangeMin = OutputRangeMax;
            //    OutputRangeMax = t;
            //}
            context.Value =    (v / 127) * (OutputRangeMax - OutputRangeMin) + OutputRangeMin;
            return context;
        }

        public void RegisterAndTryOpenAtInputDevices()
        {
            foreach (var device in InputDevice.InstalledDevices)
            {
                try
                {
                    if (!device.IsOpen)
                        device.Open();
                    if (!device.IsOpen)
                        Logger.Error("MidiInput: Failed to open midi input device {0}", device.Name);
                    if (!device.IsReceiving)
                        device.StartReceiving(null);
                    if (!device.IsReceiving)
                        Logger.Error("MidiInput: failed to start message receiving of midi input device {0}", device.Name);
 
                    device.ControlChange += this.ControlChange;
                }
                catch (Exception e)
                {
                    Logger.Error("MidiInput: failed to register and open input device: {0}: {1}", device.Name, e.Message);
                }
            }
            _currentValue = 0;
        }

        public void UnregisterAndTryCloseInputDevices()
        {
            _currentValue = 0;
            foreach (var device in InputDevice.InstalledDevices)
            {
                try
                {
                    device.ControlChange -= this.ControlChange;
                    if (!device.HasListeners())
                    {
                        Logger.Info("MidiInput: no listener active on device. closing device: {0}", device.Name);
                        device.RemoveAllEventHandlers();
                        device.StopReceiving();
                        device.Close();
                    }
                }
                catch (Exception e)
                {
                    Logger.Error("MidiInput: failed to unregister and close input device {0}: {1}", device.Name, e.Message);
                }
            }
        }

        public void ControlChange(ControlChangeMessage msg)
        {
            lock (this)
            {
                if (_teachingActive ||
                    ((String.IsNullOrEmpty(_teachedDeviceName) || msg.Device.Name == _teachedDeviceName) &&
                     (_teachedChannel < 0 || (int)msg.Channel == _teachedChannel) &&
                     (_teachedControl < 0 || (int)msg.Control == _teachedControl)))
                {
                    _lastMessage = msg;
                    _asyncChanged = true;
                }
            }
        }

        bool _valueHasBeenChanged = false;
        
        bool _asyncChanged = false;
        float _oldTeachTrigger = 0;
        bool _teachingActive = false;
        String _teachedDeviceName;
        int _teachedChannel = -1;
        int _teachedControl = -1;
        ControlChangeMessage _lastMessage;
        float _currentValue;
    }

}

