using OpenNest.CNC.CuttingStrategy;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace OpenNest.Controls
{
    public class CuttingPanel : Panel
    {
        private static readonly string[] LeadInTypes =
            { "None", "Line", "Arc", "Line + Arc", "Clean Hole", "Line + Line" };

        private static readonly string[] LeadOutTypes =
            { "None", "Line", "Arc" };

        private readonly TabControl tabControl;
        private readonly ComboBox cboExternalLeadIn, cboExternalLeadOut;
        private readonly ComboBox cboInternalLeadIn, cboInternalLeadOut;
        private readonly ComboBox cboArcCircleLeadIn, cboArcCircleLeadOut;

        private readonly Panel pnlExternalLeadIn, pnlExternalLeadOut;
        private readonly Panel pnlInternalLeadIn, pnlInternalLeadOut;
        private readonly Panel pnlArcCircleLeadIn, pnlArcCircleLeadOut;

        private readonly CheckBox chkTabsEnabled;
        private readonly NumericUpDown nudTabWidth;
        private readonly RadioButton rbTabAll;
        private readonly RadioButton rbAutoTab;
        private readonly NumericUpDown nudAutoTabMin;
        private readonly NumericUpDown nudAutoTabMax;
        private readonly NumericUpDown nudPierceClearance;

        private readonly CheckBox chkRoundLeadInAngles;
        private readonly NumericUpDown nudLeadInAngleIncrement;

        private readonly Button btnAutoAssign;

        private bool suppressEvents;

        public event EventHandler ParametersChanged;
        public event EventHandler AutoAssignClicked;

        public bool ShowAutoAssign
        {
            get => btnAutoAssign.Visible;
            set => btnAutoAssign.Visible = value;
        }

        public ContourType? ActiveContourType
        {
            get
            {
                return tabControl.SelectedIndex switch
                {
                    0 => ContourType.External,
                    1 => ContourType.Internal,
                    2 => ContourType.ArcCircle,
                    _ => null
                };
            }
            set
            {
                if (value == null)
                    return;

                var index = value.Value switch
                {
                    ContourType.External => 0,
                    ContourType.Internal => 1,
                    ContourType.ArcCircle => 2,
                    _ => -1
                };

                if (index >= 0 && tabControl.SelectedIndex != index)
                    tabControl.SelectedIndex = index;
            }
        }

        public CuttingPanel()
        {
            AutoScroll = true;
            BackColor = Color.White;

            // Tab control for contour types — wrapped in a fixed-height panel for Dock.Top
            tabControl = new TabControl
            {
                Dock = DockStyle.Fill
            };

            var tabExternal = new TabPage("External") { Padding = new Padding(4) };
            var tabInternal = new TabPage("Internal") { Padding = new Padding(4) };
            var tabArcCircle = new TabPage("Arc / Circle") { Padding = new Padding(4) };

            SetupTab(tabExternal, out cboExternalLeadIn, out pnlExternalLeadIn,
                out cboExternalLeadOut, out pnlExternalLeadOut);
            SetupTab(tabInternal, out cboInternalLeadIn, out pnlInternalLeadIn,
                out cboInternalLeadOut, out pnlInternalLeadOut);
            SetupTab(tabArcCircle, out cboArcCircleLeadIn, out pnlArcCircleLeadIn,
                out cboArcCircleLeadOut, out pnlArcCircleLeadOut);

            tabControl.Controls.Add(tabExternal);
            tabControl.Controls.Add(tabInternal);
            tabControl.Controls.Add(tabArcCircle);

            var tabWrapper = new Panel
            {
                Dock = DockStyle.Top,
                Height = 340
            };
            tabWrapper.Controls.Add(tabControl);

            // Tabs section
            var tabsPanel = new CollapsiblePanel
            {
                HeaderText = "Tabs",
                Dock = DockStyle.Top,
                ExpandedHeight = 160,
                IsExpanded = false
            };

            chkTabsEnabled = new CheckBox
            {
                Text = "Enable Tabs",
                Location = new Point(12, 4),
                AutoSize = true
            };
            tabsPanel.ContentPanel.Controls.Add(chkTabsEnabled);

            tabsPanel.ContentPanel.Controls.Add(new Label
            {
                Text = "Tab Size:",
                Location = new Point(160, 6),
                AutoSize = true
            });

            nudTabWidth = CreateNumeric(225, 3, 0.25, 0.0625);
            nudTabWidth.Enabled = false;
            tabsPanel.ContentPanel.Controls.Add(nudTabWidth);

            rbTabAll = new RadioButton
            {
                Text = "Tab all parts",
                Location = new Point(28, 28),
                AutoSize = true,
                Enabled = false,
                Checked = true
            };
            tabsPanel.ContentPanel.Controls.Add(rbTabAll);

            rbAutoTab = new RadioButton
            {
                Text = "Auto-tab when smallest part dimension is between:",
                Location = new Point(28, 50),
                AutoSize = true,
                Enabled = false
            };
            tabsPanel.ContentPanel.Controls.Add(rbAutoTab);

            tabsPanel.ContentPanel.Controls.Add(new Label
            {
                Text = "Min:",
                Location = new Point(44, 76),
                AutoSize = true
            });

            nudAutoTabMin = CreateNumeric(77, 73, 0, 0.0625);
            nudAutoTabMin.Enabled = false;
            tabsPanel.ContentPanel.Controls.Add(nudAutoTabMin);

            tabsPanel.ContentPanel.Controls.Add(new Label
            {
                Text = "Max:",
                Location = new Point(210, 76),
                AutoSize = true
            });

            nudAutoTabMax = CreateNumeric(245, 73, 0, 0.0625);
            nudAutoTabMax.Enabled = false;
            tabsPanel.ContentPanel.Controls.Add(nudAutoTabMax);

            chkTabsEnabled.CheckedChanged += (s, e) =>
            {
                var enabled = chkTabsEnabled.Checked;
                nudTabWidth.Enabled = enabled;
                rbTabAll.Enabled = enabled;
                rbAutoTab.Enabled = enabled;
                nudAutoTabMin.Enabled = enabled && rbAutoTab.Checked;
                nudAutoTabMax.Enabled = enabled && rbAutoTab.Checked;
                OnParametersChanged();
            };

            rbTabAll.CheckedChanged += (s, e) =>
            {
                nudAutoTabMin.Enabled = chkTabsEnabled.Checked && rbAutoTab.Checked;
                nudAutoTabMax.Enabled = chkTabsEnabled.Checked && rbAutoTab.Checked;
                OnParametersChanged();
            };

            // Pierce section
            var piercePanel = new CollapsiblePanel
            {
                HeaderText = "Pierce",
                Dock = DockStyle.Top,
                ExpandedHeight = 90,
                IsExpanded = true
            };

            piercePanel.ContentPanel.Controls.Add(new Label
            {
                Text = "Pierce Clearance:",
                Location = new Point(12, 6),
                AutoSize = true
            });

            nudPierceClearance = CreateNumeric(130, 3, 0.0625, 0.0625);
            piercePanel.ContentPanel.Controls.Add(nudPierceClearance);

            chkRoundLeadInAngles = new CheckBox
            {
                Text = "Round Lead-In Angles",
                Location = new Point(12, 32),
                AutoSize = true
            };
            chkRoundLeadInAngles.CheckedChanged += (s, e) =>
            {
                nudLeadInAngleIncrement.Enabled = chkRoundLeadInAngles.Checked;
                OnParametersChanged();
            };
            piercePanel.ContentPanel.Controls.Add(chkRoundLeadInAngles);

            piercePanel.ContentPanel.Controls.Add(new Label
            {
                Text = "Increment:",
                Location = new Point(175, 34),
                AutoSize = true
            });

            nudLeadInAngleIncrement = CreateNumeric(245, 31, 5, 1);
            nudLeadInAngleIncrement.DecimalPlaces = 0;
            nudLeadInAngleIncrement.Minimum = 1;
            nudLeadInAngleIncrement.Maximum = 90;
            nudLeadInAngleIncrement.Enabled = false;
            nudLeadInAngleIncrement.ValueChanged += (s, e) => OnParametersChanged();
            piercePanel.ContentPanel.Controls.Add(nudLeadInAngleIncrement);

            // Auto-Assign button — wrapped in a panel for Dock.Top with padding
            btnAutoAssign = new Button
            {
                Text = "Auto-Assign Lead-ins",
                Dock = DockStyle.Top,
                Height = 32,
                Visible = false
            };
            btnAutoAssign.Click += (s, e) => AutoAssignClicked?.Invoke(this, EventArgs.Empty);

            var btnWrapper = new Panel
            {
                Dock = DockStyle.Top,
                Height = 36,
                Padding = new Padding(4, 2, 4, 2)
            };
            btnWrapper.Controls.Add(btnAutoAssign);

            // Add in reverse order — Dock.Top stacks top-down
            Controls.Add(btnWrapper);
            Controls.Add(piercePanel);
            Controls.Add(tabsPanel);
            Controls.Add(tabWrapper);

            // Wire up change events
            PopulateDropdowns();
            WireChangeEvents();
        }

        public CuttingParameters BuildParameters()
        {
            return new CuttingParameters
            {
                ExternalLeadIn = BuildLeadIn(cboExternalLeadIn, pnlExternalLeadIn),
                ExternalLeadOut = BuildLeadOut(cboExternalLeadOut, pnlExternalLeadOut),
                InternalLeadIn = BuildLeadIn(cboInternalLeadIn, pnlInternalLeadIn),
                InternalLeadOut = BuildLeadOut(cboInternalLeadOut, pnlInternalLeadOut),
                ArcCircleLeadIn = BuildLeadIn(cboArcCircleLeadIn, pnlArcCircleLeadIn),
                ArcCircleLeadOut = BuildLeadOut(cboArcCircleLeadOut, pnlArcCircleLeadOut),
                TabsEnabled = chkTabsEnabled.Checked && rbTabAll.Checked,
                TabConfig = new NormalTab { Size = (double)nudTabWidth.Value },
                PierceClearance = (double)nudPierceClearance.Value,
                RoundLeadInAngles = chkRoundLeadInAngles.Checked,
                LeadInAngleIncrement = (double)nudLeadInAngleIncrement.Value,
                AutoTabMinSize = chkTabsEnabled.Checked && rbAutoTab.Checked ? (double)nudAutoTabMin.Value : 0,
                AutoTabMaxSize = chkTabsEnabled.Checked && rbAutoTab.Checked ? (double)nudAutoTabMax.Value : 0
            };
        }

        public void LoadFromParameters(CuttingParameters p)
        {
            suppressEvents = true;

            LoadLeadIn(cboExternalLeadIn, pnlExternalLeadIn, p.ExternalLeadIn);
            LoadLeadOut(cboExternalLeadOut, pnlExternalLeadOut, p.ExternalLeadOut);
            LoadLeadIn(cboInternalLeadIn, pnlInternalLeadIn, p.InternalLeadIn);
            LoadLeadOut(cboInternalLeadOut, pnlInternalLeadOut, p.InternalLeadOut);
            LoadLeadIn(cboArcCircleLeadIn, pnlArcCircleLeadIn, p.ArcCircleLeadIn);
            LoadLeadOut(cboArcCircleLeadOut, pnlArcCircleLeadOut, p.ArcCircleLeadOut);

            var hasAutoTab = p.AutoTabMinSize > 0 || p.AutoTabMaxSize > 0;
            chkTabsEnabled.Checked = p.TabsEnabled || hasAutoTab;
            rbAutoTab.Checked = hasAutoTab;
            rbTabAll.Checked = !hasAutoTab;
            if (p.TabConfig != null)
                nudTabWidth.Value = (decimal)p.TabConfig.Size;
            nudPierceClearance.Value = (decimal)p.PierceClearance;
            chkRoundLeadInAngles.Checked = p.RoundLeadInAngles;
            nudLeadInAngleIncrement.Value = (decimal)p.LeadInAngleIncrement;
            nudLeadInAngleIncrement.Enabled = p.RoundLeadInAngles;
            nudAutoTabMin.Value = (decimal)p.AutoTabMinSize;
            nudAutoTabMax.Value = (decimal)p.AutoTabMaxSize;

            suppressEvents = false;
        }

        private void OnParametersChanged()
        {
            if (!suppressEvents)
                ParametersChanged?.Invoke(this, EventArgs.Empty);
        }

        private static void SetupTab(TabPage tab,
            out ComboBox leadInCombo, out Panel leadInPanel,
            out ComboBox leadOutCombo, out Panel leadOutPanel)
        {
            var grpLeadIn = new GroupBox
            {
                Text = "Lead-In",
                Location = new Point(4, 4),
                Size = new Size(340, 148)
            };
            tab.Controls.Add(grpLeadIn);

            grpLeadIn.Controls.Add(new Label
            {
                Text = "Type:",
                Location = new Point(8, 22),
                AutoSize = true
            });

            leadInCombo = new ComboBox
            {
                DropDownStyle = ComboBoxStyle.DropDownList,
                Location = new Point(90, 19),
                Size = new Size(230, 24)
            };
            grpLeadIn.Controls.Add(leadInCombo);

            leadInPanel = new Panel
            {
                Location = new Point(8, 48),
                Size = new Size(320, 92),
                AutoScroll = true
            };
            grpLeadIn.Controls.Add(leadInPanel);

            var grpLeadOut = new GroupBox
            {
                Text = "Lead-Out",
                Location = new Point(4, 156),
                Size = new Size(340, 132)
            };
            tab.Controls.Add(grpLeadOut);

            grpLeadOut.Controls.Add(new Label
            {
                Text = "Type:",
                Location = new Point(8, 22),
                AutoSize = true
            });

            leadOutCombo = new ComboBox
            {
                DropDownStyle = ComboBoxStyle.DropDownList,
                Location = new Point(90, 19),
                Size = new Size(230, 24)
            };
            grpLeadOut.Controls.Add(leadOutCombo);

            leadOutPanel = new Panel
            {
                Location = new Point(8, 48),
                Size = new Size(320, 76),
                AutoScroll = true
            };
            grpLeadOut.Controls.Add(leadOutPanel);
        }

        private void PopulateDropdowns()
        {
            foreach (var combo in new[] { cboExternalLeadIn, cboInternalLeadIn, cboArcCircleLeadIn })
            {
                combo.Items.AddRange(LeadInTypes);
                combo.SelectedIndex = 0;
            }

            foreach (var combo in new[] { cboExternalLeadOut, cboInternalLeadOut, cboArcCircleLeadOut })
            {
                combo.Items.AddRange(LeadOutTypes);
                combo.SelectedIndex = 0;
            }
        }

        private void WireChangeEvents()
        {
            cboExternalLeadIn.SelectedIndexChanged += OnLeadInTypeChanged;
            cboInternalLeadIn.SelectedIndexChanged += OnLeadInTypeChanged;
            cboArcCircleLeadIn.SelectedIndexChanged += OnLeadInTypeChanged;

            cboExternalLeadOut.SelectedIndexChanged += OnLeadOutTypeChanged;
            cboInternalLeadOut.SelectedIndexChanged += OnLeadOutTypeChanged;
            cboArcCircleLeadOut.SelectedIndexChanged += OnLeadOutTypeChanged;
        }

        private void OnLeadInTypeChanged(object sender, EventArgs e)
        {
            var combo = (ComboBox)sender;
            var panel = GetLeadInPanel(combo);
            if (panel != null)
                BuildLeadInParamControls(panel, combo.SelectedIndex);
            OnParametersChanged();
        }

        private void OnLeadOutTypeChanged(object sender, EventArgs e)
        {
            var combo = (ComboBox)sender;
            var panel = GetLeadOutPanel(combo);
            if (panel != null)
                BuildLeadOutParamControls(panel, combo.SelectedIndex);
            OnParametersChanged();
        }

        private Panel GetLeadInPanel(ComboBox combo)
        {
            if (combo == cboExternalLeadIn) return pnlExternalLeadIn;
            if (combo == cboInternalLeadIn) return pnlInternalLeadIn;
            if (combo == cboArcCircleLeadIn) return pnlArcCircleLeadIn;
            return null;
        }

        private Panel GetLeadOutPanel(ComboBox combo)
        {
            if (combo == cboExternalLeadOut) return pnlExternalLeadOut;
            if (combo == cboInternalLeadOut) return pnlInternalLeadOut;
            if (combo == cboArcCircleLeadOut) return pnlArcCircleLeadOut;
            return null;
        }

        private void BuildLeadInParamControls(Panel panel, int typeIndex)
        {
            panel.Controls.Clear();
            var y = 0;

            switch (typeIndex)
            {
                case 1:
                    AddNumericField(panel, "Length:", 0.25, ref y, "Length");
                    AddNumericField(panel, "Approach Angle:", 90, ref y, "ApproachAngle");
                    break;
                case 2:
                    AddNumericField(panel, "Radius:", 0.25, ref y, "Radius");
                    break;
                case 3:
                    AddNumericField(panel, "Line Length:", 0.25, ref y, "LineLength");
                    AddNumericField(panel, "Arc Radius:", 0.125, ref y, "ArcRadius");
                    AddNumericField(panel, "Approach Angle:", 135, ref y, "ApproachAngle");
                    break;
                case 4:
                    AddNumericField(panel, "Line Length:", 0.25, ref y, "LineLength");
                    AddNumericField(panel, "Arc Radius:", 0.125, ref y, "ArcRadius");
                    AddNumericField(panel, "Kerf:", 0.06, ref y, "Kerf");
                    break;
                case 5:
                    AddNumericField(panel, "Length 1:", 0.25, ref y, "Length1");
                    AddNumericField(panel, "Angle 1:", 90, ref y, "Angle1");
                    AddNumericField(panel, "Length 2:", 0.25, ref y, "Length2");
                    AddNumericField(panel, "Angle 2:", 90, ref y, "Angle2");
                    break;
            }
        }

        private void BuildLeadOutParamControls(Panel panel, int typeIndex)
        {
            panel.Controls.Clear();
            var y = 0;

            switch (typeIndex)
            {
                case 1:
                    AddNumericField(panel, "Length:", 0.25, ref y, "Length");
                    AddNumericField(panel, "Approach Angle:", 90, ref y, "ApproachAngle");
                    break;
                case 2:
                    AddNumericField(panel, "Radius:", 0.25, ref y, "Radius");
                    break;
            }
        }

        private void AddNumericField(Panel panel, string label, double defaultValue,
            ref int y, string tag)
        {
            panel.Controls.Add(new Label
            {
                Text = label,
                Location = new Point(0, y + 3),
                AutoSize = true
            });

            var nud = CreateNumeric(130, y, defaultValue, 0.0625);
            nud.Tag = tag;
            nud.ValueChanged += (s, e) => OnParametersChanged();
            panel.Controls.Add(nud);

            y += 30;
        }

        private static NumericUpDown CreateNumeric(int x, int y, double defaultValue, double increment)
        {
            return new NumericUpDown
            {
                Location = new Point(x, y),
                Size = new Size(120, 22),
                DecimalPlaces = 4,
                Increment = (decimal)increment,
                Minimum = 0,
                Maximum = 9999,
                Value = (decimal)defaultValue
            };
        }

        private static void LoadLeadIn(ComboBox combo, Panel panel, LeadIn leadIn)
        {
            switch (leadIn)
            {
                case LineLeadIn line:
                    combo.SelectedIndex = 1;
                    SetParam(panel, "Length", line.Length);
                    SetParam(panel, "ApproachAngle", line.ApproachAngle);
                    break;
                case ArcLeadIn arc:
                    combo.SelectedIndex = 2;
                    SetParam(panel, "Radius", arc.Radius);
                    break;
                case LineArcLeadIn lineArc:
                    combo.SelectedIndex = 3;
                    SetParam(panel, "LineLength", lineArc.LineLength);
                    SetParam(panel, "ArcRadius", lineArc.ArcRadius);
                    SetParam(panel, "ApproachAngle", lineArc.ApproachAngle);
                    break;
                case CleanHoleLeadIn cleanHole:
                    combo.SelectedIndex = 4;
                    SetParam(panel, "LineLength", cleanHole.LineLength);
                    SetParam(panel, "ArcRadius", cleanHole.ArcRadius);
                    SetParam(panel, "Kerf", cleanHole.Kerf);
                    break;
                case LineLineLeadIn lineLine:
                    combo.SelectedIndex = 5;
                    SetParam(panel, "Length1", lineLine.Length1);
                    SetParam(panel, "Angle1", lineLine.ApproachAngle1);
                    SetParam(panel, "Length2", lineLine.Length2);
                    SetParam(panel, "Angle2", lineLine.ApproachAngle2);
                    break;
                default:
                    combo.SelectedIndex = 0;
                    break;
            }
        }

        private static void LoadLeadOut(ComboBox combo, Panel panel, LeadOut leadOut)
        {
            switch (leadOut)
            {
                case LineLeadOut line:
                    combo.SelectedIndex = 1;
                    SetParam(panel, "Length", line.Length);
                    SetParam(panel, "ApproachAngle", line.ApproachAngle);
                    break;
                case ArcLeadOut arc:
                    combo.SelectedIndex = 2;
                    SetParam(panel, "Radius", arc.Radius);
                    break;
                default:
                    combo.SelectedIndex = 0;
                    break;
            }
        }

        private static LeadIn BuildLeadIn(ComboBox combo, Panel panel)
        {
            return combo.SelectedIndex switch
            {
                1 => new LineLeadIn
                {
                    Length = GetParam(panel, "Length", 0.25),
                    ApproachAngle = GetParam(panel, "ApproachAngle", 90)
                },
                2 => new ArcLeadIn
                {
                    Radius = GetParam(panel, "Radius", 0.25)
                },
                3 => new LineArcLeadIn
                {
                    LineLength = GetParam(panel, "LineLength", 0.25),
                    ArcRadius = GetParam(panel, "ArcRadius", 0.125),
                    ApproachAngle = GetParam(panel, "ApproachAngle", 135)
                },
                4 => new CleanHoleLeadIn
                {
                    LineLength = GetParam(panel, "LineLength", 0.25),
                    ArcRadius = GetParam(panel, "ArcRadius", 0.125),
                    Kerf = GetParam(panel, "Kerf", 0.06)
                },
                5 => new LineLineLeadIn
                {
                    Length1 = GetParam(panel, "Length1", 0.25),
                    ApproachAngle1 = GetParam(panel, "Angle1", 90),
                    Length2 = GetParam(panel, "Length2", 0.25),
                    ApproachAngle2 = GetParam(panel, "Angle2", 90)
                },
                _ => new NoLeadIn()
            };
        }

        private static LeadOut BuildLeadOut(ComboBox combo, Panel panel)
        {
            return combo.SelectedIndex switch
            {
                1 => new LineLeadOut
                {
                    Length = GetParam(panel, "Length", 0.25),
                    ApproachAngle = GetParam(panel, "ApproachAngle", 90)
                },
                2 => new ArcLeadOut
                {
                    Radius = GetParam(panel, "Radius", 0.25)
                },
                _ => new NoLeadOut()
            };
        }

        private static void SetParam(Panel panel, string tag, double value)
        {
            foreach (Control c in panel.Controls)
            {
                if (c is NumericUpDown nud && (string)nud.Tag == tag)
                {
                    nud.Value = (decimal)value;
                    return;
                }
            }
        }

        private static double GetParam(Panel panel, string tag, double defaultValue)
        {
            foreach (Control c in panel.Controls)
            {
                if (c is NumericUpDown nud && (string)nud.Tag == tag)
                    return (double)nud.Value;
            }
            return defaultValue;
        }
    }
}
