using FeMM.Common.Models;
using FeMM.Grasshopper.Components.Models.MeCheck2;
using FeMM.Grasshopper.Components.Parameters;
using FeMM.Grasshopper.DataTypes;
using FeMM.Grasshopper.DataTypes.FeMM;
using FeMM.Grasshopper.DataTypes.MeCheck2;
using FeMM.Grasshopper.Helpers;
using Grasshopper.Kernel;
using Maffeis.Geometry;
using Maffeis.Model.Materials;
using Maffeis.Model.Results;
using Maffeis.Model.Sections.Concrete;
using Maffeis.Model.Standards;
using Maffeis.Utilities.Maths;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.Versioning;
using System.Text;

namespace FeMM.Grasshopper.Components.MeCheck2
{
#if NETCOREAPP
    [SupportedOSPlatform("windows")]
#endif
    public class SuperElementSectionExtractorComponent : GH_Component
    {
        protected bool _run;

        #region JSON_VARIABLES

        StandardEN1992p11 _concreteStandard;
        StandardEN1993p11 _structuralSteelStandard;

        ConcreteMaterialEN1992 _concreteMaterialEN1992;
        SteelMaterialEN1993 _steelMaterialEN1993;
        SteelMaterialEN1992 _rebarMaterial;

        private double _geometrySlabHeight;
        private double _geometrySlabWidth;
        private double _geometryTopFlangeThickness;
        private double _geometryTopFlangeWidth;
        private double _geometryNetWebHeight;
        private double _geometryWebThickness;
        private double _geometryBottomFlangeThickness;
        private double _geometryBottomFlangeWidth;
        private double _geometryRebarsTopDistanceLayer0;
        private double _geometryRebarsTopDistanceLayer1;
        private double _geometryRebarsAreaLayer0;
        private double _geometryRebarsAreaLayer1;
        private double _geometryConcreteOffest;
        private bool _geometryTopFlangeLocalBuckling;
        private bool _geometryWebLocalBuckling;
        private bool _geometryBottomFlangeLocalBuckling;
        private bool _epsilonBasedOnCurrentStress;

        private double _homogenizationNInf;
        private double _homogenizationN0;
        private bool _useHomogenizationN;

        private bool _considerConcrete;
        private bool _considerRebar;

        private (ResultBeamForces OnlySteel, ResultBeamForces NInfinite, ResultBeamForces NInstant) _selectedComboForces = (null, null, null);
        private (double StrainInfiniteTime, double StrainIstantTime) _selectedStrains = (0, 0);

        private (ResultBeamForces[] OnlySteel, ResultBeamForces[] NInfinite, ResultBeamForces[] NInstant) _forcesForOutput;
        private (double[] StrainInfiniteTime, double[] StrainIstantTime) _strains;

        private CompositeSectionStiffeners _sectionStiffeners;

        // Units conversions
        public static readonly double FROM_N_TO_KN = 0.001;
        public static readonly double FROM_NM_TO_KNM = 0.000001;

        #endregion

        /// <summary>
        /// Initializes a new instance of the ChecksComponent class.
        /// </summary>
        public SuperElementSectionExtractorComponent()
          : base("Super Element Section Extractor", "SESE", "Extract the data of a Superelement section", CategoryNameConstants.CATEGORY_CHECKS, CategoryNameConstants.SUBCATEGORY_MECHECK2)
        {
            _run = false;
        }

        public override void CreateAttributes()
        {
            ComponentAttributes.ComponentOneButtonAttributes attr = new(this, "Run");
            attr.ButtonPressed += () =>
            {
                _run = true;
                ExpireSolution(true);
            };
            m_attributes = attr;
        }

        /// <summary>
        /// Registers all the input parameters for this component.
        /// </summary>
        protected override void RegisterInputParams(GH_InputParamManager pManager)
        {
            pManager.AddGenericParameter("FeMM Model", "FM", "The FeMM model", GH_ParamAccess.item);
            pManager.AddGenericParameter("Super-Element Beam", "SE", "The Super-Element Beam", GH_ParamAccess.list);
            pManager.AddNumberParameter("Section Distance", "SD", "Distance of the section to extract", GH_ParamAccess.item, 0.0);
            pManager.AddTextParameter("Combination Name", "CN", "Name of the combination of which the force analysis will be selected", GH_ParamAccess.item);
            pManager.AddBooleanParameter("Web Local Instability", "WLI", "If true, web is subjected to loacel instability", GH_ParamAccess.item, true);
            pManager.AddBooleanParameter("Top Flange Local Instability", "TFLI", "If true, top flange is subjected to loacel instability", GH_ParamAccess.item, true);
            pManager.AddBooleanParameter("Bottom Flange Local Instability", "BLI", "If true, bottom flange is subjected to loacel instability", GH_ParamAccess.item, true);
            pManager.AddBooleanParameter("Effective Epsilon", "ε", "If true, consider the epsilon based on the real tensione distribuition on the part of the section. " +
                "Otherwise, it consider the yelding tension over all the section (safety side, higher tension, lower class)", GH_ParamAccess.item, false);
            pManager.AddTextParameter("Save Path", "P", "Local folder path at which the data will be saved", GH_ParamAccess.item);
            pManager.AddBooleanParameter("Autorun", "A", "Execute the component authomatically on input data change", GH_ParamAccess.item, false);
            int idx = pManager.AddParameter(new CompositeSectionStiffenersParameter());
            pManager[idx].Optional = true;
        }

        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }

