using K11_CNC.Command;
using K11_CNC.Model;
using K11_CNC.ViewModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Xml.Serialization;

namespace K11_CNC.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        #region Ereignisse
        public event CancelEventHandler ConfirmDeleting;

        public void OnConfirmDeleting(CancelEventArgs e)
        {
            if (ConfirmDeleting != null)
                ConfirmDeleting(this, e);
            else
                throw new Exception("Das Löschen muss bestätigt werden.");
        }

        #endregion
        #region "Private Felder"
        private ObservableCollection<PositionViewModel> _positionsList;
        private string _actualPosition;
        private ListCollectionView _positionsView;
        private CommandBinding _newCommandBinding;
        private CommandBinding _deleteCommandBinding;
        private CommandBinding _saveCommandBinding;
        private CommandBinding _undoCommandBinding;
        private CommandBinding _runCommandBinding;
        private string _positionDetails;
        private bool _ListChanged;
        private bool _setFocus;
        private static string[] _sortCriteria = { "id", "XPos", "YPos" };
        private string _sortByProperty = _sortCriteria[0];
        public Motor _Motor;

        #endregion

        #region "Öffentliche Eigenschaften"
        public string AbsXPos { get { return _Motor.PosX; } }
        public string AbsYPos { get { return _Motor.PosY; } }

        public string[] SortCriteria
        {
            get { return _sortCriteria; }
        }

        public string SortByProperty
        {
            get { return _sortByProperty; }
            set
            {
                if (value != _sortByProperty)
                {
                    SetProperty(ref _sortByProperty, value);
                    UpdateSorting();
                }
            }
        }
        public bool SetFocus
        {
            get { return _setFocus; }
            set { SetProperty<bool>(ref _setFocus, value); }
        }
        public bool ListChanged
        {
            get { return _ListChanged;  }
            set
            {
                if (_ListChanged != value)
                    SetProperty(ref _ListChanged, value);
            }
        }
        public ListCollectionView PositionsView
        {
            get { return _positionsView; }
        }
        public string ActualPosition
        {
            get { return _actualPosition; }
            private set
            {
                SetProperty<string>(ref _actualPosition, value);
            }
        }
        public ICommand FirstCommand { get; private set; }
        public ICommand NextCommand { get; private set; }
        public ICommand PreviousCommand { get; private set; }
        public ICommand LastCommand { get; private set; }
        public ICommand MouseEnterCommand { get; private set; }
        public ICommand MouseLeaveCommand { get; private set; }
        public ICommand RunCommand { get; private set; }

        public CommandBinding NewCommandBinding
        {
            get { return _newCommandBinding; }
        }
        public CommandBinding SaveCommandBinding
        {
            get { return _saveCommandBinding; }
        }
        public CommandBinding DeleteCommandBinding
        {
            get { return _deleteCommandBinding; }
        }
        public CommandBinding UndoCommandBinding
        {
            get { return _undoCommandBinding; }
        }
        #endregion

        #region "Konstruktor"
        public MainViewModel()
        {
            // Positionenliste füllen
            _positionsList = new ObservableCollection<PositionViewModel>();
            LoadPositions(ref _positionsList);

            // ListCollectionView initialisieren
            _positionsView = new ListCollectionView(_positionsList);
            _positionsView.CurrentChanged += _positions_CurrentChanged;

            // Commands initialisieren
            FirstCommand = new RelayCommand(FirstExecute, BackCanExecute);
            PreviousCommand = new RelayCommand(PreviousExecute, BackCanExecute);
            NextCommand = new RelayCommand(NextExecute, ForwardCanExecute);
            LastCommand = new RelayCommand(LastExecute, ForwardCanExecute);
            RunCommand = new RelayCommand(RunExecute, RunCanExecute);

            // CommandBindings erzeugen
            _newCommandBinding = new CommandBinding(ApplicationCommands.New, NewExecuted, NewCanExecute);
            _deleteCommandBinding = new CommandBinding(ApplicationCommands.Delete, DeleteExecuted, DeleteCanExecute);
            _saveCommandBinding = new CommandBinding(ApplicationCommands.Save, SaveExecuted, SaveCanExecute);
            _undoCommandBinding = new CommandBinding(ApplicationCommands.Undo, UndoExecuted, UndoCanExecute);

            UpdateSorting();
            _positionsView.MoveCurrentToFirst();
            _Motor = new();
            // stpMotorData.DataContext = _Motor;
        }
        #endregion

        #region "Methoden der CommandBindings"
        private void SaveCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if(ListChanged)
            {
                e.CanExecute = true;
                return;
            }
            foreach (PositionViewModel item in _positionsView)
            {
                if (item.Changed == "*")
                {
                    e.CanExecute = true;
                    return;
                }
            }
            e.CanExecute = false;
        }

        private void SaveExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            ObservableCollection<Position> position = new ObservableCollection<Position>();
            foreach(PositionViewModel item in _positionsList)
            {
                Position p = new Position();
                p.Id = item.Id.Value;
                p.XPos = item.XPos.Value;
                p.YPos = item.YPos.Value;
                p.ZPos = item.ZPos.Value;
                p.Dwell = item.Dwell.Value;
                p.DaliAdr = item.DaliAdr.Value;
                p.DaliVal = item.DaliVal.Value;
                position.Add(p);
            }
            FileStream fs = new FileStream("Positions.xml", FileMode.Create);
            XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<Position>));
            serializer.Serialize(fs, position);
            fs.Close();
            AcceptChanges();
            ListChanged = false;
        }

        private void DeleteCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = PositionsView.Count > 0;
        }

        private void DeleteExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            CancelEventArgs arg = new CancelEventArgs();
            OnConfirmDeleting(arg);
            if (arg.Cancel == true) return;
            PositionViewModel posDelete = PositionsView.CurrentItem as PositionViewModel;
            if (posDelete != null)
                _positionsList.Remove(posDelete);
            _positionsView.MoveCurrentToFirst();
            ListChanged = true;
        }
        private void NewCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        private void NewExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            PositionViewModel position = new PositionViewModel(null);
            _positionsList.Add(position);
            _positionsView.MoveCurrentTo(position);
            SetFocus = true;
        }
        private void UndoCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if ((_positionsView).Count == 0)
            {
                e.CanExecute = false;
                return;
            }
            e.CanExecute = ((PositionViewModel)_positionsView.CurrentItem).Changed == "*";
        }

        private void UndoExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            ((PositionViewModel)_positionsView.CurrentItem).UndoChanges();
        }
        #endregion

        #region "Methoden für Commands"

        private bool BackCanExecute(object obj)
        {
            return _positionsView.CurrentPosition > 0;
        }
        private bool ForwardCanExecute(object obj)
        {
            return _positionsView.CurrentPosition < _positionsView.Count - 1;
        }
        private bool RunCanExecute(object obj)
        {
            return _positionsView.Count > 0;
        }
        private void FirstExecute(object obj)
        {
            _positionsView.MoveCurrentToFirst();
        }
        private void PreviousExecute(object obj)
        {
            _positionsView.MoveCurrentToPrevious();
        }
        private void NextExecute(object obj)
        {
            _positionsView.MoveCurrentToNext();
        }
        private void LastExecute(object obj)
        {
            _positionsView.MoveCurrentToLast();
        }
        private void RunExecute(object obj)
        {
            _positionsView.MoveCurrentToFirst();
        }
        #endregion
        #region "Öffentliche Methoden"
        public void CancelViewClosing()
        {
            bool hasChanged = false;
            if (ListChanged)
                hasChanged = true;
            else
                foreach (PositionViewModel item in _positionsView)
                    if (item.Changed == "*") hasChanged = true;
            if(hasChanged)
            {
                string message = "Sollen die Änderungen gespeichert werden?";
                string caption = "MVVM_Sample";
                MessageBoxResult result = MessageBox.Show(message, caption, MessageBoxButton.YesNo, MessageBoxImage.Question);
                if (result == MessageBoxResult.Yes)
                    SaveExecuted(this, null);
            }
        }
        #endregion

        #region "Private Methoden"

        private void UpdateSorting()
        {
            PositionsView.SortDescriptions.Clear();
            PositionsView.SortDescriptions.Add(new SortDescription(this.SortByProperty, ListSortDirection.Ascending));
        }

        private void LoadPositions(ref ObservableCollection<PositionViewModel> liste)
        {
            // XML-Datei deserialisieren
            if (File.Exists("Positions.xml"))
            {
                FileStream fs = new FileStream("Positions.xml", FileMode.Open);
                XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<Position>));
                var tempListe = (ObservableCollection<Position>)serializer.Deserialize(fs);
                foreach (var item in tempListe)
                    liste.Add(new PositionViewModel(item));
                fs.Close();
            }
        }

        private void _positions_CurrentChanged(object sender, EventArgs e)
        {
            ActualPosition = "Position " + (_positionsView.CurrentPosition + 1) + " von " + _positionsView.Count;
        }

        private void AcceptChanges()
        {
            foreach (PositionViewModel item in _positionsView)
                item.AcceptChanges();
        }
        public bool UpdatePos(Motor m, string msg)
        {
            Motor mot = m as Motor;
            int wert = 0;
            bool success = int.TryParse(msg.Substring(4), out wert);
            string Kennung = msg.Substring(3, 1);
            switch (Kennung)
            {
                case "X":
                    SetProperty<string>(ref mot.posX, wert.ToString("D4"));
                    //_Motor.PosX = wert.ToString("D4");
                    break;
                case "Y":
                    _Motor.PosY = wert.ToString("D4");
                    break;
            }
            return success;
        }
        #endregion
    }
}
