using Pilz.Drawing;
using System;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Windows.Forms;

namespace Pilz.UI;


[Serializable]
internal class PaintingObjectResizing
{

    public static event CheckEnabledEventHandler CheckEnabled;

    public delegate void CheckEnabledEventHandler(PaintingObjectResizing sender, ref bool enabled);

    private PaintingObject _mObj;

    private PaintingObject mObj
    {
        [MethodImpl(MethodImplOptions.Synchronized)]
        get
        {
            return _mObj;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        set
        {
            if (_mObj != null)
            {
                _mObj.MouseDown -= mControl_MouseDown;
                _mObj.MouseUp -= mControl_MouseUp;
                _mObj.Paint -= mControl_Paint;
                _mObj.SelectedChanged -= mControl_MouseLeave;
                _mObj.ParentChanged -= mObj_ParentChanged;
            }

            _mObj = value;
            if (_mObj != null)
            {
                _mObj.MouseDown += mControl_MouseDown;
                _mObj.MouseUp += mControl_MouseUp;
                _mObj.Paint += mControl_Paint;
                _mObj.SelectedChanged += mControl_MouseLeave;
                _mObj.ParentChanged += mObj_ParentChanged;
            }
        }
    }
    private Control _mObjParent;

    private Control mObjParent
    {
        [MethodImpl(MethodImplOptions.Synchronized)]
        get
        {
            return _mObjParent;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        set
        {
            _mObjParent = value;
        }
    }
    private Control _mObjControl;

    private Control mObjControl
    {
        [MethodImpl(MethodImplOptions.Synchronized)]
        get
        {
            return _mObjControl;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        set
        {
            if (_mObjControl != null)
            {
                _mObjControl.MouseMove -= mControl_MouseMove;
                _mObjControl.ParentChanged -= mObjParent_ParentChanged;
            }

            _mObjControl = value;
            if (_mObjControl != null)
            {
                _mObjControl.MouseMove += mControl_MouseMove;
                _mObjControl.ParentChanged += mObjParent_ParentChanged;
            }
        }
    }
    private bool mMouseDown = false;
    private EdgeEnum mEdge = EdgeEnum.None;
    private int mWidth = 4;
    private int qWidth = 4 * 4;
    private Rectangle rect = new Rectangle();

    public bool Enabled { get; set; } = true;
    public SizeF MinimumSize { get; set; } = new SizeF(15f, 15f);

    [Serializable]
    private enum EdgeEnum
    {
        None,
        Right,
        Left,
        Top,
        Bottom,
        TopLeft,
        TopRight,
        BottomLeft,
        BottomRight
    }

    public bool IsResizing
    {
        get
        {
            return mMouseDown && mEdge != EdgeEnum.None;
        }
    }

    public PaintingObject PaintingObject
    {
        get
        {
            return mObj;
        }
    }

    public PaintingObjectResizing(PaintingObject obj)
    {
        mObjParent = null;
        mObjControl = null;
        mObj = obj;
        mObjControl = mObj.Parent;
    }

    private bool IsEnabled()
    {
        bool enabled = Enabled;

        CheckEnabled?.Invoke(this, ref enabled);

        return enabled;
    }

    private void mControl_MouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
            mMouseDown = true;
    }

    private void mControl_MouseUp(object sender, MouseEventArgs e)
    {
        mMouseDown = false;

        if (mObj.Selected)
            mObj.AutoArrangeToGrid();
    }

    private void KeepInRange(ref SizeF size)
    {
        if (size.Height < MinimumSize.Height || size.Width < MinimumSize.Width)
            size = new SizeF(Math.Max(size.Width, MinimumSize.Width), Math.Max(size.Height, MinimumSize.Height));
    }

    private void mControl_MouseMove(object sender, MouseEventArgs e)
    {
        if (mMouseDown && mEdge != EdgeEnum.None)
        {

            int eX = (int)Math.Round(e.X + mObj.Parent.Offset.X);
            int eY = (int)Math.Round(e.Y + mObj.Parent.Offset.Y);

            switch (mEdge)
            {
                case EdgeEnum.TopLeft:
                    {
                        mObj.SetBounds(eX, eY, (int)Math.Round(mObj.Width + (mObj.Left - eX)), (int)Math.Round(mObj.Height + (mObj.Top - eY)));
                        break;
                    }
                case EdgeEnum.TopRight:
                    {
                        mObj.SetBounds(mObj.Left, eY, eX - mObj.Left, (int)Math.Round(mObj.Height + (mObj.Top - eY)));
                        break;
                    }
                case EdgeEnum.BottomRight:
                    {
                        mObj.SetBounds(mObj.Left, mObj.Top, eX - mObj.Left, eY - mObj.Top);
                        break;
                    }
                case EdgeEnum.BottomLeft:
                    {
                        mObj.SetBounds(eX, mObj.Top, (int)Math.Round(mObj.Width + (mObj.Left - eX)), eY - mObj.Top);
                        break;
                    }
                case EdgeEnum.Left:
                    {
                        mObj.SetBounds(eX, mObj.Top, (int)Math.Round(mObj.Width + (mObj.Left - eX)), (int)Math.Round(mObj.Height));
                        break;
                    }
                case EdgeEnum.Right:
                    {
                        mObj.SetBounds(mObj.Left, mObj.Top, eX - mObj.Left, (int)Math.Round(mObj.Height));
                        break;
                    }
                case EdgeEnum.Top:
                    {
                        mObj.SetBounds(mObj.Left, eY, (int)Math.Round(mObj.Width), (int)Math.Round(mObj.Height + (mObj.Top - eY)));
                        break;
                    }
                case EdgeEnum.Bottom:
                    {
                        mObj.SetBounds(mObj.Left, mObj.Top, (int)Math.Round(mObj.Width), eY - mObj.Top);
                        break;
                    }
            }

            var argsize = mObj.Size;
            KeepInRange(ref argsize);
            mObj.Size = argsize;
        }

        else if (!mMouseDown)
        {

            int eXo = e.X;
            int eYo = e.Y;
            int realX = (int)Math.Round(eXo + mObj.Parent.Offset.X);
            int realY = (int)Math.Round(eYo + mObj.Parent.Offset.Y);
            int eXwo = (int)Math.Round(eXo - mObj.X);
            int eYwo = (int)Math.Round(eYo - mObj.Y);
            int eX = (int)Math.Round(eXwo + mObj.Parent.Offset.X);
            int eY = (int)Math.Round(eYwo + mObj.Parent.Offset.Y);
            var eLocation = new Point(eX, eY);
            RectangleF extRect = mObj.RectangleExtended;
            var oldRect = mObj.Rectangle;

            var newRect = new RectangleF();
            newRect.X = extRect.X - oldRect.X;
            newRect.Y = extRect.Y - oldRect.Y;
            newRect.Width = (extRect.Width - oldRect.Width) / 2f;
            newRect.Height = (extRect.Height - oldRect.Height) / 2f;

            bool setToNone = false;
            bool isOnTop = ReferenceEquals(mObj.Parent.GetObject(new PointF(realX, realY), true), mObj);

            if (IsEnabled() && isOnTop)
            {
                switch (true)
                {
                    case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(newRect.X), (int)Math.Round(newRect.Y), (int)Math.Round(newRect.Width), (int)Math.Round(newRect.Height))):
                        {
                            mObj.Cursor = Cursors.SizeNWSE;
                            mEdge = EdgeEnum.TopLeft;
                            break;
                        }
                    case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(mObj.Width), (int)Math.Round(newRect.Y), (int)Math.Round(newRect.Width), (int)Math.Round(newRect.Height))):
                        {
                            mObj.Cursor = Cursors.SizeNESW;
                            mEdge = EdgeEnum.TopRight;
                            break;
                        }
                    case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(mObj.Width), (int)Math.Round(mObj.Height), (int)Math.Round(newRect.Width), (int)Math.Round(newRect.Height))):
                        {
                            mObj.Cursor = Cursors.SizeNWSE;
                            mEdge = EdgeEnum.BottomRight;
                            break;
                        }
                    case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(newRect.X), (int)Math.Round(mObj.Height), (int)Math.Round(newRect.Width), (int)Math.Round(newRect.Height))):
                        {
                            mObj.Cursor = Cursors.SizeNESW;
                            mEdge = EdgeEnum.BottomLeft;
                            break;
                        }
                    case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(-newRect.Width), 0, (int)Math.Round(newRect.Width), (int)Math.Round(mObj.Height))):
                        {
                            mObj.Cursor = Cursors.SizeWE;
                            mEdge = EdgeEnum.Left;
                            break;
                        }
                    case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle((int)Math.Round(mObj.Width), 0, (int)Math.Round(newRect.Width), (int)Math.Round(mObj.Height))):
                        {
                            mObj.Cursor = Cursors.SizeWE;
                            mEdge = EdgeEnum.Right;
                            break;
                        }
                    case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle(0, (int)Math.Round(-newRect.Height), (int)Math.Round(mObj.Width), (int)Math.Round(newRect.Height))):
                        {
                            mObj.Cursor = Cursors.SizeNS;
                            mEdge = EdgeEnum.Top;
                            break;
                        }
                    case object _ when HelpfulDrawingFunctions.IsPointInRectangle(eLocation, new Rectangle(0, (int)Math.Round(mObj.Height), (int)Math.Round(mObj.Width), (int)Math.Round(newRect.Height))):
                        {
                            mObj.Cursor = Cursors.SizeNS;
                            mEdge = EdgeEnum.Bottom;
                            break;
                        }

                    default:
                        {
                            setToNone = true;
                            break;
                        }
                }
            }
            else
            {
                setToNone = true;
            }

            if (setToNone)
            {
                mObj.Cursor = Cursors.Default;
                mEdge = EdgeEnum.None;
            }
        }
    }

    private void mControl_Paint(PaintingObject sender, PaintEventArgs e)
    {
        // e.Graphics.FillRectangle(brush, rect)
    }

    private void mControl_MouseLeave(PaintingObject sender, EventArgs e)
    {
        if (!sender.Selected)
            mEdge = EdgeEnum.None;
    }

    private void mObjParent_ParentChanged(object sender, EventArgs e)
    {
        mObjParent = mObjControl.Parent;
    }

    private void mObj_ParentChanged(PaintingObject sender, EventArgs e)
    {
        mObjControl = mObj.Parent;
        mObjParent = mObjControl?.Parent;
    }

}