        /// <summary>
        /// This is the method that actually does the work.
        /// </summary>
        /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            Message = "";
            GH_Model model = new();
            List<GH_CompositeBeam> beams = [];
            double sectionDistance = 0.0;
            string comboName = null;
            bool webFlangeInstab = false;
            bool topFlangeInstab = false;
            bool bottomFlangeInstab = false;
            bool effectiveEpsilon = false;
            string savePath = null;
            string folderPath = null;
            string fileName = null;
            bool autoRun = false;

            int n = 0;
            if (!DA.GetData(n++, ref model)) return;
            if (!DA.GetDataList(n++, beams)) return;
            if (!DA.GetData(n++, ref sectionDistance)) return;
            if (!DA.GetData(n++, ref comboName)) return;
            if (!DA.GetData(n++, ref webFlangeInstab)) return;
            if (!DA.GetData(n++, ref topFlangeInstab)) return;
            if (!DA.GetData(n++, ref bottomFlangeInstab)) return;
            if (!DA.GetData(n++, ref effectiveEpsilon)) return;
            if (!DA.GetData(n++, ref savePath)) return;
            if (!DA.GetData(n++, ref autoRun)) return;


            GH_CompositeSectionStiffeners goo = null;
            DA.GetData(n++, ref goo);
            _sectionStiffeners = goo?.Value;

