using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Machinations.Annotations;
using Machinations.FromFlash.graph;
using Machinations.FromFlash.machinations.model;
using Machinations.GUI;
using Machinations.UserComponents;

namespace Machinations.WPF
{
    /// <summary>
    /// Interaction logic for UserComponentEditor.xaml
    /// Class for viewing and editing IComponentsSchemas 
    /// </summary>
    public partial class UserComponentEditor : INotifyPropertyChanged
    {
        private readonly IUserComponentSchema m_originalSchema;
        private readonly IUserComponentSchema m_editedSchema;
        //private m_graph m_graph;
        //private string m_name;
        private UcEditorClosedState m_closeStatus;
        private readonly bool m_editable;
        private readonly string m_name;
        private readonly HashSet<UserComponentGraph> m_alreadyOpened;

        public UserComponentEditor(IUserComponentSchema schema, 
                                    bool editable, string name, IEnumerable<IUserComponentSchema> otherSchemas,
                                    MachinationsGraph machinationsGraph)
        {
            m_editable = editable;
            m_name = name;
            m_originalSchema = schema;
            m_editedSchema = m_editable ? schema.Clone() : m_originalSchema;
            m_alreadyOpened = new HashSet<UserComponentGraph>();
            InitializeComponent();
            PaintingCanvas.BaseGraph = machinationsGraph;
            PaintingCanvas.Initialize(Graph, GraphControl, ComponentsSettings);
            if (m_editable)
            {
                ComponentsSettings.Initialize(Graph, PaintingCanvas);
                GraphControl.ButtonPressed += GraphControlButtonPressed;
                GraphControl.SelectAllPressed += PaintingCanvas.SelectAll;
            }
            else
            {
                EditPanel.Visibility = Visibility.Collapsed;
            }
            LoadElementsToCanvas();
            PaintingCanvas.Schemas = otherSchemas.ToList();
            UserComponents.AcceptComponents(PaintingCanvas.Schemas);
            UserComponents.ButtonChecked += () =>
            {
                GraphControl.GraphButtonUncheck();
                PaintingCanvas.InsertedObject = UserComponents.SelectedComponent;
            };
            PaintingCanvas.EditDisabled = !m_editable;
            //PaintingCanvas.IsEnabled = m_editable;
            if (IsDeck)
            {
                Cards.OnCardsChanged += CloseAfterChanged;
            }
        }

        private void CloseAfterChanged()
        {
            Cards.OnCardsChanged -= CloseAfterChanged;
            this.Close();
        }

        public string ComponentName
        {
            get { return m_editedSchema.Name; }
            set
            {
                m_editedSchema.Name = value; 
                OnPropertyChanged(nameof(HasName));
            }
        }

        public bool NotFromInitComponent
        {
            get
            {
                return !m_originalSchema.Name.Equals(MachinationsVariables.UserComponentInitName);
            }
        }

        public bool HasName { get { return m_editedSchema.Name.Length > 0 && !m_editedSchema.Name.Equals(MachinationsVariables.UserComponentInitName); } }

        public UserComponentGraph Graph
        {
            get
            {
                return Instance?.InternalGraph ?? new UserComponentGraph(null);
            }
        }

        private IUserComponentInstance Instance
        {
            get
            {
                if (!IsDeck)
                    return (UserComponent)m_editedSchema;
                if (m_editable)
                    return Cards.CurrentCard;
                //pokud je to balicek karet a otevre se za behu
                return Cards.Instances.Single(i=> i.Name == m_name);
            }
        }

        public UcEditorClosedState CloseStatus
        {
            get { return m_closeStatus; }
        }

        public IUserComponentSchema EditedSchema
        {
            get { return m_editedSchema; }
        }

        public bool IsDeck
        {
            get { return EditedSchema.Type == UserComponentType.Card; }
        }

        /// <summary>
        /// Nasledujici property jsou pro karty
        /// </summary>

        public Cards Cards
        {
            get { return ((Cards) m_editedSchema); }
        }

        public ObservableCollection<string> Cards2
        {
            get
            {
                if(IsDeck)
                    return new ObservableCollection<string>(Cards.ActualTemplateNames);
                return null;
            }
        }  

        public string CardName
        {
            get { return IsDeck ? Cards.CurrentCard.Name : ""; }
            set
            {
                if (Cards.TrySetCurrentCardName(value))
                {
                    OnPropertyChanged(nameof(Cards2));
                    CardComboBoxRefresh();
                }
            }
        }

