using System;
using System.Collections.Generic;
using EmreIstikam001.component.TehranOffice.ToosForAll;
using EmreIstikam001.Properties;
using Grasshopper.Kernel;
using Rhino;
using Rhino.Geometry;
using Rhino.Input.Custom;
using Rhino.Input;
using Bitmap = System.Drawing.Bitmap;
using Color = System.Drawing.Color;
using Rhino.Display;
using EmreIstikam001.component.TehranOffice.ZAbsolute.Tools;
using static EmreIstikam001.component.TehranOffice.Components.ÇİZİM_YARDIMCILARI.DetailSymbol.DetailSymbolGH2;
using Rhino.DocObjects;
using Rhino.UI;

namespace EmreIstikam001.component.TehranOffice.Components.ÇİZİM_YARDIMCILARI.DetailSymbol
{
    public class DetailSymbolGH : BaseGhComponent
    {
        
        private readonly string BlockName = "Detay İşareti_0";
        private bool Bake = false;
        private List<GeometryBase> FinalGeometries = new List<GeometryBase>();

        //private readonly DetailSymbolFRM frm = new DetailSymbolFRM();


        // Fields
        private FormManager formManager;
        private RhinoInteractionHandler rhinoInteractionHandler;
        private DetailSymbolConduit conduit;


        // Constructor
        public DetailSymbolGH()
            : base("DETAIL SYMBOL", "DS", "DETAIL SYMBOL", "PARDUS", "ÇİZİM YARDIMCILARI")
        {
            // Initialize the form manager and rhino interaction handler
            formManager = new FormManager();
            conduit = new DetailSymbolConduit();
            rhinoInteractionHandler = new RhinoInteractionHandler(conduit);
        }
        // Overriding methods
        protected override void RegisterInputParams(GH_InputParamManager pManager)
        {
        }
        protected override void RegisterOutputParams(GH_OutputParamManager pManager)
        {
        }
        public override void AddedToDocument(GH_Document document)
        {
            base.AddedToDocument(document);

            // Show the form
            formManager.ShowForm();

            // Subscribe to form events
            formManager.ScaleChanged += OnScaleChanged;
            formManager.AxIdChanged += OnAxIdChanged;
            formManager.GetPointsClicked += OnGetPointsClicked;
            formManager.BakeClicked += OnBakeClicked;
            formManager.FormClosed += OnFormClosed;
            formManager.Closed += OnFormClosed;
        }

        public override void RemovedFromDocument(GH_Document document)
        {
            base.RemovedFromDocument(document);

            //Unsubscribe from form events
            formManager.ScaleChanged -= OnScaleChanged;
            formManager.AxIdChanged -= OnAxIdChanged;
            formManager.GetPointsClicked -= OnGetPointsClicked;
            formManager.BakeClicked -= OnBakeClicked;
            formManager.FormClosed -= OnFormClosed;
            formManager.Closed -= OnFormClosed;

            // Dispose of the form and conduit
            formManager.Dispose();
            conduit.Disable();
        }

        //// Event Handlers
        ///
        /// 
        private void OnScaleChanged(object sender, double newScale)
        {
            rhinoInteractionHandler.Scale = newScale;
            rhinoInteractionHandler.RecalculateGeometryData();
        }

        private void OnAxIdChanged(object sender, string newAxId)
        {
            rhinoInteractionHandler.AxId = newAxId;
            rhinoInteractionHandler.RecalculateGeometryData();
        }

        private void OnGetPointsClicked(object sender, EventArgs e)
        {
            // Disable the conduit to stop the preview
            conduit.Disable();
            // Initiate point picking in Rhino
            rhinoInteractionHandler.GetPointsFromUser(conduit);
            // Expire the solution to trigger a redraw
            RhinoDoc.ActiveDoc.Views.Redraw();
            ExpireSolution(true);
        }

        private void OnBakeClicked(object sender, EventArgs e)
        {
            Bake = true;

            // Expire the solution to trigger SolveInstance
            ExpireSolution(true);
        }

