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

using NAudio.Midi;

using System.IO;
using System.Dynamic;
using System.Text.RegularExpressions;

namespace Framefield.Core.ID0eac4452_e37e_4a8d_81e6_328692e3f14e
{
    public class Class_MidiFile : OperatorPart.Function, Framefield.Core.OperatorPartTraits.ITimeAccessor
    {
        //>>> _inputids
        private enum InputId
        {
            FilePath = 0,
            TimeRate = 1,
            TimeOffset = 2
        }
        //<<< _inputids

        //>>> _outputids
        private enum OutputId
        {
            Note = 0,
            LastNoteNumber = 1,
            LastNoteVelocity = 2
        }
        //<<< _outputids


        struct MidiNote
        {
            public double StartTime;
            public double Duration;
            public int NoteNumber;
            public int Channel;
            public int Velocity;
            public int TrackId;
        }

        private NAudio.Midi.MidiFile _midiFile;
        private double _lastTime;
        private int _track;
        private float _timeRate;
        private List<MidiNote> _notes = new List<MidiNote>();
        
        private int _trackCount = 0;
        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx) {
            //>>> _params
            var FilePath = inputs[(int)InputId.FilePath].Eval(context).Text;
            var TimeRate = inputs[(int)InputId.TimeRate].Eval(context).Value;
            var TimeOffset = inputs[(int)InputId.TimeOffset].Eval(context).Value;
            //<<< _params


            if( FilePath != _filePath || TimeRate != _timeRate) {
            
                _filePath = FilePath;
                _timeRate = TimeRate;

                if (!File.Exists(_filePath))
                {
                    Logger.Info("Can't find midifile {0}", _filePath);
                    return context;
                }

                _midiFile = new MidiFile(_filePath);

                _trackCount = _midiFile.Tracks;

                var playedNoteEvents = new List<NoteOnEvent>();
    
                double ppqn = TimeRate;
    
                if ( _midiFile != null)
                {
                    _notes.Clear();
                    for (var trackIndex = 0; trackIndex < _trackCount; ++trackIndex)
                    {
                        var events = _midiFile.Events.GetTrackEvents(trackIndex);
                        foreach (var e in events)
                        {
                            if (e.CommandCode == MidiCommandCode.NoteOn) {
                                var noteOn = e as NoteOnEvent;
                                _notes.Add(new MidiNote()
                                {
                                    TrackId = trackIndex,
                                    StartTime = (double)e.AbsoluteTime / ppqn,
                                    Channel = e.Channel,
                                    NoteNumber = noteOn.NoteNumber,
                                    Duration = (double)noteOn.NoteLength,
                                    Velocity = noteOn.Velocity
                                });
                            }
                            else if (e.CommandCode == MidiCommandCode.NoteOff) {
                            }
                            else if(e.CommandCode == MidiCommandCode.ControlChange) {
                                var t = e.AbsoluteTime;
                                var cce = e as ControlChangeEvent;
                            }
                            else if(e.CommandCode == MidiCommandCode.MetaEvent)
                            {
                                var timeEvent = e as TimeSignatureEvent;
                                if (timeEvent != null)
                                {
                                    //Logger.Info("Time Event: {0}", timeEvent);
                                    continue;
                                }
    
                                var setTempoEvent = e as TempoEvent;
                                if (setTempoEvent != null)
                                {
                                    ppqn = setTempoEvent.Tempo * 4;
                                    continue;
                                }
    
                                //Logger.Info("Command code: {0}", e);                                            
                            }
                            else {
                                //Logger.Info("more: {0}", e);
                                continue;
                            }
                        }
                    }
                }
    
                if (_notes.Count <= 0) {
                    return context;
                }
                _notes.Sort((s1, s2) => s1.StartTime.CompareTo(s2.StartTime));        
            }
            
            if( context.Time != _lastTime) {
                _playedNotes.Clear();
                // Accumulate all NoteOnEvents between last time and current time
                foreach( var note in _notes) {
                    if (note.StartTime > _lastTime && note.StartTime <= context.Time) {
                        _playedNotes.Add(note);
                        _lastNote = note;
                    }
                }
            }

            switch (outputIdx)
            {
                case (int) OutputId.Note:
                    context.Value = (_playedNotes.Count >=1) ? 1 :0;
                    break;
                    
                case  (int)OutputId.LastNoteNumber:
                    context.Value = _lastNote.NoteNumber;  
                    break;
                    
                case  (int)OutputId.LastNoteVelocity:
                    context.Value = _lastNote.Velocity;  
                    break;
            }
            //context.Value = _list.Count;
            _lastTime = context.Time;
            return context;
        }
        
        private MidiNote _lastNote;
        private string _filePath;
        private string _matchString;
        //private List<string> _list = new List<string>();
        private List<MidiNote> _playedNotes = new List<MidiNote>();
        
    }
}