        public int CardDensity
        {
            get { return IsDeck ? Cards.CurrentCard.Density : 0; }
            set
            {
                Cards.CurrentCard.Density = value;
                CardComboBoxRefresh();
            }
        }

        public bool Shuffle
        {
            get { return IsDeck && Cards.Shuffle; }
            set { Cards.Shuffle = value; }
        }

        public string SelectedCard
        {
            get { return IsDeck? Cards.CurrentCard.Name : ""; }
            set
            {
                if (!Cards.CurrentCard.Name.Equals(value) && value != null)
                {
                    Cards.ChooseActiveTemplate(value);
                    ChangeCard();
                }
            }
        }

        public bool IsEditable
        {
            get { return m_editable; }
        }

        private void ChangeCard()
        {
            PaintingCanvas.Reset(Graph);
            LoadElementsToCanvas();
            OnPropertyChanged(nameof(CardDensity));
            OnPropertyChanged(nameof(CardName));
        }

        private void GraphControlButtonPressed()
        {
            PaintingCanvas.InsertedObject = GraphControl.CreateElement();
        }

        private void SaveClick(object sender, RoutedEventArgs e)
        {
            string tag = (string)((Button) sender).Tag;
            if (tag == "ChangeAll")
            {
                m_closeStatus = UcEditorClosedState.OverwriteAll;
                m_editedSchema.Name = m_originalSchema.Name;
            }
            else if(tag == "ChangeOne")
                m_closeStatus = UcEditorClosedState.SaveAsNew;
            else if (tag == "Delete")
                m_closeStatus = UcEditorClosedState.Delete;
            DialogResult = true;
        }

        private void CancelClick(object sender, RoutedEventArgs e)
        {
            Close();
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (m_editable)
                KeyDownEdit(e);
            //e.Handled = true;
        }

        
        private void KeyDownEdit(KeyEventArgs e)
        {
            if (e.Key == Key.Delete || e.Key == Key.Escape)
            {
                PaintingCanvas.Delete();
                return;
            }
            if (ProgramControlUtils.IsFunctionKey(e.Key))
                GraphControl.SetButton(e.Key);

        }

        private void LoadElementsToCanvas()
        {
            if (Graph.Elements.Count > 0)
            {
                var elements = Instance.UnzippedElements;
                Dictionary<GraphElement, IObjectView> createdViews = new Dictionary<GraphElement, IObjectView>();
                elements.ForEach(e => ProgramControlUtils.CreateViews(e, PaintingCanvas, createdViews, elements, true));
                bool addPointsToConnections = m_editable && (!IsDeck || !m_alreadyOpened.Contains(Graph));
                elements.ForEach(e => //tady se to spojuje podruhe. Respektive viewy chci, modely uz jsou 
                {
                    MachinationsConnection c = e as MachinationsConnection;
                    if (c != null)
                    {
                        ProgramControlUtils.ConnectConnections(c, Graph.Elements, createdViews, false, addPointsToConnections);
                    }
                });
                if(IsDeck)
                    m_alreadyOpened.Add(Graph);
            }
        }

        private void AddCardClick(object sender, RoutedEventArgs e)
        {
            string newName = Cards.CreateCardTemplate(true);
            Cards.ChooseActiveTemplate(newName);
            CardComboBoxRefresh();
            ChangeCard();
        }

        private void RemoveCardClick(object sender, RoutedEventArgs e)
        {
            var cards = (Cards)m_editedSchema;
            if (cards.TemplateNames.Count > 1)
            {
                cards.RemoveCardTemplate(CardName);
                ChangeCard();
                CardComboBoxRefresh();
            }
            else
            {
                m_closeStatus = UcEditorClosedState.Delete;
                DialogResult = true;
            }
            
        }

        private void CardComboBoxRefresh()
        {
            OnPropertyChanged(nameof(Cards2));
            CardComboBox.SelectedItem = Cards2[Cards2.IndexOf(Cards.CurrentCard.Name)];
            //OnPropertyChanged(nameof(SelectedCard));
            //CardComboBox.GetBindingExpression(ItemsControl.ItemsSourceProperty)?.UpdateTarget();
        }

        private void ChangeOrder(object sender, RoutedEventArgs e)
        {
            CardsOrderEditor editor = new CardsOrderEditor(Cards.Order);
            editor.Activate();
            if (editor.ShowDialog() == true)
            {
                Cards.Order = editor.Cards.ToList();
            }
        }
    }

    public enum UcEditorClosedState
    {
        None, SaveAsNew, OverwriteAll, Delete
    }
}