            if (model.Value == null)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Null model");
                Message = "Error";
                return;
            }
            List<SuperElementAttributeModel> superElementsBuffer = Common.Helpers.CommonModelHelper.GetSuperElements(model.Value);
            if (superElementsBuffer == null || superElementsBuffer.Count == 0)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "No Super-Element in the model");
                Message = "Error";
                return;
            }
            if (beams == null || beams.Count == 0)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "No Beam passed");
                Message = "Error";
                return;
            }
            if (string.IsNullOrWhiteSpace(savePath))
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "No save path selected");
                Message = "Error";
                return;
            }
            if (sectionDistance < 0)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The distance value must be greater than zero");
                Message = "Error";
                _run = false;
                return;
            }

            if (_run || autoRun)
            {
                CompositeBeamModel beamModel = null;
                double cumulativeDistance = 0;
                foreach (var beam in beams)
                {
                    cumulativeDistance += beam.Value.Length;
                    if (cumulativeDistance >= sectionDistance)
                    {
                        beamModel = beam.Value;
                        sectionDistance -= (cumulativeDistance - beam.Value.Length);
                        break;
                    }
                }

                if (beamModel == null)
                {
                    AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The distance value exceeds the beam dimension");
                    Message = "Error";
                    _run = false;
                    return;
                }

                fileName = Path.GetFileNameWithoutExtension(savePath);
                folderPath = Path.GetDirectoryName(savePath);
                if (!Directory.Exists(folderPath))
                {
                    AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The selected folder doesn't exists");
                    Message = "Error";
                    _run = false;
                    return;
                }

                try
                {
                    /* ---------- Data Extraction ---------- */

                    double start_bfw = beamModel.StartBeamProperty.SteelSection.B1;            // bottom flange width
                    double start_tfw = beamModel.StartBeamProperty.SteelSection.B2;            // top flange width
                    double start_h = beamModel.StartBeamProperty.SteelSection.D;               // heigth
                    double start_bft = beamModel.StartBeamProperty.SteelSection.T1;            // Bottom flange thickness
                    double start_tft = beamModel.StartBeamProperty.SteelSection.T2;            // Top flange thickness
                    double start_wt = beamModel.StartBeamProperty.SteelSection.T3;             // Web thickness

                    double start_hh = beamModel.StartBeamProperty.ConcreteSection.Height;      // heigth
                    double start_b = beamModel.StartBeamProperty.ConcreteSection.Width;        // base
                    double start_topRebarsAreaTot = beamModel.StartBeamProperty.ConcreteSection.TopRebarArea;
                    double start_topRebarsCover = beamModel.StartBeamProperty.ConcreteSection.TopCover;
                    double start_bottomRebarsAreaTot = beamModel.StartBeamProperty.ConcreteSection.BottomRebarArea;
                    double start_bottomRebarsCover = beamModel.StartBeamProperty.ConcreteSection.BottomCover;

                    double start_verticalOffset = beamModel.StartBeamProperty.VerticalOffset;
                    double start_NInst = beamModel.StartBeamProperty.NInstant;
                    double start_NInf = beamModel.StartBeamProperty.NInfinite;
                    bool start_considerConcrete = beamModel.StartBeamProperty.ConsiderConcrete;
                    bool start_considerRebar = beamModel.StartBeamProperty.ConsiderRebar;

                    MaterialModel start_concreteModel = beamModel.StartBeamProperty.ConcreteMaterial;
                    MaterialModel start_steelModel = beamModel.StartBeamProperty.SteelMaterial;
                    MaterialModel start_rebarModel = beamModel.StartBeamProperty.RebarMaterial;

                    double end_bfw = beamModel.EndBeamProperty.SteelSection.B1;                // bottom flange width
                    double end_tfw = beamModel.EndBeamProperty.SteelSection.B2;                // top flange width
                    double end_h = beamModel.EndBeamProperty.SteelSection.D;                   // heigth
                    double end_bft = beamModel.EndBeamProperty.SteelSection.T1;                // Bottom flange thickness
                    double end_tft = beamModel.EndBeamProperty.SteelSection.T2;                // Top flange thickness
                    double end_wt = beamModel.EndBeamProperty.SteelSection.T3;                 // Web thickness

                    double end_hh = beamModel.EndBeamProperty.ConcreteSection.Height;          // heigth
                    double end_b = beamModel.EndBeamProperty.ConcreteSection.Width;            // base
                    double end_topRebarsAreaTot = beamModel.EndBeamProperty.ConcreteSection.TopRebarArea;
                    double end_topRebarsCover = beamModel.EndBeamProperty.ConcreteSection.TopCover;
                    double end_bottomRebarsAreaTot = beamModel.EndBeamProperty.ConcreteSection.BottomRebarArea;
                    double end_bottomRebarsCover = beamModel.EndBeamProperty.ConcreteSection.BottomCover;

                    double end_verticalOffset = beamModel.EndBeamProperty.VerticalOffset;
                    double end_NInst = beamModel.EndBeamProperty.NInstant;
                    double end_NInf = beamModel.EndBeamProperty.NInfinite;
                    bool end_considerConcrete = beamModel.EndBeamProperty.ConsiderConcrete;
                    bool end_considerRebar = beamModel.EndBeamProperty.ConsiderRebar;

                    MaterialModel end_concreteModel = beamModel.EndBeamProperty.ConcreteMaterial;
                    MaterialModel end_steelModel = beamModel.EndBeamProperty.SteelMaterial;
                    MaterialModel end_rebarModel = beamModel.EndBeamProperty.RebarMaterial;

                    double homogenizedFactorInfinite = beamModel.StartBeamProperty.NInfinite;
                    double homogenizedFactorInstant = beamModel.StartBeamProperty.NInstant;

                    /* ---------- Section Data ---------- */

                    bool constantSection = false;

                    if (start_bfw == end_bfw && start_tfw == end_tfw && start_h == end_h && start_bft == end_bft && start_tft == end_tft && start_wt == end_wt)
                    {
                        if (start_hh == end_hh && start_b == end_b && start_topRebarsAreaTot == end_topRebarsAreaTot && start_topRebarsCover == end_topRebarsCover &&
                            start_verticalOffset == end_verticalOffset)
                        {
                            if (start_concreteModel == end_concreteModel && start_steelModel == end_steelModel && start_rebarModel == end_rebarModel)
                                constantSection = true;
                        }
                    }

                    // Standards
                    _concreteStandard = new StandardEN1992p11();
                    _structuralSteelStandard = new StandardEN1993p11();

                    // Geometry
                    _geometryTopFlangeLocalBuckling = topFlangeInstab;
                    _geometryWebLocalBuckling = webFlangeInstab;
                    _geometryBottomFlangeLocalBuckling = bottomFlangeInstab;
                    _epsilonBasedOnCurrentStress = effectiveEpsilon;

                    // Homogenization
                    _useHomogenizationN = true;
                    _homogenizationNInf = homogenizedFactorInfinite;
                    _homogenizationN0 = homogenizedFactorInstant;

                    // Options
                    _considerConcrete = start_considerConcrete;
                    _considerRebar = start_considerRebar;

                    if (constantSection)
                    {
                        // Geometry
                        _geometrySlabHeight = start_hh;
                        _geometrySlabWidth = start_b;
                        _geometryConcreteOffest = start_verticalOffset;
                        _geometryTopFlangeThickness = start_tft;
                        _geometryTopFlangeWidth = start_tfw;
                        _geometryWebThickness = start_wt;
                        _geometryNetWebHeight = start_h - (start_bft + start_tft);
                        _geometryBottomFlangeThickness = start_bft;
                        _geometryBottomFlangeWidth = start_bfw;
                        _geometryRebarsAreaLayer0 = start_bottomRebarsAreaTot;
                        _geometryRebarsAreaLayer1 = start_topRebarsAreaTot;
                        _geometryRebarsTopDistanceLayer0 = start_bottomRebarsCover;
                        _geometryRebarsTopDistanceLayer1 = start_topRebarsCover;

                        // Materials
                        _concreteMaterialEN1992 = new ConcreteMaterialEN1992(start_concreteModel.Name, start_concreteModel.SpecificCompressiveStrength, ConcreteMaterial.CompressionStressStrainDiagrams.ParabolaRectangle);
                        _steelMaterialEN1993 = new SteelMaterialEN1993(start_steelModel.Name, start_steelModel.Modulus, start_steelModel.MinimumYieldStress, start_steelModel.MinimumTensileStress, 0.1, SteelMaterial.StressStrainCurveType.ElasticPerfectPlastic, SteelMaterial.SteelTypes.Structural);
                        _rebarMaterial = new SteelMaterialEN1992(start_rebarModel.Name, start_rebarModel.Modulus, start_rebarModel.MinimumTensileStress, start_rebarModel.ExpectedTensileStress, 0.1, SteelMaterial.StressStrainCurveType.ElasticPerfectPlastic, SteelMaterial.SteelTypes.Rebar);
                    }
                    else
                    {
                        // Geometry
                        _geometrySlabHeight = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_hh, end_hh, sectionDistance);
                        var hInterpolated = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_h, end_h, sectionDistance);
                        _geometrySlabWidth = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_b, end_b, sectionDistance);
                        _geometryConcreteOffest = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_verticalOffset, start_verticalOffset, sectionDistance);
                        _geometryTopFlangeThickness = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_tft, end_tft, sectionDistance);
                        _geometryTopFlangeWidth = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_tfw, end_tfw, sectionDistance);
                        _geometryWebThickness = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_wt, end_wt, sectionDistance);
                        _geometryBottomFlangeThickness = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_bft, end_bft, sectionDistance);
                        _geometryNetWebHeight = hInterpolated - (_geometryBottomFlangeThickness + _geometryTopFlangeThickness);
                        _geometryBottomFlangeWidth = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_bfw, end_bfw, sectionDistance);
                        _geometryRebarsAreaLayer0 = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_bottomRebarsAreaTot, end_bottomRebarsAreaTot, sectionDistance);
                        _geometryRebarsAreaLayer1 = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_topRebarsAreaTot, end_topRebarsAreaTot, sectionDistance);
                        _geometryRebarsTopDistanceLayer0 = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_bottomRebarsCover, end_bottomRebarsCover, sectionDistance);
                        _geometryRebarsTopDistanceLayer1 = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_topRebarsCover, end_topRebarsCover, sectionDistance);

                        // Materials
                        double fck = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_concreteModel.SpecificCompressiveStrength, end_concreteModel.SpecificCompressiveStrength, sectionDistance);
                        _concreteMaterialEN1992 = new ConcreteMaterialEN1992(start_concreteModel.Name, fck, ConcreteMaterial.CompressionStressStrainDiagrams.ParabolaRectangle);

                        double mysS = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_steelModel.MinimumYieldStress, end_steelModel.MinimumYieldStress, sectionDistance);
                        double mtsS = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_steelModel.MinimumTensileStress, end_steelModel.MinimumTensileStress, sectionDistance);
                        double eS = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_steelModel.Modulus, end_steelModel.Modulus, sectionDistance);
                        _steelMaterialEN1993 = new SteelMaterialEN1993(start_steelModel.Name, eS, mysS, mtsS, 0.1, SteelMaterial.StressStrainCurveType.ElasticPerfectPlastic, SteelMaterial.SteelTypes.Structural);

                        double mysR = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_rebarModel.MinimumYieldStress, end_rebarModel.MinimumYieldStress, sectionDistance);
                        double mtsR = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_rebarModel.MinimumTensileStress, end_rebarModel.MinimumTensileStress, sectionDistance);
                        double eR = Interpolation.GetLinearInterpolation(0, beamModel.Length, start_rebarModel.Modulus, end_rebarModel.Modulus, sectionDistance);
                        _rebarMaterial = new SteelMaterialEN1992(start_rebarModel.Name, eR, mysR, mtsR, 0.1, SteelMaterial.StressStrainCurveType.ElasticPerfectPlastic, SteelMaterial.SteelTypes.Rebar);
                    }

                    /* ---------- Forces ---------- */

                    try
                    {
                        CoordinateSystem cs = new(Point3d.Origin, new Vector3d(-1, 0, 0), new Vector3d(0, -1, 0));

                        ResultBeamForces[] resultBeamForcesOnlySteelsForOutput = new ResultBeamForces[model.Value.Combinations.Count];
                        ResultBeamForces[] resultBeamForcesInfinitesForOutput = new ResultBeamForces[model.Value.Combinations.Count];
                        ResultBeamForces[] resultBeamForcesInstantsForOutput = new ResultBeamForces[model.Value.Combinations.Count];

                        double[] strainNIstantArray = new double[model.Value.Combinations.Count];
                        double[] strainNInfiniteArray = new double[model.Value.Combinations.Count];

                        for (int c = 0; c < model.Value.Combinations.Count; c++)
                        {
                            if (model.Value.Combinations[c] is LoadCaseCombinationModel combo)
                            {
                                ResultBeamForces resultBeamForcesOnlySteelForOutput = new(0, 0, 0, 0, 0, 0, cs, 1, combo.Name);
                                ResultBeamForces resultBeamForcesInfiniteForOutput = new(0, 0, 0, 0, 0, 0, cs, 1, combo.Name);
                                ResultBeamForces resultBeamForcesInstantForOutput = new(0, 0, 0, 0, 0, 0, cs, 1, combo.Name);

                                double strainNIstant = 0;
                                double strainNInfinite = 0;

                                /* ----- Get Strain Values ----- */

                                for (int ss = 0; ss < beamModel.Loads.Count; ss++)
                                {
                                    if (beamModel.Loads[ss] is CompositeBeamSlabStrainModel compositeBeamSlabStrainModel)
                                    {
                                        if (compositeBeamSlabStrainModel != null)
                                        {
                                            foreach (KeyValuePair<CaseModel, double> kvp in combo.Values)
                                            {
                                                CaseModel loadCase = kvp.Key;
                                                double coeff = kvp.Value;
                                                {
                                                    if (compositeBeamSlabStrainModel.LoadCase.Name == loadCase.Name)
                                                    {
                                                        if (compositeBeamSlabStrainModel.LoadCase.Stages.Count > 0 && compositeBeamSlabStrainModel.LoadCase.Stages.FirstOrDefault().BridgePhase == LoadCaseModel.BridgeStage.BridgePhases.StrainInstant)
                                                        {
                                                            strainNIstant = compositeBeamSlabStrainModel.Value.Strain;
                                                            strainNIstant *= coeff;
                                                        }
                                                        else if (compositeBeamSlabStrainModel.LoadCase.Stages.Count > 0 && compositeBeamSlabStrainModel.LoadCase.Stages.FirstOrDefault().BridgePhase == LoadCaseModel.BridgeStage.BridgePhases.StrainInfinite)
                                                        {
                                                            strainNInfinite = compositeBeamSlabStrainModel.Value.Strain;
                                                            strainNInfinite *= coeff;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }

                                /* ----- Get AxialForce, Vx, Vy, Torque, Mx, My Values ----- */

                                foreach (KeyValuePair<CaseModel, double> kvp in combo.Values)
                                {
                                    CaseModel loadCase = kvp.Key;
                                    double coeff = kvp.Value;

                                    List<BeamForceResultModel> beamForceResultModels = [];
                                    for (int ll = 0; ll < beamModel.Results.Count; ll++)
                                    {
                                        if (beamModel.Results[ll] is BeamForceResultModel beamForceResultModel)
                                        {
                                            beamForceResultModels.Add(beamForceResultModel);
                                        }
                                    }

                                    var fff = beamForceResultModels.Where(w => w.LoadCase.Name == loadCase.Name).OrderBy(ff => ff.Station).ToList();
                                    if (fff != null && fff.Count > 0)
                                    {
                                        double[] dists = new double[fff.Count];
                                        double[] axialForces = new double[fff.Count];
                                        double[] vxs = new double[fff.Count];
                                        double[] vys = new double[fff.Count];
                                        double[] torques = new double[fff.Count];
                                        double[] mxs = new double[fff.Count];
                                        double[] mys = new double[fff.Count];
                                        for (int i = 0; i < fff.Count; i++)
                                        {
                                            var ff = fff[i];

                                            double dist = 0;
                                            if (ff.NormalizedLength)
                                                dist = ff.Station * beamModel.Length;
                                            else
                                                dist = ff.Station;

                                            dists[i] = dist;
                                            axialForces[i] = ff.AxialForce;
                                            vxs[i] = ff.Vx;
                                            vys[i] = ff.Vy;
                                            torques[i] = ff.Torque;
                                            mxs[i] = ff.Mx;
                                            mys[i] = ff.My;
                                        }

                                        var axialForce = Interpolation.GetLinearInterpolation(dists, axialForces, sectionDistance);
                                        var vx = Interpolation.GetLinearInterpolation(dists, vxs, sectionDistance);
                                        var vy = Interpolation.GetLinearInterpolation(dists, vys, sectionDistance);
                                        var torque = Interpolation.GetLinearInterpolation(dists, torques, sectionDistance);
                                        var mx = Interpolation.GetLinearInterpolation(dists, mxs, sectionDistance);
                                        var my = Interpolation.GetLinearInterpolation(dists, mys, sectionDistance);

                                        ResultBeamForces fffForOutput = new(axialForce, vx, vy, torque, my, mx, cs);

                                        fffForOutput *= coeff;

                                        if (((LoadCaseModel)fff[0].LoadCase).Stages.FirstOrDefault().BridgePhase == LoadCaseModel.BridgeStage.BridgePhases.OnlySteel)
                                            resultBeamForcesOnlySteelForOutput += fffForOutput;
                                        if (((LoadCaseModel)fff[0].LoadCase).Stages.FirstOrDefault().BridgePhase == LoadCaseModel.BridgeStage.BridgePhases.NInfinite)
                                            resultBeamForcesInfiniteForOutput += fffForOutput;
                                        if (((LoadCaseModel)fff[0].LoadCase).Stages.FirstOrDefault().BridgePhase == LoadCaseModel.BridgeStage.BridgePhases.NInstant)
                                            resultBeamForcesInstantForOutput += fffForOutput;
                                    }
                                }

                                /* -------------------- */

                                resultBeamForcesOnlySteelsForOutput[c] = resultBeamForcesOnlySteelForOutput;
                                resultBeamForcesInfinitesForOutput[c] = resultBeamForcesInfiniteForOutput;
                                resultBeamForcesInstantsForOutput[c] = resultBeamForcesInstantForOutput;

                                strainNInfiniteArray[c] = strainNInfinite;
                                strainNIstantArray[c] = strainNIstant;

                                if (combo.Name == comboName)
                                {
                                    _selectedComboForces = (resultBeamForcesOnlySteelForOutput, resultBeamForcesInfiniteForOutput, resultBeamForcesInstantForOutput);
                                    _selectedStrains = (strainNInfinite, strainNIstant);
                                }
                            }
                        }

                        _forcesForOutput = (resultBeamForcesOnlySteelsForOutput, resultBeamForcesInfinitesForOutput, resultBeamForcesInstantsForOutput);
                        _strains = (strainNInfiniteArray, strainNIstantArray);
                    }
                    catch (Exception e)
                    {
                        AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Fail to get forces: {e.Message}");
                        Message = "Error";
                        _run = false;
                        return;
                    }

                    /* ---------- Data Export ---------- */

                    string filePath = $"{folderPath}\\{fileName}.bscj";
                    switch (ExportSection(filePath))
                    {
                        case -1:
                            AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Unable to export beam section to file");
                            Message = "Error";
                            _run = false;
                            return;
                        case 1:
                            AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"There is no combo named {comboName}");
                            break;
                    }

                    /* --------------------------------- */

                    Message = "Done";
                }
                catch (Exception e)
                {
                    AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Generic error: {e.Message}");
                    Message = "Error";
                    _run = false;
                    return;
                }

                _run = false;
            }
        }

        /// <summary>
        /// Export section analysis data of the beam to archive.<br/>
        /// Returns:<br/>
        /// -1 -> An unhandled error occurred during the export process<br/>
        /// 0 -> Data exported successfully<br/>
        /// 1 -> Data exported, but the selected combo was not found<br/>
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="beamModel"></param>
        /// <returns></returns>
        private int ExportSection(string filePath)
        {
            int res = 0;

            try
            {
                var memStream = new MemoryStream();
                var streamWriter = new StreamWriter(memStream, Encoding.UTF8);
                var writer = new JsonTextWriter(streamWriter);
                var serializer = new JsonSerializer
                {
                    TypeNameHandling = TypeNameHandling.Auto
                };

                writer.WriteStartObject();

                // Save file version
                int fileVersionNumber = 8;
                writer.WritePropertyName("Version");
                writer.WriteValue(fileVersionNumber);

                serializer.TypeNameHandling = TypeNameHandling.Objects; // to force "$type" for standard in json

                // Save current concrete standard
                writer.WritePropertyName("ConcreteStandard");
                serializer.Serialize(writer, _concreteStandard);

                // Save current structural steel standard
                writer.WritePropertyName("StructuralSteelStandard");
                serializer.Serialize(writer, _structuralSteelStandard);

                // Save current concrete material
                writer.WritePropertyName("ConcreteMaterial");
                serializer.Serialize(writer, _concreteMaterialEN1992);

                // Save current rebars material
                writer.WritePropertyName("RebarSteelMaterial");
                serializer.Serialize(writer, _rebarMaterial);

                // Save current structural material
                writer.WritePropertyName("StrucSteelMaterial");
                serializer.Serialize(writer, _steelMaterialEN1993);

                serializer.TypeNameHandling = TypeNameHandling.Auto;

                // Geometry
                WriteGeometry(writer);

                // Homogenization
                WriteHomogenization(writer);

                // Selected Combo Forces
                if (_selectedComboForces != (null, null, null))
                    WriteSelectedComboForces(writer);
                else
                    res = 1;

                // Options
                WriteOptions(writer);

                //Stiffeners
                _sectionStiffeners?.Write(writer);

                // Forces
                writer.WritePropertyName("Forces");
                writer.WriteStartArray();
                for (int c = 0; c < _forcesForOutput.OnlySteel.Length; c++)
                    WriteComboForce(writer, _forcesForOutput.OnlySteel[c], _forcesForOutput.NInfinite[c], _forcesForOutput.NInstant[c], _strains.StrainInfiniteTime[c], _strains.StrainIstantTime[c]);
                writer.WriteEndArray();

                writer.WriteEndObject();

                writer.Flush();

                var memArray = memStream.ToArray();
                var compressedMemArray = Compress(memArray);
                File.WriteAllBytes(filePath, compressedMemArray);
            }
            catch
            {
                return -1;
            }

            return res;
        }
        private void WriteGeometry(JsonTextWriter writer)
        {
            writer.WritePropertyName("GeometrySlabHeight");
            writer.WriteValue(_geometrySlabHeight);
            writer.WritePropertyName("GeometrySlabWidth");
            writer.WriteValue(_geometrySlabWidth);
            writer.WritePropertyName("GeometryConcreteOffest");
            writer.WriteValue(_geometryConcreteOffest);
            writer.WritePropertyName("GeometryTopFlangeThickness");
            writer.WriteValue(_geometryTopFlangeThickness);
            writer.WritePropertyName("GeometryTopFlangeWidth");
            writer.WriteValue(_geometryTopFlangeWidth);
            writer.WritePropertyName("GeometryNetWebHeight");
            writer.WriteValue(_geometryNetWebHeight);
            writer.WritePropertyName("GeometryWebThickness");
            writer.WriteValue(_geometryWebThickness);
            writer.WritePropertyName("GeometryBottomFlangeThickness");
            writer.WriteValue(_geometryBottomFlangeThickness);
            writer.WritePropertyName("GeometryBottomFlangeWidth");
            writer.WriteValue(_geometryBottomFlangeWidth);
            writer.WritePropertyName("GeometryTopFlangeLocalBuckling");
            writer.WriteValue(_geometryTopFlangeLocalBuckling);
            writer.WritePropertyName("GeometryWebLocalBuckling");
            writer.WriteValue(_geometryWebLocalBuckling);
            writer.WritePropertyName("GeometryBottomFlangeLocalBuckling");
            writer.WriteValue(_geometryBottomFlangeLocalBuckling);
            writer.WritePropertyName("EpsilonBasedOnCurrentStress");
            writer.WriteValue(_epsilonBasedOnCurrentStress);
            writer.WritePropertyName("GeometryRebarsTopDistanceLayer0");
            writer.WriteValue(_geometryRebarsTopDistanceLayer0);
            writer.WritePropertyName("GeometryRebarsTopDistanceLayer1");
            writer.WriteValue(_geometryRebarsTopDistanceLayer1);
            writer.WritePropertyName("GeometryRebarsAreaLayer0");
            writer.WriteValue(_geometryRebarsAreaLayer0);
            writer.WritePropertyName("GeometryRebarsAreaLayer1");
            writer.WriteValue(_geometryRebarsAreaLayer1);
        }
        private void WriteHomogenization(JsonTextWriter writer)
        {
            writer.WritePropertyName("HomogenizationNInf");
            writer.WriteValue(_homogenizationNInf);
            writer.WritePropertyName("HomogenizationN0");
            writer.WriteValue(_homogenizationN0);
            writer.WritePropertyName("UseHomogenizationN");
            writer.WriteValue(_useHomogenizationN);
        }
        private void WriteSelectedComboForces(JsonTextWriter writer)
        {
            writer.WritePropertyName("OnlySteelForceN");
            writer.WriteValue(_selectedComboForces.OnlySteel.N * FROM_N_TO_KN); // [N] -> [kN]
            writer.WritePropertyName("OnlySteelForceVy");
            writer.WriteValue(_selectedComboForces.OnlySteel.V2 * FROM_N_TO_KN); // [N] -> [kN]
            writer.WritePropertyName("OnlySteelForceMx");
            writer.WriteValue(_selectedComboForces.OnlySteel.M1 * FROM_NM_TO_KNM); // [N*mm] -> [kN*m]
            writer.WritePropertyName("InfiniteTimeForceN");
            writer.WriteValue(_selectedComboForces.NInfinite.N * FROM_N_TO_KN);
            writer.WritePropertyName("InfiniteTimeForceVy");
            writer.WriteValue(_selectedComboForces.NInfinite.V2 * FROM_N_TO_KN);
            writer.WritePropertyName("InfiniteTimeForceMx");
            writer.WriteValue(_selectedComboForces.NInfinite.M1 * FROM_NM_TO_KNM);
            writer.WritePropertyName("InfiniteTimeForceE");
            writer.WriteValue(_selectedStrains.StrainInfiniteTime);
            writer.WritePropertyName("InstantTimeForceN");
            writer.WriteValue(_selectedComboForces.NInstant.N * FROM_N_TO_KN);
            writer.WritePropertyName("InstantTimeForceVy");
            writer.WriteValue(_selectedComboForces.NInstant.V2 * FROM_N_TO_KN);
            writer.WritePropertyName("InstantTimeForceMx");
            writer.WriteValue(_selectedComboForces.NInstant.M1 * FROM_NM_TO_KNM);
            writer.WritePropertyName("InstantTimeForceE");
            writer.WriteValue(_selectedStrains.StrainIstantTime);
        }

        private void WriteOptions(JsonTextWriter writer)
        {
            writer.WritePropertyName("ConsiderConcrete");
            writer.WriteValue(_considerConcrete);
            writer.WritePropertyName("ConsiderRebars");
            writer.WriteValue(_considerRebar);
        }

        private static void WriteComboForce(JsonTextWriter writer, ResultBeamForces onlySteel, ResultBeamForces nInfinite, ResultBeamForces nInstant, double strainInfiniteTime, double strainIstantTime)
        {
            writer.WriteStartObject();
            writer.WritePropertyName("Name");
            writer.WriteValue(onlySteel.Name);
            writer.WritePropertyName("N0");
            writer.WriteValue(onlySteel.N * FROM_N_TO_KN); // [N] -> [kN]
            writer.WritePropertyName("Vy0");
            writer.WriteValue(onlySteel.V2 * FROM_N_TO_KN); // [N] -> [kN]
            writer.WritePropertyName("Mx0");
            writer.WriteValue(onlySteel.M1 * FROM_NM_TO_KNM); // [N*mm] -> [kN*m]
            writer.WritePropertyName("N1");
            writer.WriteValue(nInfinite.N * FROM_N_TO_KN);
            writer.WritePropertyName("Vy1");
            writer.WriteValue(nInfinite.V2 * FROM_N_TO_KN);
            writer.WritePropertyName("Mx1");
            writer.WriteValue(nInfinite.M1 * FROM_NM_TO_KNM);
            writer.WritePropertyName("N2");
            writer.WriteValue(nInstant.N * FROM_N_TO_KN);
            writer.WritePropertyName("Vy2");
            writer.WriteValue(nInstant.V2 * FROM_N_TO_KN);
            writer.WritePropertyName("Mx2");
            writer.WriteValue(nInstant.M1 * FROM_NM_TO_KNM);
            writer.WritePropertyName("StrainInfinite");
            writer.WriteValue(strainInfiniteTime);
            writer.WritePropertyName("StrainInstant");
            writer.WriteValue(strainIstantTime);
            writer.WriteEndObject();
        }

        private static byte[] Compress(byte[] data)
        {
            if (data is null || data.Length == 0)
            {
                throw new ArgumentException("Data to compress cannot be null or empty");
            }

            var compressedStream = new MemoryStream();
            using (var gzipStream = new GZipStream(compressedStream, CompressionLevel.Optimal))
            {
                gzipStream.Write(data, 0, data.Length);
            }

            return compressedStream.ToArray();
        }

        public override GH_Exposure Exposure => GH_Exposure.tertiary;

        /// <summary>
        /// Provides an Icon for the component.
        /// </summary>
        protected override System.Drawing.Bitmap Icon => Properties.Resources.SuperElementCheckIcon;

        /// <summary>
        /// Gets the unique ID for this component. Do not change this ID after release.
        /// </summary>
        public override Guid ComponentGuid => new("18116f13-8dfd-4a4d-ac05-b6ccc8dc6bd9");
    }
}
303 files24 directories