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.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using Machinations.Annotations;
using Machinations.FromFlash.graph;
using Machinations.FromFlash.machinations.model;
using Machinations.GUI;
using Machinations.UserComponents;
using Xceed.Wpf.Toolkit;

namespace Machinations.WPF
{
    /// <summary>
    /// Interaction logic for ComponentsSettings.xaml
    /// handles changing of parameters to elements anf graph
    /// </summary>
    public partial class ComponentsSettings : INotifyPropertyChanged
    {
        private Graph m_graph;
        private WPF.PaintingCanvas m_paintingCanvas;
        public event PropertyChangedEventHandler PropertyChanged;
        private readonly List<string> m_commonStateCValues = new List<string>()
        {
            "D6",
            "R6",
            "*",
            "+1"
        };

        private string m_selectedCommonLabel;

        public void Initialize(Graph graph, WPF.PaintingCanvas canvas)
        {
            m_graph = graph;
            if (IsMachinationsGraph)
            {
                ((MachinationsGraph) graph).SizeChanged += OnSizeChanged;
                ((MachinationsGraph) graph).GraphLoaded += () =>
                {
                    HideSettings();
                };
            }
            m_paintingCanvas = canvas;
            OnPropertyChanged(nameof(HasTurnPools));
            InitializeComponent();
        }

        private List<IObjectView> SelectedObjects { get { return m_paintingCanvas.SelectedObjects; } }

        public string GraphName
        {
            get { return IsMachinationsGraph ? ((MachinationsGraph)m_graph).Name : ""; }
            set { ((MachinationsGraph)m_graph).Name = value; }
        }

        public string GraphAuthor
        {
            get { return IsMachinationsGraph ? ((MachinationsGraph)m_graph).Author : ""; }
            set { ((MachinationsGraph)m_graph).Author = value; }
        }

        public int GraphSelectedTimeMode
        {
            get
            {
                return IsMachinationsGraph ? (int)((MachinationsGraph)m_graph).TimeMode - 1 : 0;
            }
            set
            {
                ((MachinationsGraph)m_graph).TimeMode = (TimeMode)value + 1;
                OnPropertyChanged(nameof(GraphActionIsVisible));
            }
        }

        public bool GraphActionIsVisible
        {
            get
            {
                return IsMachinationsGraph && ((MachinationsGraph)m_graph).TimeMode == TimeMode.TurnBased;
            }
        }

        public double GraphFireInterval
        {
            get
            {
                var machinationsGraph = m_graph as MachinationsGraph;
                return machinationsGraph?.FireInterval ?? 0;
            }
            set {
                ((MachinationsGraph)m_graph).FireInterval = value;
            }
        }

        public int GraphActionsPerTurn
        {
            get
            {
                var machinationsGraph = m_graph as MachinationsGraph;
                if (machinationsGraph != null) return machinationsGraph.ActionsPerTurn;
                return 0;
            }
            set
            {
                ((MachinationsGraph)m_graph).ActionsPerTurn = value;
            }
        }

        public int GraphDistributionIndex
        {
            get { return IsMachinationsGraph ? (int)((MachinationsGraph)m_graph).DistributionMode - 1 : 0;}
            set { ((MachinationsGraph)m_graph).DistributionMode = (DistributionMode)value + 1; }
        }

        public bool GraphColorCoding
        {
            get { return (IsMachinationsGraph && ((MachinationsGraph)m_graph).ColorCoding); }
            set { ((MachinationsGraph)m_graph).ColorCoding = value; }
        }

        public bool CanSkipTurn
        {
            get { return IsMachinationsGraph && ((MachinationsGraph)m_graph).CanSkipTurn; }
            set { ((MachinationsGraph)m_graph).CanSkipTurn = value; }
        }

        //public bool PullAllSkipsInhibited
        //{
        //    get { return IsMachinationsGraph && ((MachinationsGraph)m_graph).PullAllCountsInhibited; }
        //    set { ((MachinationsGraph)m_graph).PullAllCountsInhibited = value; }
        //}

        public string GraphDice
        {
            get { return IsMachinationsGraph ? ((MachinationsGraph) m_graph).Dice : ""; }
            set { ((MachinationsGraph)m_graph).Dice = value; }
        }

