using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using AIStudio.Wpf.DiagramDesigner.Helpers;

namespace AIStudio.Wpf.DiagramDesigner
{
    public class BlockItemsContainer : ContentControl
    {
        static BlockItemsContainer()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(BlockItemsContainer), new FrameworkPropertyMetadata(typeof(BlockItemsContainer)));
        }

        public static readonly DependencyProperty BorderProperty = DependencyProperty.Register(
          nameof(Border), typeof(object), typeof(BlockItemsContainer), new PropertyMetadata(default(object)));

        public object Border
        {
            get => GetValue(BorderProperty);
            set => SetValue(BorderProperty, value);
        }

        public static readonly DependencyProperty BorderTemplateProperty = DependencyProperty.Register(
            nameof(BorderTemplate), typeof(DataTemplate), typeof(BlockItemsContainer), new PropertyMetadata(default(DataTemplate)));

        [Bindable(true), Category("Content")]
        public DataTemplate BorderTemplate
        {
            get => (DataTemplate)GetValue(BorderTemplateProperty);
            set => SetValue(BorderTemplateProperty, value);
        }

        public static readonly DependencyProperty BorderTemplateSelectorProperty = DependencyProperty.Register(
            nameof(BorderTemplateSelector), typeof(DataTemplateSelector), typeof(BlockItemsContainer), new PropertyMetadata(default(DataTemplateSelector)));

        [Bindable(true), Category("Content")]
        public DataTemplateSelector BorderTemplateSelector
        {
            get => (DataTemplateSelector)GetValue(BorderTemplateSelectorProperty);
            set => SetValue(BorderTemplateSelectorProperty, value);
        }

        public static readonly DependencyProperty BorderStringFormatProperty = DependencyProperty.Register(
            nameof(BorderStringFormat), typeof(string), typeof(BlockItemsContainer), new PropertyMetadata(default(string)));

        [Bindable(true), Category("Content")]
        public string BorderStringFormat
        {
            get => (string)GetValue(BorderStringFormatProperty);
            set => SetValue(BorderStringFormatProperty, value);
        }


        public static readonly DependencyProperty ParentPanelProperty =
           DependencyProperty.Register(nameof(ParentPanel), typeof(FrameworkElement), typeof(BlockItemsContainer),
               new FrameworkPropertyMetadata(null));

        public FrameworkElement ParentPanel
        {
            get
            {
                return (FrameworkElement)GetValue(ParentPanelProperty);
            }
            set
            {
                SetValue(ParentPanelProperty, value);
            }
        }

        public static readonly DependencyProperty GetOffSetFuncProperty =
       DependencyProperty.Register(nameof(GetOffSetFunc),
                                  typeof(Func<Point>),
                                  typeof(BlockItemsContainer),
                                  new FrameworkPropertyMetadata(null));
        public Func<Point> GetOffSetFunc
        {
            get
            {
                return (Func<Point>)this.GetValue(GetOffSetFuncProperty);
            }
            set
            {
                this.SetValue(GetOffSetFuncProperty, value);
            }
        }

        public Func<Point> GetOffSet
        {
            get
            {
                return new Func<Point>(() => this.TransformToAncestor(ParentPanel).Transform(new Point(0, 0)));
            }
        }

        public BlockItemsContainerInfo Info
        {
            get
            {
                if (Border is BlockItemsContainerInfo itemsContainerInfo)
                    return itemsContainerInfo;

                return this.DataContext as BlockItemsContainerInfo;
            }
        }

        public BlockDesignerItemViewModel DragObject
        {
            get; private set;
        }

        public Point DragOffset
        {
            get; private set;
        }

        public Point? FirstPoint
        {
            get; private set;
        }

        public BlockItemsContainer()
        {

        }

        public static List<BlockItemsContainer> Containers = new List<BlockItemsContainer>();

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            GetOffSetFunc = GetOffSet;
        }

      
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            FirstPoint = e.GetPosition(this);
            Containers.Add(this);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (FirstPoint != null)
            {
                var currentPoint = e.GetPosition(this);
                if (Math.Sqrt(Math.Pow(FirstPoint.Value.X - currentPoint.X, 2) + Math.Pow(FirstPoint.Value.Y - currentPoint.Y, 2)) > 5)
                {
                    FirstPoint = null;
                    if (Info?.Children.Count > 0)
                    {
                        var borders = VisualHelper.FindVisualChildren<BlockGrid>(this);
                        foreach (var border in borders)
                        {
                            var itemsContainers = VisualHelper.TryFindParent<BlockItemsContainer>(border);
                            if (this != itemsContainers)
                            {
                                continue;
                            }

                            var point = border.TransformToAncestor(this).Transform(new Point(0, 0));
                            var rect = new Rect(point.X, point.Y, border.ActualWidth, border.ActualHeight);
                            if (rect.Contains(currentPoint))
                            {
                                DragObject = border.DataContext as BlockDesignerItemViewModel;
                                DragOffset = new Point(currentPoint.X - point.X, currentPoint.Y - point.Y);
                                break;
                            }
                        }

                        if (DragObject != null)
                        {
                            Containers.ForEach(p => p.FirstPoint = null);
                            DesignerCanvas canvas = GetDesignerCanvas(this);
                            if (canvas != null)
                            {                               
                                canvas.SourceItemsContainer = this;
                                e.Handled = true;
                            }
                        }
                    }
                }
            }
        }
        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);

            FirstPoint = null;

            Containers.Clear();
        }

        public ConnectorOrientation Orientation
        {
            get; set;
        }

        // iterate through visual tree to get parent DesignerCanvas
        private DesignerCanvas GetDesignerCanvas(DependencyObject element)
        {
            while (element != null && !(element is DesignerCanvas))
                element = VisualTreeHelper.GetParent(element);

            return element as DesignerCanvas;
        }

    }
}
