using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using Hawkeye.VisionBuilder.Features.ImagePreview;
using Hawkeye.VisionBuilder.Panels;
using Hawkeye.VisionBuilder.Workflow;
using Hawkeye.VisionBuilder.Workflow.Datatypes;
using Hawkeye.VisionBuilder.Workflow.Datatypes.Elements;
using Hawkeye.VisionBuilder.Workflow.Links;
using Rectangle = System.Drawing.Rectangle;

namespace Hawkeye.VisionBuilder.Features.VisionSteps
{
    public partial class VisionStepsPanel : BasePannel
    {
        private readonly WorkflowList _workflowList;
        private readonly OperationDiscoveryService _discoveryService;
        private readonly ImagePreviewPanel _imagePreviewPanel;

        private BindingSource _bindingSource = new BindingSource();


        public VisionStepsPanel(Workflow.WorkflowList workflowList, OperationDiscoveryService discoveryService,
            ImagePreview.ImagePreviewPanel imagePreviewPanel)
        {
            _workflowList = workflowList;
            _discoveryService = discoveryService;
            _imagePreviewPanel = imagePreviewPanel;

            InitializeComponent();
            dataGridView1.AutoGenerateColumns = false;


            dataGridView1.DataSource = _bindingSource;
            dataGridView1.Columns[0].DataPropertyName = nameof(BaseOperationDecorator.Result);
            dataGridView1.Columns[1].DataPropertyName = nameof(BaseOperationDecorator.IsEnabled);
            dataGridView1.Columns[2].DataPropertyName = nameof(BaseOperationDecorator.Label);
            dataGridView1.Columns[2].ReadOnly = false;
            dataGridView1.ReadOnly = false;

            dataGridView1.Columns[3].DataPropertyName = nameof(BaseOperationDecorator.ResultString);
            dataGridView1.Columns[4].DataPropertyName = nameof(BaseOperationDecorator.TypeName);
            dataGridView1.Columns[5].DataPropertyName = nameof(BaseOperationDecorator.ExecutionTimeString);

            dataGridView1.DefaultCellStyle.SelectionBackColor = Color.Bisque;
            dataGridView1.DefaultCellStyle.SelectionForeColor = Color.Black;
            dataGridView1.CellPainting += DataGridView1_CellPainting;


            dataGridView1.SelectionChanged += DataGridView1_SelectionChanged;




            RefreshWorkflow();

            //dataGridView1.Paint += DataGridView1_Paint;

        }



        public void RefreshWorkflow()
        {
            _bindingSource.Clear();
            foreach (BaseOperation operation in _workflowList.Operations)
            {
                _bindingSource.Add(new BaseOperationDecorator(operation));
            }
        }


        public event Action<BaseOperation> OperationSelected = delegate { };

        private void DataGridView1_SelectionChanged(object? sender, EventArgs e)
        {

        }

        public void UpdateView()
        {
            _imagePreviewPanel.ClearGraphics();
            _imagePreviewPanel.SetImage(_workflowList.Context.ActiveImage);
            _workflowList.Context.GraphicsElements.ForEach(_imagePreviewPanel.AddGraphics);
            _imagePreviewPanel.Refresh();
            Refresh();
        }


        private void DataGridView1_Paint(object? sender, PaintEventArgs e)
        {
           

        }


        private void DataGridView1_CellPainting(object? sender, DataGridViewCellPaintingEventArgs e)
        {
            
            if (e.RowIndex > -1)
            {
                var c1 = dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);
                var row = dataGridView1.Rows[e.RowIndex];
                var rowValue = row.DataBoundItem as BaseOperationDecorator;
                e.PaintBackground(c1, true);
                if (e.ColumnIndex == 0 && e.Value != null)
                {
                    var cx = e.CellBounds.Width / 2 + e.CellBounds.Left;
                    var cy = e.CellBounds.Height / 2 + e.CellBounds.Top;
                    var radius = e.CellBounds.Height / 2 - 3;



                    if ((bool) e.Value)
                    {
                        e.Graphics.FillEllipse(Brushes.Green, cx - radius, cy - radius, radius * 2, radius * 2);
                    }
                    else
                    {
                        e.Graphics.FillEllipse(Brushes.Red, cx - radius, cy - radius, radius * 2, radius * 2);
                    }
                    e.Handled = true;


                }
                
            }
        }

        private static ToolStripMenuItem EnsureCategory(ToolStripItemCollection collection, string category)
        {
            var name = category.Split("/", StringSplitOptions.RemoveEmptyEntries);
            ToolStripMenuItem currentCategory = null;
            foreach (var s in name)
            {
                if (currentCategory == null)
                {
                    if (!collection.ContainsKey(s))
                    {
                        currentCategory = new ToolStripMenuItem()
                        {
                            Name = s,
                            Text = s
                        };
                        collection.Add(currentCategory);
                    }
                    else
                    {
                        currentCategory = collection[s] as ToolStripMenuItem;
                    }
                }
                else
                {
                    if (!currentCategory.DropDownItems.ContainsKey(s))
                    {
                        var newCategory = new ToolStripMenuItem()
                        {
                            Name = s,
                            Text = s
                        };
                        currentCategory.DropDownItems.Add(newCategory);
                        currentCategory = newCategory;
                    }
                    else
                    {
                        currentCategory = currentCategory.DropDownItems[s] as ToolStripMenuItem;
                    }
                }
            }

            return currentCategory!;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var factories = _discoveryService.DiscoverOperations();
            var context = new ContextMenuStrip();

            foreach (OperationDescription desc in factories)
            {
                var descCategory = desc.Category;
                ToolStripMenuItem currentCategory = EnsureCategory(context.Items, descCategory);

                currentCategory.DropDownItems.Add(desc.Name).Click += (o, args) =>
                {
                    var operation = _discoveryService.CreateInstance(desc.Type);
                    _workflowList.Operations.Add(operation);
                    int num = 1;
                    while (_workflowList.Operations.Any(x => x.Label == operation.Label + num)) num++;//generate unique name


                    operation.Label += "" + num;
                    _bindingSource.Add(new BaseOperationDecorator(operation));

                    dataGridView1.ClearSelection();
                    dataGridView1.Rows[^1].Selected = true;
                    ProcessRowSelection();
                };
            }

            context.Show(button1, button1.Location);
        }

