using System;
using System.Drawing;
using System.Windows.Forms;
using FastReport.Data;
using FastReport.Table;
using FastReport.Utils;

namespace FastReport.AdvMatrix
{
    public partial class AdvMatrixObject
    {
        #region Fields
        internal DragInfo dragInfo = new DragInfo();
        #endregion

        #region Properties
        private bool DragCellMode
        {
            get { return dragInfo.SelectedCell != null && dragInfo.DragSelectedCell; }
        }
        #endregion

        #region Private Methods
        #endregion

        #region Public Methods
        /// <inheritdoc/>
        public override void SelectionChanged()
        {
            base.SelectionChanged();

            dragInfo.SelectedCell = null;
            dragInfo.Source = MatrixElement.None;
            if (!IsSelected || Report.Designer.SelectedObjects.Count != 1)
                return;

            TableCell cell = Report.Designer.SelectedObjects[0] as TableCell;
            if (cell == null)
                return;

            MatrixElement el = cell.GetMatrixElement();
            if (el == MatrixElement.Column || el == MatrixElement.Row)
            {
                dragInfo.SelectedCell = cell;
                dragInfo.Source = el;
            }
        }

        /// <inheritdoc/>
        public override void HandleMouseDown(FRMouseEventArgs e)
        {
            if (DragCellMode)
            {
                e.handled = true;
                e.mode = WorkspaceMode2.Custom;
                e.activeObject = this;
            }
            else
                base.HandleMouseDown(e);
        }

        /// <inheritdoc/>
        public override void HandleMouseMove(FRMouseEventArgs e)
        {
            if (DragCellMode && e.button == MouseButtons.Left)
            {
                HandleDragOver(e);
            }
            else
            {
                base.HandleMouseMove(e);

                if (dragInfo.SelectedCell != null && e.button == MouseButtons.None)
                {
                    PointF point = new PointF(e.x, e.y);
                    RectangleF innerRect = dragInfo.SelectedCell.AbsBounds;
                    RectangleF outerRect = innerRect;
                    innerRect.Inflate(-3, -3);
                    outerRect.Inflate(3, 3);

                    dragInfo.DragSelectedCell = outerRect.Contains(point) && !innerRect.Contains(point);
                    if (dragInfo.DragSelectedCell)
                    {
                        e.DragSource = dragInfo.SelectedCell;
                        e.handled = true;
                        e.cursor = Cursors.SizeAll;
                    }
                }
            }
        }

        /// <inheritdoc/>
        public override void HandleMouseUp(FRMouseEventArgs e)
        {
            if (DragCellMode)
            {
                HandleDragDrop(e);
                Report.Designer.SetModified(this, "Change");
                Report.Designer.SelectedObjects.Clear();
                Report.Designer.SelectedObjects.Add(dragInfo.SelectedCell);
                dragInfo.DragSelectedCell = false;
            }
            else
                base.HandleMouseUp(e);
        }

        /// <inheritdoc/>
        public override void HandleDragOver(FRMouseEventArgs e)
        {
            // matrix that is defined in the base report cannot be configured this way
            if (IsAncestor)
                return;

            dragInfo.Target = MatrixElement.None;
            dragInfo.TargetCell = null;
            dragInfo.Indicator = Rectangle.Empty;
            if (!(e.DragSource is TextObject))
                return;

            PointF point = new PointF(e.x, e.y);
            for (int x = 0; x < Columns.Count; x++)
            {
                for (int y = 0; y < Rows.Count; y++)
                {
                    TableCell cell = this[x, y];
                    if (cell != e.DragSource && cell.PointInObject(point))
                    {
                        MatrixElement el = cell.GetMatrixElement();
                        if (el == MatrixElement.Column)
                        {
                            dragInfo.SetHeaderTargetInfo(cell, point);
                            e.dragMessage = Res.Get("ComponentsMisc,Matrix,NewColumn");
                        }
                        else if (el == MatrixElement.Row)
                        {
                            dragInfo.SetHeaderTargetInfo(cell, point);
                            e.dragMessage = Res.Get("ComponentsMisc,Matrix,NewRow");
                        }
                        else if (el == MatrixElement.Cell && dragInfo.Source == MatrixElement.None)
                        {
                            dragInfo.SetCellTargetInfo(cell, point);
                            e.dragMessage = Res.Get("ComponentsMisc,Matrix,NewCell");
                        }
                        else if (dragInfo.Source == MatrixElement.None)
                        {
                            dragInfo.Target = el;
                            dragInfo.TargetCell = cell;
                        }

                        e.handled = true;
                        return;
                    }
                }
            }
            e.handled = PointInObject(point);
        }