        public string GraphSkill
        {
            get { return IsMachinationsGraph ? ((MachinationsGraph)m_graph).Skill : ""; }
            set { ((MachinationsGraph)m_graph).Skill = value; }
        }

        public string GraphMultiplayer
        {
            get { return IsMachinationsGraph ? ((MachinationsGraph)m_graph).Multiplayer : ""; }
            set { ((MachinationsGraph)m_graph).Multiplayer = value; }
        }

        public string GraphStrategy
        {
            get { return IsMachinationsGraph ? ((MachinationsGraph)m_graph).Strategy : ""; }
            set { ((MachinationsGraph)m_graph).Strategy = value; }
        }

        public int GraphWidth
        {
            get { return Convert.ToInt32(m_paintingCanvas.CanvasWidth); }
            set { m_paintingCanvas.CanvasWidth = Convert.ToDouble(value); }
        }

        public int GraphHeight
        {
            get { return Convert.ToInt32(m_paintingCanvas.CanvasHeight); }
            set { m_paintingCanvas.CanvasHeight = Convert.ToDouble(value); }
        }

        public Color SelectedColor
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any())
                {
                    return Colors.Black;
                }
                return SelectedObjects.First().BaseColor;
            }
            set { SelectedObjects.ForEach(selected => selected.BaseColor = value); }
        }

        public ObservableCollection<Pool> GraphSpecialPools
        {
            get
            {
                var machinationsGraph = m_graph as MachinationsGraph;
                if(machinationsGraph != null)
                    return ((MachinationsGraph) m_graph).TurnPools;
                return new ObservableCollection<Pool>();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public string SelectedLabel
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any())
                {
                    return "";
                }
                return SelectedObjects.First().Label;
            }
            set
            {
                SelectedObjects.ForEach(selected => selected.Label = value);
            }
        }

        public int SelectedNodeActivationIndex
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any())
                {
                    return 0;
                }
                var node = (INodeView<MachinationsNode>)SelectedObjects.First();
                return node.ActivationMode;
            }
            set
            {
                foreach (var node in SelectedObjects.Where(so => so is INodeView<MachinationsNode>))
                {
                    ((INodeView<MachinationsNode>)node).ActivationMode = value;
                }
            }
        }

        public int SelectedNodeActions
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                return ((INodeView<MachinationsNode>)SelectedObjects.First()).Model.Actions;
            }
            set
            {
                foreach (var node in SelectedObjects.Where(so => so is INodeView<MachinationsNode>))
                {
                    ((INodeView<MachinationsNode>)node).Model.Actions = value;
                }
            }
        }

        public int SelectedNodePullIndex
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any())
                {
                    return 0;
                }
                var node = (INodeView<MachinationsNode>)SelectedObjects.First();
                return node.PullMode;
            }
            set
            {
                foreach (var node in SelectedObjects.Where(so => so is INodeView<MachinationsNode>))
                {
                    ((INodeView<MachinationsNode>)node).PullMode = value;
                }
            }
        }

        public Color SelectedNodeResourcesColor
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return Colors.Black;
                return ((Source)SelectedObjects.Cast<INodeView<MachinationsNode>>().First().Model).ResourceColor;
            }
            set
            {
                var sources = SelectedObjects.Select(so => ((IObjectView<Source>)so).Model);
                foreach (var source in sources)
                {
                    source.ResourceColor = value;
                }
            }
        }

        public int SelectedPoolResources
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                var pool = (PoolView)SelectedObjects.First();
                return (pool.Model).StartingResources;
            }
            set
            {
                SelectedObjects.ForEach(so =>
                {
                    ((PoolView)so).ResourcesCount = value;
                });
            }
        }

        public int SelectedPoolCapacity
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return -1;
                var pool = (PoolView)SelectedObjects.First();
                return (pool.Model).Capacity;
            }
            set
            {
                SelectedObjects.ForEach(so => ((PoolView)so).Model.Capacity = value);
            }
        }

        public bool SelectedTurnPool
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return false;
                var pool = (PoolView)SelectedObjects.First();
                return (pool.Model).IsTurnPool;
            }
            set
            {
                SelectedObjects.ForEach(so => ((PoolView)so).Model.IsTurnPool = value);
                OnPropertyChanged(nameof(SelectedTurnPool));
                OnPropertyChanged(nameof(HasTurnPools));
            }
        }

        public int SelectedTurnPoolMode
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                var pool = (PoolView)SelectedObjects.First();
                return (pool.Model).TurnPoolIsNormal;
            }
            set { SelectedObjects.ForEach(so => ((PoolView)so).Model.TurnPoolIsNormal = value);}
        }

        public bool FireTwice
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return false;
                var pool = (PoolView)SelectedObjects.First();
                return (pool.Model).FireTwice;
            }
            set { SelectedObjects.ForEach(so => ((PoolView)so).Model.FireTwice = value); }
        }

        public int SelectedGateModeIndex
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                return (int)((GateView)SelectedObjects.First()).Model.GateType - 1;
            }
            set
            {
                SelectedObjects.ForEach(o => ((GateView)o).Model.GateType = (GateMode)value + 1);
            }
        }

        public int SelectedLineOrRegisterMinValue
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                var connection = SelectedObjects.OfType<IConnectionView>().FirstOrDefault();
                if (connection != null) return Convert.ToInt32(connection.Model.Label.Min);
                return ((RegisterView)SelectedObjects.First()).Model.MinValue;
            }
            set
            {
                if (SelectedObjects.All(o => o is IConnectionView))
                {
                    SelectedObjects.ForEach(o => ((IConnectionView)o).Model.Label.Min = value);
                }
                else SelectedObjects.ForEach(o => ((RegisterView)o).Model.MinValue = value);
            }
        }

        public int SelectedLineOrRegisterMaxValue
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                var connection = SelectedObjects.OfType<IConnectionView>().FirstOrDefault();
                if (connection != null) return Convert.ToInt32(connection.Model.Label.Max);
                return ((RegisterView)SelectedObjects.First()).Model.MaxValue;
            }
            set
            {
                if (SelectedObjects.All(o => o is IConnectionView))
                {
                    SelectedObjects.ForEach(o => ((IConnectionView)o).Model.Label.Max = value);
                }
                else SelectedObjects.ForEach(o => ((RegisterView)o).Model.MaxValue = value);
            }
        }

        public List<string> CommonStateCValues
        {
            get { return m_commonStateCValues; }
        }

        public string SelectedCommonValue
        {
            get { return m_selectedCommonLabel; }
            set
            {
                SelectedLabel = value;
                OnPropertyChanged(nameof(SelectedLabel));
                m_selectedCommonLabel = value;
            }
        }

        public bool ForwardOnly
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return false;
                var connection = SelectedObjects.OfType<StateConnectionView>().FirstOrDefault();
                if (connection != null) return ((StateConnection)connection.Model).ForwardOnly;
                return false;
            }
            set
            {
               SelectedObjects.ForEach(o => ((StateConnection)((IConnectionView)o).Model).ForwardOnly = value);
            }
        }

        public bool SelectedDelayIsQueue
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return false;
                return ((DelayView)SelectedObjects.First()).Model.DelayType == Delay.TypeQeue;
            }
            set
            {
                SelectedObjects.ForEach(o => ((DelayView)o).Model.DelayType = value ? Delay.TypeQeue : Delay.TypeNormal);
            }
        }

        public string SelectedAIScript
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return "";
                return ((ArtifitialPlayerView)SelectedObjects.First()).Model.Script;
            }
            set
            {
                SelectedObjects.ForEach(o => ((ArtifitialPlayerView)o).Model.Script = value);
            }
        }

        public bool SelectedRegisterIsInteractive
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return false;
                return ((RegisterView)SelectedObjects.First()).Interactive;
            }
            set
            {
                SelectedObjects.ForEach(o => ((RegisterView)o).Interactive = value);
                OnPropertyChanged(nameof(SelectedRegisterIsInteractive));    
            }
        }

        public int SelectedRegisterStartingValue
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                return ((RegisterView)SelectedObjects.First()).Model.StartValue;
            }
            set
            {
                SelectedObjects.ForEach(o =>
                {
                    ((RegisterView)o).Model.StartValue = value;
                    ((RegisterView)o).RefreshText();
                });

            }
        }

        public int SelectedRegisterStep
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                return ((RegisterView)SelectedObjects.First()).Model.ValueStep;
            }
            set
            {
                SelectedObjects.ForEach(o => ((RegisterView)o).Model.ValueStep = value);
            }
        }

        public bool IsMachinationsGraph
        {
            get { return m_graph is MachinationsGraph; }
        }

        public int UserComponentRadius
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                return ((UserComponentView)SelectedObjects.First()).Model.Radius;
            }
            set
            {
                SelectedObjects.ForEach(o => ((UserComponentView)o).Model.Radius = value);
            }
        }

        public Color UserComponentColor
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return ColorsUtils.InitColor;
                return ((UserComponentView)SelectedObjects.First()).Model.Color;
            }
            set
            {
                SelectedObjects.ForEach(o => ((UserComponentView)o).Model.Color = value);
            }
        }

        public int Thickness
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                return ((ConnectionView)SelectedObjects.First()).Model.Thickness;
            }
            set
            {
                SelectedObjects.ForEach(o => ((ConnectionView)o).Model.Thickness = value);
            }
        }

        public int ShowedCard
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                return ((UserComponentView)SelectedObjects.First()).Model.CurrentCard;
            }
            set
            {
                SelectedObjects.ForEach(o => ((UserComponentView)o).Model.CurrentCard = value);
            }

        }

        public int CardsCount
        {
            get
            {
                if (SelectedObjects == null || !SelectedObjects.Any()) return 0;
                return ((Cards)((UserComponentView)SelectedObjects.First()).Model.GetUcScheme()).CardsCount;
            }
        }

        public bool HasTurnPools
        {
            get { return IsMachinationsGraph && ((MachinationsGraph) m_graph).TurnPools.Any(); }
        }


        private void OnSizeChanged()
        {
            OnPropertyChanged(nameof(GraphWidth));
            OnPropertyChanged(nameof(GraphHeight));
        }

        private void HideSettings(IEnumerable<StackPanel> except)
        {
            List<StackPanel> allSettings = new List<StackPanel>()
            {
                GraphSettings,
                BaseElementSettings,
                NodeSettings,
                SourceSettings,
                PoolSettings,
                GateSettings,
                ConnectionSettings,
                StateConnectionSettings,
                DelaySettings,
                RegisterSettings,
                AiSettings,
                UserComponentSettings,
                CardsSettings,
                NameSettings,
            };
            allSettings.ForEach(
                settings => settings.Visibility = !except.Contains(settings) ? Visibility.Collapsed : Visibility.Visible);
            foreach (StackPanel stackPanel in except)
            {
                stackPanel.InvalidateVisual();
                foreach (var hopefullyGrid in stackPanel.Children)
                {
                    if (hopefullyGrid is TextBlock) continue;
                    var textBox = hopefullyGrid as TextBox;
                    if (textBox != null)
                    {
                        textBox.GetBindingExpression(TextBox.TextProperty)?.UpdateTarget();
                        continue;
                    }
                    var toggle = hopefullyGrid as ToggleButton;
                    if (toggle != null)
                    {
                        toggle.GetBindingExpression(ToggleButton.IsCheckedProperty)?.UpdateTarget();
                        continue;
                    }
                    Grid grid = hopefullyGrid as Grid;
                    if (grid != null)
                    {
                        foreach (UIElement child in grid.Children)
                        {
                            if (child is TextBlock) continue;
                            var box = child as TextBox;
                            if (box != null)
                            {
                                box.GetBindingExpression(TextBox.TextProperty)?.UpdateTarget();
                                continue;
                            }
                            var comboBox = child as ComboBox;
                            if (comboBox != null)
                            {
                                comboBox.GetBindingExpression(Selector.SelectedIndexProperty)?.UpdateTarget();
                                continue;
                            }
                            var checkBox = child as CheckBox;
                            if (checkBox != null)
                            {
                                checkBox.GetBindingExpression(ToggleButton.IsCheckedProperty)?.UpdateTarget();
                                continue;
                            }
                            var colorPicker = child as ColorPicker;
                            if (colorPicker != null)
                            {
                                colorPicker.GetBindingExpression(ColorPicker.SelectedColorProperty)?.UpdateTarget();
                                continue;
                            }
                            var upDown = child as ShortUpDown;
                            if (upDown != null)
                            {
                                upDown.GetBindingExpression(ShortUpDown.ValueProperty)?.UpdateTarget();
                                upDown.GetBindingExpression(ShortUpDown.MaximumProperty)?.UpdateTarget();
                                upDown.GetBindingExpression(ShortUpDown.MinimumProperty)?.UpdateTarget();
                                continue;
                            }
                            var listView = child as ListView;
                            if (listView != null)
                            {
                                listView.GetBindingExpression(ItemsControl.ItemsSourceProperty)?.UpdateTarget();
                                var selectedO = SelectedObjects.ToList();
                                SelectedObjects.Clear();
                                listView.SelectedItems.Clear();
                                for (int i = 0; i < listView.Items.Count; i++)
                                {
                                    if (
                                        selectedO.All(
                                            o => o.GetModelElement().TurnPools.Contains(
                                                listView.Items[i])))
                                                
                                    {
                                        listView.SelectedItems.Add(listView.Items[i]);
                                    }
                                }
                                SelectedObjects.AddRange(selectedO);
                                continue;
                            }
                            throw new NotImplementedException();
                        }
                    }
                    else throw new NotImplementedException();
                }
            }
        }

        internal void HideSettings(List<IObjectView> selectedObjects = null)
        {
            if (selectedObjects == null || !selectedObjects.Any())
            {
                if (IsMachinationsGraph)
                {
                    HideSettings(new[] { GraphSettings });
                    //OnPropertyChanged("SettingsPanel");
                }
                return;
            }
            if (SelectedObjects.All(sel => sel is IConnectionView))
            {
                HideSettings(SelectedObjects.All(sel => sel is StateConnectionView)
                    ? new[] {BaseElementSettings, ConnectionSettings, StateConnectionSettings}
                    : new[] {BaseElementSettings, ConnectionSettings});
            }
            else if (SelectedObjects.All(sel => sel is INodeView<MachinationsNode>))
            {
                if (SelectedObjects.All(sel => sel is PoolView))
                {
                    HideSettings(new[] { BaseElementSettings, NodeSettings, SourceSettings, PoolSettings });
                }
                else if (SelectedObjects.All(sel => sel is GateView))
                {
                    HideSettings(new[] { BaseElementSettings, NodeSettings, GateSettings });
                }
                else if (SelectedObjects.All(sel => sel is SourceView || sel is PoolView || sel is ConverterView || sel is TraderView))
                {
                    HideSettings(new[] { BaseElementSettings, NodeSettings, SourceSettings });
                }
                else if (SelectedObjects.All(sel => sel is DelayView))
                {
                    HideSettings(new[] { BaseElementSettings, NodeSettings, DelaySettings });
                }
                else if (SelectedObjects.All(sel => sel is RegisterView))
                {
                    HideSettings(new[] { BaseElementSettings, RegisterSettings });
                    OnPropertyChanged(nameof(SelectedRegisterIsInteractive));
                }
                else if (SelectedObjects.All(sel => sel is ArtifitialPlayerView))
                {
                    HideSettings(new[] { BaseElementSettings, NodeSettings, AiSettings });
                }
                else
                {
                    HideSettings(new[] { BaseElementSettings, NodeSettings });
                }
            }
            else if (SelectedObjects.All(sel => sel is InOutInnerView))
            {
                HideSettings(new[] { NameSettings });
            }
            else if (SelectedObjects.All(sel => sel is UserComponentView))
            {
                if(SelectedObjects.All(sel => ((UserComponentView)sel).Model.Type == UserComponentType.Card))
                    HideSettings(new[] { UserComponentSettings, CardsSettings });
                else
                    HideSettings(new[] { UserComponentSettings });
            }
            else HideSettings(new[] { BaseElementSettings });
            //OnPropertyChanged("SettingsPanel");
        }

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

        private void UIElement_OnMouseEnter(object sender, MouseEventArgs e)
        {
            var selected = (GraphElement)((StackPanel)sender).DataContext;
            selected.Selected();
        }
        
        private void UIElement_OnMouseLeave(object sender, MouseEventArgs e)
        {
            var selected = (GraphElement)((StackPanel)sender).DataContext;
            selected.UnSelected();
        }

        private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ListView listView = (ListView)sender;
            System.Collections.IList items = listView.SelectedItems;
            if (IsMachinationsGraph)
            {
                SelectedObjects.ForEach(
               selected =>
                   selected.GetModelElement().TurnPools = items.Cast<Pool>().ToList());
            }
        }
    }
}