        private void btnTest_Click(object sender, EventArgs e)
        {
            ExecuteWorkflow();
        }
        public WorkflowList WorkflowList => _workflowList;
        public void ExecuteWorkflow()
        {
            _workflowList.Context = new Context();
            var sw = Stopwatch.StartNew();
            _workflowList.Operations.ForEach(x => x.NeedsUpdate = true);
            _workflowList.Execute();
            sw.Stop();
            lblProcessingTime.Text = sw.ElapsedMilliseconds.ToString() + " ms";
            _imagePreviewPanel.SetImage(_workflowList.Context.ActiveImage);
            _imagePreviewPanel.ClearGraphics();
            _workflowList.Context.GraphicsElements.ForEach(_imagePreviewPanel.AddGraphics);
            Refresh();
        }

        private void dataGridView1_MouseUp(object sender, MouseEventArgs e)
        {
            ProcessRowSelection();
        }


        public BaseOperation? GetSelectedOperation()
        {
            if (dataGridView1.SelectedRows.Count == 0) return null;
            var rowIndex = dataGridView1.SelectedRows[0].Index;
            return (_bindingSource[rowIndex] as BaseOperationDecorator)?.Operation;
        }

        public void ProcessRowSelection()
        {
            if (dataGridView1.SelectedRows.Count == 0) return;
            var rowIndex = dataGridView1.SelectedRows[0].Index;
            var x = (_bindingSource[rowIndex] as BaseOperationDecorator);
            OperationSelected.Invoke(x.Operation);
            _imagePreviewPanel.ClearGraphics();
            foreach (BaseOperation operation in _workflowList.Operations)
            {
                if (operation.NeedsUpdate) _workflowList.ExecuteOne(operation);
                if (operation == x.Operation) break;
            }

            UpdateView();
        }



        private void dataGridView1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                if (dataGridView1.SelectedRows.Count == 0) return;
                var rowIndex = dataGridView1.SelectedRows[0].Index;
                _bindingSource.RemoveAt(rowIndex);
                _workflowList.Operations.RemoveAt(rowIndex);
                if (rowIndex > 0)
                {
                    dataGridView1.ClearSelection();
                    dataGridView1.Rows[rowIndex - 1].Selected = true;
                    ProcessRowSelection();
                }
            }
        }

        private Rectangle dragBoxFromMouseDown;
        private int rowIndexFromMouseDown;
        private int rowIndexOfItemUnderMouseToDrop;

        private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
        {
            if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
            {
                // If the mouse moves outside the rectangle, start the drag.
                if (dragBoxFromMouseDown != Rectangle.Empty &&
                    !dragBoxFromMouseDown.Contains(e.X, e.Y))
                {

                    // Proceed with the drag and drop, passing in the list item.                    
                    DragDropEffects dropEffect = dataGridView1.DoDragDrop(
                        dataGridView1.Rows[rowIndexFromMouseDown],
                        DragDropEffects.Move);
                }
            }
        }

        private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
        {
            // Get the index of the item the mouse is below.
            rowIndexFromMouseDown = dataGridView1.HitTest(e.X, e.Y).RowIndex;
            if (rowIndexFromMouseDown != -1)
            {
                // Remember the point where the mouse down occurred. 
                // The DragSize indicates the size that the mouse can move 
                // before a drag event should be started.                
                Size dragSize = SystemInformation.DragSize;

                // Create a rectangle using the DragSize, with the mouse position being
                // at the center of the rectangle.
                dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2),
                        e.Y - (dragSize.Height / 2)),
                    dragSize);
            }
            else
                // Reset the rectangle if the mouse is not over an item in the ListBox.
                dragBoxFromMouseDown = Rectangle.Empty;
        }

        private void dataGridView1_DragOver(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Move;
        }

        private void dataGridView1_DragDrop(object sender, DragEventArgs e)
        {
            // The mouse locations are relative to the screen, so they must be 
            // converted to client coordinates.
            Point clientPoint = dataGridView1.PointToClient(new Point(e.X, e.Y));

            // Get the row index of the item the mouse is below. 
            rowIndexOfItemUnderMouseToDrop =
                dataGridView1.HitTest(clientPoint.X, clientPoint.Y).RowIndex;

            var movable = _workflowList.Operations[rowIndexFromMouseDown];
            _workflowList.Operations.RemoveAt(rowIndexFromMouseDown);
            if (rowIndexOfItemUnderMouseToDrop == -1) rowIndexOfItemUnderMouseToDrop = _workflowList.Operations.Count;

            _workflowList.Operations.Insert(rowIndexOfItemUnderMouseToDrop, movable);
            dataGridView1.ClearSelection();
            RefreshWorkflow();
            dataGridView1.Rows[rowIndexOfItemUnderMouseToDrop].Selected = true;
            ProcessRowSelection();

        }
    }
}