        private void OnFormClosed(object sender, EventArgs e)
        {
            // Dispose of the form and conduit
            formManager.Dispose();
            conduit.Disable();

            RhinoDoc.ActiveDoc.Views.Redraw();
            // Expire the solution to update the display
        }
        protected override void AuthenticatedSolveInstance(IGH_DataAccess DA)
        {
            FinalGeometries.Clear();
            var doc = RhinoDoc.ActiveDoc;
            // Update the conduit geometry based on the interaction handler's data
            var geometryData = rhinoInteractionHandler.GetGeometryData();
            conduit.UpdateGeometry(geometryData);
            RhinoDoc.ActiveDoc.Views.Redraw();

            // Redraw the viewport to reflect the changes
            RhinoDoc.ActiveDoc.Views.Redraw();

            FinalGeometries.AddRange(geometryData.DashedCurves);
            FinalGeometries.AddRange(geometryData.FilletRectangles);
            FinalGeometries.AddRange(geometryData.Leaders);
            FinalGeometries.AddRange(geometryData.Texts);

            if (Bake)
            {
                // Bake the geometry
                var BakeGeometries = new BakeGeometry();
                BakeGeometries.BakeGeometries(FinalGeometries, BlockName, rhinoInteractionHandler.Scale.ToString());

                // Reset the Bake flag
                Bake = false;

                // Close the form and disable the preview
                formManager.Dispose();
                conduit.Disable();
                RhinoDoc.ActiveDoc.Views.Redraw();
                // Expire the solution to update the display
            }

           
        }

        protected override Bitmap Icon => Resources.AksBalonuCi̇zme;
        protected override string CodeName => "DETAY_ISARETI";
        protected override GH_Exposure? ValidExposure => GH_Exposure.quarternary;
        public override Guid ComponentGuid => new Guid("3f706da9-1d53-4b13-b421-93083f5de516");
    }
}

public class FormManager : IDisposable
{
    // Events
    public event EventHandler<double> ScaleChanged;
    public event EventHandler<string> AxIdChanged;
    public event EventHandler GetPointsClicked;
    public event EventHandler FormClosed;
    public event EventHandler Closed;
    public event EventHandler BakeClicked;

    // Private fields
    private DetailSymbolFRM form;

    public FormManager()
    {
        form = new DetailSymbolFRM();
        // Subscribe to form events
        form.cmb1.TextChanged += OnScaleTextChanged;
        form.txt1.TextChanged += OnAxIdTextChanged;
        form.btnInoutData.Click += OnGetPointsButtonClicked;
        form.FormClosed += OnFormClosed;
        form.FormClosing += OnFormClosing;
        //form.btnDispose.Click += OnDisposeButtonClicked;
        form.btnBake.Click += OnBakeButtonClicked;
        form.Closed += OnFormClosed;
    }

    public void ShowForm()
    {
        form.Show();
    }

    private void OnFormClosing(object sender, FormClosingEventArgs e)
    {
        FormClosed?.Invoke(this, EventArgs.Empty);
    }

    private void OnScaleTextChanged(object sender, EventArgs e)
    {
        if (double.TryParse(form.cmb1.Text, out double newScale))
        {
            ScaleChanged?.Invoke(this, newScale);
        }
    }

    private void OnAxIdTextChanged(object sender, EventArgs e)
    {
        AxIdChanged?.Invoke(this, form.txt1.Text);
    }

    private void OnGetPointsButtonClicked(object sender, EventArgs e)
    {
        GetPointsClicked?.Invoke(this, EventArgs.Empty);
    }

    private void OnBakeButtonClicked(object sender, EventArgs e)
    {
        BakeClicked?.Invoke(this, EventArgs.Empty);
    }

    private void OnFormClosed(object sender, EventArgs e)
    {
        FormClosed?.Invoke(this, EventArgs.Empty);
    }

    private void OnDisposeButtonClicked(object sender, EventArgs e)
    {
        FormClosed?.Invoke(this, EventArgs.Empty);
    }

