using QuikDawEditor.MiscClasses;
using QuikDawEditor.EDITING.PianoRollModel;
using QuikDawEditor.EditingClasses;
using QuikDawEditor.Undo;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using static QuikDawEditor.EDITING.MiscMethods;
using static QuikDawEditor.EDITING.StaticProperties;
using System.Collections.Generic;

namespace QuikDawEditor
{
    public partial class PianoRoll : UserControl, INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }

        public string RelRes() { ReleaseResources(); return "Resources released"; }

        public PianoRoll() { this.InitializeComponent(); }

        private void PRoll_Loaded(object sender, RoutedEventArgs e)
        {
            SnapToCB.SelectedIndex = 5;  //set initial to snap 16th notes 

        }

        private void ThisPianoRoll_VerticalScrollChange(double setVertical)   {   UDScrollViewer.ScrollToVerticalOffset(setVertical);  }

        public double SnapToMilliseconds { get { return NoteSnapTo == 0 ? -1 : 4 / NoteSnapTo * MillisecondsPerBeat; } }
        public double SnapToMillisecondsTriplet { get { return  2 / (3 / thisPianoRoll.tripletRes) * MillisecondsPerBeat; } }

        public double NoteSnapTo = 4;

        private void SnapToCB_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            string switchExpr = this.SnapToCB.SelectedValue.ToString();
            switch (switchExpr)
            {
                case "---": NoteSnapTo = 0; break;
                case "Whole note": NoteSnapTo = 1; break;
                case "1/2 note": NoteSnapTo = 2; break;
                case "1/4 note": NoteSnapTo = 4; break;
                case "1/8 note": NoteSnapTo = 8; break;
                case "1/16 note": NoteSnapTo = 16; break;
            }
        }


        private void FixMidiOverlaps()
        {
            //// Check heads, tails and completely containing another note
            //foreach (DisplayableMidiNoteOnEvent snote in SelectedNotes)
            //{
            //    for (int evno = DisplayableMidiNotes.Count - 1; evno >= 0; evno += -1)
            //    {
            //        CheckNote = DisplayableMidiNotes[evno];
            //        if (CheckNote.NoteNum == snote.NoteNum & !(CheckNote == snote))
            //        {
            //            // Note contains another note
            //            if (CheckNote.AbsTime >= snote.AbsTime & CheckNote.AbsTime + CheckNote.NoteLen < snote.AbsTime + snote.NoteLen)
            //            {
            //                MidiEvents.Remove(CheckNote.NEvent);
            //                this.NoteCanvas.Children.Remove(CheckNote.MyPath);
            //                DisplayableMidiNotes.Remove(CheckNote);
            //            }
            //            else
            //            {
            //                // Note overlaps tail of another
            //                if (CheckNote.AbsTime <= snote.AbsTime & CheckNote.AbsTime + CheckNote.NoteLen > snote.AbsTime)
            //                {
            //                    CheckNote.NoteLen = (int)(snote.AbsTime - CheckNote.AbsTime);
            //                    UpdatePath(ref CheckNote);
            //                }
            //                // Note overlaps head of another
            //                if (snote.AbsTime <= CheckNote.AbsTime & snote.AbsTime + snote.NoteLen > CheckNote.AbsTime)
            //                {
            //                    CheckNote.AbsTime = snote.AbsTime + snote.NoteLen;
            //                    CheckNote.NoteLen -= (int)snote.AbsTime + snote.NoteLen - (int)CheckNote.AbsTime;
            //                    UpdatePath(ref CheckNote);
            //                }
            //            }
            //        }
            //    }
            //}
        }

        private Cursor DefCursor = Cursors.Arrow;
       
   
        private void UDScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            UDPianoScrollViewer.ScrollToVerticalOffset(UDScrollViewer.VerticalOffset);
            ScaleScrollViewer.ScrollToHorizontalOffset(UDScrollViewer.HorizontalOffset);
            MidiEventsSV.ScrollToHorizontalOffset(UDScrollViewer.HorizontalOffset);

            if (thisPianoRoll != null)
                thisPianoRoll.VerticalScrollValue = UDScrollViewer.VerticalOffset;
        }

        private void UDPianoScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            e.Handled = true;
            UDScrollViewer.ScrollToVerticalOffset(UDScrollViewer.VerticalOffset + Math.Sign(e.Delta) * -25);

        }

        double currentSnapToMs { get { return thisPianoRoll.TripletOn ? SnapToMillisecondsTriplet : SnapToMilliseconds; } }

        private void PianoKeyPlayOff(int noteval)
        {
            PianoKey playingPianoKey = thisPianoRoll.PianoKeys.Where(pk => pk.NoteNo == noteval).FirstOrDefault();
            if (playingPianoKey != null)
            {
                playingPianoKey.IsPlaying = false;
            }
        }

        private void PianoKeyPlayOn(int noteval)
        {
            PianoKey toPlayPianoKey = thisPianoRoll.PianoKeys.Where(pk => pk.NoteNo == noteval).FirstOrDefault();
            if (toPlayPianoKey != null)
            {
                toPlayPianoKey.IsPlaying = true;
            }

        }

        private Clip myClip {get{return thisPianoRoll?.myClip; } }
        private PianoRollModel thisPianoRoll { get { return (PianoRollModel)this.DataContext; } }

        private void HideBut_Click(object sender, RoutedEventArgs e)
        {
            if (projPlayer.IsPianoRollPlaying) return;
            thisPianoRoll.IsVisible = false;
            editingProject.NeedsSaving = true;
        }

        private void ToggleTripletBut_Click(object sender, RoutedEventArgs e)
        {
            thisPianoRoll.TripletOn = !thisPianoRoll.TripletOn;
        }

     
        private void QuantizeMI_Click(object sender, RoutedEventArgs e)
        {
            Quantize_Execute();

        }

        double lastChangeValue = 0;

        private void MidiNoteTransposeKnob_ValueChanged(object sender, double Value)
        {
            int valchange = (int)(Value - lastChangeValue);

            foreach (QDMidiNote mnote in thisPianoRoll.MidiNotes)
                mnote.Note += valchange;

            RelativeTransposeValue += valchange;

            lastChangeValue = Value;

        }

        int RelativeTransposeValue;

        private void MidiNoteTransKnob_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            RelativeTransposeValue = 0;
        }

        private void MidiNoteTransKnob_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            lastChangeValue = 0;
            ((MidiNoteTransposeKnob)sender).Value = 0;
            undoActions.Add(new MidiTransposeUndo(myClip.UndoClipID, RelativeTransposeValue));
            myClip.UpdateMidiImageSource();

        }

        private void PianoRoll_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
        
            thisPianoRoll.VerticalScrollChange -= ThisPianoRoll_VerticalScrollChange;
            thisPianoRoll.VerticalScrollChange += ThisPianoRoll_VerticalScrollChange;

            thisPianoRoll.ScrollToNoteChange -= ThisPianoRoll_ScrollToNoteChange;
            thisPianoRoll.ScrollToNoteChange += ThisPianoRoll_ScrollToNoteChange;
        }

        private void ThisPianoRoll_ScrollToNoteChange(double scrollToNote)
        {
            int centeringNote = (int)(scrollToNote + UDScrollViewer.ActualHeight / PianoRollNoteHeight);
            thisPianoRoll.VerticalScrollValue = NotesIC.ActualHeight * (127D - centeringNote) / 127D;
        }

        private void PianoRoll_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (myClip != null)
                thisPianoRoll.DrawMeasureLines();

            if (this.IsVisible) this.Focus();

        }

        private void ShowMidiEventsMI_SubmenuOpened(object sender, RoutedEventArgs e)
        {
            MidiEventsLB.Items.Clear();
            foreach (QDMidiEvent qme in myClip.ClipMidiEvents)
                MidiEventsLB.Items.Add(qme.GetType().ToString() + " ::: " + qme.ClipRelTimeMs.ToString() + (qme is QDMidiNote ? " note: " + ((QDMidiNote)qme).Note.ToString() : "") + " val=" + qme.MainValue.ToString());
        }


        private void MidiEventViewCB_DropDownClosed(object sender, EventArgs e)
        {
            undoActions.Add(new MidiEventsSourceSelectUndo(thisPianoRoll.PreviousMidiEventIndex, thisPianoRoll));

            thisPianoRoll.PreviousMidiEventIndex = thisPianoRoll.SelectedMidiEventIndex;
           
        }

        private void MidiEventViewCB_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (MidiEventViewCB.SelectedIndex == -1) return;
            if (thisPianoRoll.SelectedMidiEventIndex == -1) return;
        
            TextBlock tblock = (TextBlock)MidiEventViewCB.SelectedItem;

            switch (tblock.Text) 
            {
                case "Velocities":
                    MidiEventsIC.ItemsSource = thisPianoRoll.MidiNotes;
                    break;

                case "Sustain":
                    MidiEventsIC.ItemsSource = myClip.ClipSustainEvents;
                    break;

                case "Pitch bend":
                    MidiEventsIC.ItemsSource = myClip.ClipPitchChangeEvents;
                    break;

                case "Modulation":
                    MidiEventsIC.ItemsSource = myClip.ClipModulationEvents;
                    break;
                }
        }


        private void Triplet4thRB_Checked(object sender, RoutedEventArgs e)
        {
            if (thisPianoRoll == null) return;
            thisPianoRoll.tripletRes = 1;
            thisPianoRoll.DrawTripletLines();
            TripletSelectCM.IsOpen = false;
        }

        private void Triplet8thRB_Checked(object sender, RoutedEventArgs e)
        {
            thisPianoRoll.tripletRes = 0.5;
            thisPianoRoll.DrawTripletLines();
            TripletSelectCM.IsOpen = false;
        }

        private void Triplet16thRB_Checked(object sender, RoutedEventArgs e)
        {
            thisPianoRoll.tripletRes = 0.25;
            thisPianoRoll.DrawTripletLines();
            TripletSelectCM.IsOpen = false;
        }

        private void PianoRoll_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (EventsGridSplitter.IsFocused) return;
            if (MidiEventViewCB.IsDropDownOpen) return;
            if (SnapToCB.IsDropDownOpen) return;
            if (ToggleTripletBut.IsFocused) return;
            if (PianoRollPlayBut.IsFocused) return;
            if (HideBut.IsFocused) return;

            this.Focus();
        }

        private void NotesIC_MouseLeave(object sender, MouseEventArgs e)
        {
            Cursor = Cursors.Arrow;
        }

        private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
           // MessageBox.Show("mouse down");
        }


        private void PlayMidiClipBut_Click(object sender, RoutedEventArgs e)
        {

            thisPianoRoll.myTrack = myClip.myTrack;
            thisPianoRoll.clipPlaying = !thisPianoRoll.clipPlaying;
            thisPianoRoll.myTrack.StopMidiNotes();
            
            switch (thisPianoRoll.clipPlaying)
            {
                case true:

                    foreach (Track t in editingProject.GetAllTracksInProject)
                        t.IsPianoRollMuted = true;
                    thisPianoRoll.myTrack.IsPianoRollMuted = false;

                    thisPianoRoll.keepProjectPlaying = projPlayer.IsProjectPlaying;
                    thisPianoRoll.keepPlayPos = projPlayer.CurrentPlayingPosMS;

                    projPlayer.IsProjectPlaying = true;

                    projPlayer.CurrentPlayingPosMS = myClip.ClipLeftMs + thisPianoRoll.CurrentMidiPlayingPosMS;

                    projPlayer.PlayControlsDPDisabled = true;

                    break;

                case false:
                    thisPianoRoll.StopPlay();
                    projPlayer.IsProjectPlaying = false;
                    projPlayer.PlayControlsDPDisabled = false;
                    break;
            }


        }

    }



}