        /// <inheritdoc/>
        public override void HandleDragDrop(FRMouseEventArgs e)
        {
            if (e.DragSource == null)
                return;

            TextObject draggedText = e.DragSource as TextObject;

            if (dragInfo.Target == MatrixElement.Column || dragInfo.Target == MatrixElement.Row)
            {
                HeaderDescriptor sourceDescr = dragInfo.SelectedCell != null ? dragInfo.SelectedCell.GetHeaderDescriptor() : null;
                HeaderDescriptor targetDescr = dragInfo.TargetCell.GetHeaderDescriptor();
                HeaderDescriptor newItem = sourceDescr;
                HeaderInsertPosition position = dragInfo.InsertPosition;

                if (sourceDescr == null)
                {
                    // we're inserting a new item from the Data window
                    newItem = new HeaderDescriptor();
                    newItem.Expression = draggedText.Text;
                }
                else
                {
                    // we're reordering header items

                    // Whole descriptor tree can be inserted before or after target item, or as a child if there is no children in the target.
                    // Also target should not be a child of source. In other cases insert selected descriptor only
                    bool canDragItemTree = !targetDescr.HasParent(sourceDescr) &&
                        position != HeaderInsertPosition.Parent &&
                        (position != HeaderInsertPosition.Child || targetDescr.Items.Count == 0);

                    // Delete source descriptor from its current position.
                    if (canDragItemTree)
                        sourceDescr.DeleteTree();
                    else
                        sourceDescr.DeleteThis();

                    // item from another header: copy template items to fill in the cell area properly
                    if (dragInfo.Source != dragInfo.Target)
                    {
                        newItem.TemplateColumn = targetDescr.TemplateColumn;
                        newItem.TemplateRow = targetDescr.TemplateRow;
                    }
                }
                    
                switch (position)
                {
                    case HeaderInsertPosition.After:
                        targetDescr.InsertAfter(newItem);
                        break;
                    case HeaderInsertPosition.Before:
                        targetDescr.InsertBefore(newItem);
                        break;
                    case HeaderInsertPosition.Parent:
                        targetDescr.InsertParent(newItem);
                        break;
                    case HeaderInsertPosition.Child:
                        targetDescr.InsertChild(newItem);
                        break;
                    case HeaderInsertPosition.Inside:
                        targetDescr.InsertAfter(newItem);
                        targetDescr.DeleteThis();
                        break;
                }

                BuildTemplate();
                FixParentHeight();
                e.dragTarget = newItem.TemplateCell;
                dragInfo.SelectedCell = newItem.TemplateCell;
                if (sourceDescr == null)
                    newItem.TemplateCell.Format = draggedText.Format;
            }
            else if (dragInfo.Target == MatrixElement.Cell)
            {
                TableCell cell = dragInfo.TargetCell;
                switch (dragInfo.Position)
                {
                    case DragPosition.Right:
                        cell.InsertAfter(draggedText);
                        break;
                    case DragPosition.Left:
                        cell.InsertBefore(draggedText);
                        break;
                    case DragPosition.Top:
                        cell.InsertAbove(draggedText);
                        break;
                    case DragPosition.Bottom:
                        cell.InsertBelow(draggedText);
                        break;
                    case DragPosition.Inside:
                        cell.InsertInside(draggedText);
                        break;
                }
                FixParentHeight();
            }
            else if (dragInfo.Target == MatrixElement.Corner)
            {
                dragInfo.TargetCell.Text = draggedText.Text;
            }

            if (DataSource == null)
            {
                string text = draggedText.Text;
                if (text.StartsWith("[") && text.EndsWith("]"))
                    text = text.Substring(1, text.Length - 2);
                DataSource = DataHelper.GetDataSource(Report.Dictionary, text);
            }

            dragInfo.Target = MatrixElement.None;
            dragInfo.TargetCell = null;
            dragInfo.Source = MatrixElement.None;
        }
        #endregion
    }
}