    public void Dispose()
    {
        // Unsubscribe from form events
        form.cmb1.TextChanged -= OnScaleTextChanged;
        form.txt1.TextChanged -= OnAxIdTextChanged;
        form.btnInoutData.Click -= OnGetPointsButtonClicked;
        form.FormClosing -= OnFormClosing;
        form.FormClosed -= OnFormClosed;
        form.Closed -= OnFormClosed;
        //form.btnDispose.Click -= OnDisposeButtonClicked;

        form.Close();
        form.Dispose();
    }
}


 public static class RhinoGetPointHelper
 {
     /// <summary>
     /// Allows the user to select two points to define a rectangle.
     /// </summary>
     /// <param name="prompt1">The prompt for the first corner.</param>
     /// <param name="prompt2">The prompt for the opposite corner.</param>
     /// <returns>A Polyline representing the rectangle, or null if cancelled.</returns>
     public static Polyline GetRectangleFromUser(string prompt1, string prompt2)
     {
         var doc = RhinoDoc.ActiveDoc;
         var points = new List<Point3d>();
         Point3d pt0;

         // Get the first corner of the rectangle
         using (var getPointAction = new GetPoint())
         {
             getPointAction.SetCommandPrompt(prompt1);
             if (getPointAction.Get() != GetResult.Point)
             {
                 RhinoApp.WriteLine("No point was selected.");
                 return null;
             }
             pt0 = getPointAction.Point();
         }

         Point3d pt1;

         // Get the opposite corner with dynamic drawing
         using (var getPointAction = new GetPoint())
         {
             getPointAction.SetCommandPrompt(prompt2);
             getPointAction.SetBasePoint(pt0, true);
             getPointAction.DynamicDraw += (sender, e) =>
             {
                 Point3d currentPt = e.CurrentPoint;

                 // Define the rectangle corners based on pt0 and currentPt
                 var rectangle = new Polyline(new[] {
                 pt0,
                 new Point3d(currentPt.X, pt0.Y, pt0.Z),
                 currentPt,
                 new Point3d(pt0.X, currentPt.Y, pt0.Z),
                 pt0 // Close the rectangle
             });

                 // Define other three corners based on pt0 and currentPt
                 Point3d pt2 = new Point3d(currentPt.X, pt0.Y, pt0.Z);
                 Point3d pt3 = new Point3d(pt0.X, currentPt.Y, pt0.Z);

                 // Draw temporary rectangle
                 e.Display.DrawPatternedLine(pt0, pt2, Color.CornflowerBlue, 61680, 2);
                 e.Display.DrawPatternedLine(pt2, currentPt, Color.CornflowerBlue, 61680, 2);
                 e.Display.DrawPatternedLine(currentPt, pt3, Color.CornflowerBlue, 61680, 2);
                 e.Display.DrawPatternedLine(pt3, pt0, Color.CornflowerBlue, 61680, 2);
             };

             if (getPointAction.Get() != GetResult.Point)
             {
                 RhinoApp.WriteLine("No end point was selected.");
                 return null;
             }
             pt1 = getPointAction.Point();
         }

         // Create the rectangle polyline
         var rectanglePolyline = new Polyline(new[] {
         pt0,
         new Point3d(pt1.X, pt0.Y, pt0.Z),
         pt1,
         new Point3d(pt0.X, pt1.Y, pt0.Z),
         pt0 // Close the rectangle
     });

         return rectanglePolyline;
     }

     /// <summary>
     /// Allows the user to select points for a leader annotation.
     /// </summary>
     /// <param name="prompt1">The prompt for the first point.</param>
     /// <param name="prompt2">The prompt for the second point.</param>
     /// <returns>A list of Point3d representing the leader points, or null if cancelled.</returns>
     public static void GetLeaderPointsFromUser(string prompt, RhinoInteractionHandler interactionHandler)
     {
         Point3d startPoint = Point3d.Unset;
         using (var getPointAction = new GetPoint())
         {
             getPointAction.SetCommandPrompt(prompt);
             getPointAction.AcceptNothing(true); // Allow pressing Enter to finish
             getPointAction.DynamicDraw += (sender, e) =>
             {
                 if (startPoint.IsValid)
                 {
                     // Draw the line from startPoint to current point
                     e.Display.DrawLine(startPoint, e.CurrentPoint, Color.CornflowerBlue, 2);
                 }
             };

             while (true)
             {
                 var res = getPointAction.Get();
                 if (res == GetResult.Point)
                 {
                     if (!startPoint.IsValid)
                     {
                         // First point of the pair
                         startPoint = getPointAction.Point();
                         getPointAction.SetCommandPrompt("Select next point:");
                         getPointAction.SetBasePoint(startPoint, true);
                     }
                     else
                     {
                         // Second point of the pair
                         Point3d endPoint = getPointAction.Point();

                         // Process the pair
                         interactionHandler.ProcessLeaderPointPair(startPoint, endPoint);

                         // Reset startPoint
                         startPoint = Point3d.Unset;

                         // Reset command prompt
                         getPointAction.SetCommandPrompt("Select new Location or press Enter to finish:");
                     }
                 }
                 else if (res == GetResult.Nothing)
                 {
                     // User pressed Enter
                     break;
                 }
                 else
                 {
                     // Cancelled or error
                     RhinoApp.WriteLine("Point selection cancelled or invalid.");
                     break;
                 }
             }
         }
     }

public class RhinoInteractionHandler
{
    // Properties
    public double Scale { get; set; } = 1.0;
    public string AxId { get; set; } = "D-1";
    private Polyline rectanglePolyline;

    // Geometry data
    private GeometryData geometryData;
    private DetailSymbolConduit conduit;

    // Store the original leader points
    private Point3d originalStartPoint = Point3d.Unset;
    private Point3d lastEndPoint = Point3d.Unset;

    public RhinoInteractionHandler(DetailSymbolConduit displayConduit)
    {
        geometryData = new GeometryData();
        conduit = displayConduit;
    }
    public void ProcessLeaderPointPair(Point3d startPoint, Point3d endPoint)
    {
        if (rectanglePolyline == null)
        {
            RhinoApp.WriteLine("Rectangle is not set. Cannot process leader point pair.");
            return;
        }

        // Store the original points
        originalStartPoint = startPoint;
        lastEndPoint = endPoint;

        // Recalculate the geometry data
        RecalculateGeometryData();
    }
    public void RecalculateGeometryData()
    {
        if (!originalStartPoint.IsValid || !lastEndPoint.IsValid)
            return;
        // Get the adjusted start point (closest point on rectangle)
        var adjustedStartPoint = GetClosestPointOnRectangle(originalStartPoint);
        var adjustedEndPoint = lastEndPoint;

        // Adjust the leader points
        var leaderPoints = new List<Point3d> { adjustedStartPoint, adjustedEndPoint };

        // Clear previous leaders and texts from geometryData
        geometryData.Leaders.Clear();
        geometryData.Texts.Clear();
        geometryData.FilletRectangles.Clear();
        geometryData.TextCurves.Clear();

        // Adjust the leader points
        
        var adjustedLeaderPoints = GeometryCreator.AdjustLeaderPoints(leaderPoints, Scale);

        // Create the leader
        string styleName = GeometryCreator.GetDimensionStyleName(Scale, 2.5);
        var leaderStyle = RhinoDoc.ActiveDoc.DimStyles.FindName(styleName) ?? RhinoDoc.ActiveDoc.DimStyles.Current;

        var leader = Leader.CreateWithRichText("", Plane.WorldXY, leaderStyle, adjustedLeaderPoints.ToArray());
        geometryData.Leaders.Add(leader);

        // Create the text entity and rectangle
        var textEntityTuple = GeometryCreator.CreateTextEntity(leader, AxId, Scale);
        if (textEntityTuple != null)
        {
            geometryData.Texts.Add(textEntityTuple.Item1);
            geometryData.FilletRectangles.Add(textEntityTuple.Item2);
            geometryData.TextCurves.AddRange(textEntityTuple.Item3);
        }

        // Update the conduit
        conduit.UpdateGeometry(geometryData);
        RhinoDoc.ActiveDoc.Views.Redraw();
    }
    private Point3d GetClosestPointOnRectangle(Point3d point)
    {
        if (rectanglePolyline == null)
            return point;

        Curve rectangleCurve = rectanglePolyline.ToNurbsCurve();
        double t;
        if (rectangleCurve.ClosestPoint(point, out t))
        {
            return rectangleCurve.PointAt(t);
        }
        else
        {
            return point;
        }
    }

    public void GetPointsFromUser(DetailSymbolConduit conduit)
    {
        // Get the rectangle from the user
        var rectangle = RhinoGetPointHelper.GetRectangleFromUser("Select the first corner of the rectangle:", "Select the opposite corner of the rectangle:");
        if (rectangle == null)
            return;

        // Store the rectangle
        rectanglePolyline = rectangle;

        // Update geometry data with the rectangle
        geometryData = new GeometryData();
        geometryData.DashedCurves.AddRange(rectangle.ToNurbsCurve().DuplicateSegments());

        // Update the conduit with the rectangle
        conduit.UpdateGeometry(geometryData);
        RhinoDoc.ActiveDoc.Views.Redraw();

        // Begin collecting leader points and processing them in pairs
        RhinoGetPointHelper.GetLeaderPointsFromUser("Select leader start point:", this);
    }

    public GeometryData GetGeometryData()
    {
        return geometryData;
    }


public static class GeometryCreator
{
  internal static string GetDimensionStyleName(double scale, double textHeight)
  {
      return scale > 9 ? $"PCY {textHeight}mm 1/{scale}" : $"PCY {textHeight}mm 1/0{scale}";
  }

  /// <summary>
  /// Adjusts the leader points based on the scale and initial direction.
  /// </summary>
  public static List<Point3d> AdjustLeaderPoints(List<Point3d> leaderPoints, double scale)
  {
      var adjustedPoints = new List<Point3d>(leaderPoints);
      var pt3 = leaderPoints[1];

      // Move the point along the X-axis based on scale
      pt3.Transform(Transform.Translation(Vector3d.XAxis * 3 * scale));

      // Detect right-to-left selection and adjust accordingly
      if (leaderPoints[0].X > leaderPoints[1].X)
          pt3.Transform(Transform.Translation(-Vector3d.XAxis * 6 * scale));

      adjustedPoints.Add(pt3);
      return adjustedPoints;
  }

  /// <summary>
  /// Creates the text entity and its surrounding filleted rectangle.
  /// </summary>
  internal static Tuple<TextEntity, Curve, List<Curve>> CreateTextEntity(Leader leader, string text, double scale)
  {
      var doc = RhinoDoc.ActiveDoc;
      var Pt = leader.Points3D[2];
      double baseTextHeight = 2.5;
      if (Pt == Point3d.Unset)
          return null;

      var xAxis = doc.Views.ActiveView.ActiveViewport.ConstructionPlane().XAxis;
      var yAxis = doc.Views.ActiveView.ActiveViewport.ConstructionPlane().YAxis;

      var txtObj = new TextEntity
      {
          Text = text,
          Plane = new Plane(Pt, xAxis, yAxis),
          TextHeight = baseTextHeight * scale,
          Justification = TextJustification.MiddleCenter
      };

      var txtObj2 = new TextEntity
      {
          Text = text,
          Plane = new Plane(Pt, xAxis, yAxis),
          TextHeight = baseTextHeight * scale,
          Justification = TextJustification.MiddleCenter
      };

      Plane recplane;
      // Determine the text style based on scale
      string styleName = GetDimensionStyleName(scale, baseTextHeight);
      var textStyle = doc.DimStyles.FindName(styleName) ?? doc.DimStyles.Current;
      txtObj.ParentDimensionStyle = textStyle;
      txtObj2.ParentDimensionStyle = textStyle;

      // Calculate text dimensions
      var txtBbox = txtObj.GetBoundingBox(true);
      var xDim = txtObj.TextModelWidth;
      var yDim = txtObj.TextHeight;

      // Adjust the text position based on leader direction
      if (leader.Points3D[0].X > leader.Points3D[1].X)
      {
          txtObj.Plane = new Plane(Pt + new Vector3d((-1 * xDim* scale), 0, 0), xAxis, yAxis);
          txtObj2.Plane = new Plane(Pt + new Vector3d((-1 * xDim* scale), 0, 0), xAxis, yAxis);
      }
      else
      {
          txtObj.Plane = new Plane(Pt + new Vector3d(0, 0, 0), xAxis, yAxis);
          txtObj2.Plane = new Plane(Pt + new Vector3d(0, 0, 0), xAxis, yAxis);
      }

      //// Create the surrounding rectangle
      //if (leader.Points3D[0].X > leader.Points3D[1].X)
      //     recplane = new Plane(Pt + new Vector3d(-Math.Max(xDim / 2 + 6, 6) * scale, 0, 0), xAxis, yAxis);
      //else
      //recplane = new Plane(Pt + new Vector3d(Math.Max(xDim / 2 + 6, 6) * scale, 0, 0), xAxis, yAxis);

      var rectWidth = (xDim  + 5 )* scale;
      var rectHeight = (yDim + 1.5) * scale;
      var rectangle = new Rectangle3d(txtObj.Plane, rectWidth, rectHeight);
      // Create filleted corners
      var filletRadius = rectHeight / 2;
      List<Curve> recsegments = new List<Curve>();
      recsegments.AddRange(rectangle.ToNurbsCurve().DuplicateSegments());
      var translation = new Vector3d(Point3d.Origin);
      var translation2 = new Vector3d(Point3d.Origin);
      if (leader.Points3D[0].X > leader.Points3D[1].X)
      {
          Point3d recMid1 = recsegments[1].PointAtLength(recsegments[1].GetLength() / 2);
          Vector3d transformRec = leader.Points3D[2] - recMid1;
          Vector3d transformText2 = new Vector3d(-1 * (2.5 * scale), (1.25 * scale), 0);
          translation = transformRec;
          translation2 = transformText2;
      }
      else
      {
          Point3d recMid2 = recsegments[3].PointAtLength(recsegments[3].GetLength() / 2);
          Vector3d transformRec2 = leader.Points3D[2] - recMid2;
          Vector3d transformText = new Vector3d(2.5 * scale, (1.25 * scale), 0);
          translation = transformRec2;
          translation2 = transformText;
      }



      //Transform the rectangle to center it around the text
      //var translation = new Vector3d(-1 * (rectHeight/2) * scale * yAxis);
      rectangle.Transform(Transform.Translation(translation));
      txtObj.Transform(Transform.Translation(translation2));
      txtObj2.Transform(Transform.Translation(translation2));

      var filletedCurve = Curve.CreateFilletCornersCurve(rectangle.ToNurbsCurve(), filletRadius, 0.01, 0.01);


      List<Curve> TextCurves = new List<Curve>();
          // Convert TextEntity to curves
      var textCurves = txtObj2.Explode(); // Explodes the text into individual curves
      TextCurves.AddRange(textCurves);

      return new Tuple<TextEntity, Curve, List<Curve>>(txtObj, filletedCurve.ToNurbsCurve(), TextCurves);
  }

   public class GeometryData
   {
       public List<Curve> DashedCurves { get; set; } = new List<Curve>();
       public List<Curve> Curves { get; set; } = new List<Curve>();
       public List<TextEntity> Texts { get; set; } = new List<TextEntity>();
       public List<double> scale { get; set; } = new List<double>();
       public List<Leader> Leaders { get; set; } = new List<Leader>();
       public List<Curve> FilletRectangles { get; set; } = new List<Curve>();
       public List<Curve> TextCurves { get; set; } = new List<Curve>();
   }


this is all of my classes,
I want to stop and reset every thing when the  private void OnBakeClicked(object sender, EventArgs e) event is trigered by user clicking on the btnBake, specialy if the user didn't pressed Enter in the GetLeaderPointsFromUser() exit the rhino comand