using FeMM.Common.Models;
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;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Maffeis.Checkers.Concrete.Attributes;
using Maffeis.Checkers.Concrete.Checkers;
using Maffeis.Checkers.Concrete.Results;
using Maffeis.Checkers.Concrete.SectionSolvers;
using Maffeis.Geometry;
using Maffeis.Model.Materials;
using Maffeis.Model.Results;
using Maffeis.Model.Sections;
using Maffeis.Model.Sections.Concrete;
using Maffeis.Model.Sections.Rebar;
using Maffeis.Model.Standards;
using Maffeis.Utilities.Maths;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using System.Threading.Tasks;

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

        //protected bool _just_loaded;
        protected StressAnalysisResultMeCheck[][][] _resultsDataTree;
        protected double[][][] _tauBDataTree;
        protected ResultBeamForces[][][] _forcesDataTree;

        /// <summary>
        /// Initializes a new instance of the ChecksComponent class.
        /// </summary>
        public SuperElementCheckComponentBase()
          : base("Super Element Check", "SEC", "Check the selected Super-Element", CategoryNameConstants.CATEGORY_CHECKS, CategoryNameConstants.SUBCATEGORY_MECHECK2)
        {
            _run = false;
            _class4MaxIterations = 20; // V1 default.
            //_just_loaded = false;
            _resultsDataTree = null;
            _tauBDataTree = null;
            _forcesDataTree = null;
        }

        public override void CreateAttributes()
        {
            var attr = new ComponentAttributes.ComponentOneButtonAttributes(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.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);
            RegisterInputs(pManager);
        }

        protected abstract void RegisterInputs(GH_InputParamManager pManager);

        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_OutputParamManager pManager)
        {
            pManager.AddParameter(new StressAnalysisResultMeCheckParam(), "Tension Check", "C", "The results", GH_ParamAccess.tree);
            pManager.AddNumberParameter("Tau b", "T", "The results", GH_ParamAccess.tree);
            pManager.AddGenericParameter("Tension Forces", "C", "The forces", GH_ParamAccess.tree);
            RegisterOutputs(pManager);
        }

        protected abstract void RegisterOutputs(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 = "";

            //if (_just_loaded)
            //{
            //    _just_loaded = false;

            //    if (_resultsDataTree != null)
            //    {
            //        DataTree<StressAnalysisResultMeCheck> resultsDataTreeSaved = new();

            //        for (int i = 0; i < _resultsDataTree.Length; i++)
            //            for (int j = 0; j < _resultsDataTree[i].Length; j++)
            //                resultsDataTreeSaved.AddRange(_resultsDataTree[i][j], new GH_Path(i, j));

            //        DA.SetDataTree(0, resultsDataTreeSaved);
            //    }
            //    if (_tauBDataTree != null)
            //    {
            //        DataTree<double> tauBResultsSaved = new();

            //        for (int i = 0; i < _tauBDataTree.Length; i++)
            //            for (int j = 0; j < _tauBDataTree[i].Length; j++)
            //                tauBResultsSaved.AddRange(_tauBDataTree[i][j], new GH_Path(i, j));

            //        DA.SetDataTree(1, tauBResultsSaved);
            //    }
            //    if (_forcesDataTree != null)
            //    {
            //        DataTree<ResultBeamForces> forcesResultSaved = new();

            //        for (int i = 0; i < _forcesDataTree.Length; i++)
            //            for (int j = 0; j < _forcesDataTree[i].Length; j++)
            //                forcesResultSaved.AddRange(_forcesDataTree[i][j], new GH_Path(i, j));

            //        DA.SetDataTree(2, forcesResultSaved);
            //    }
            //    LoadReadedData(DA);

            //    Message = "Done";
            //    return;
            //}

            var model = new GH_Model();
            var beams = new List<GH_CompositeBeam>();
            bool topFlangeInstab = false;
            bool webFlangeInstab = false;
            bool bottomFlangeInstab = false;
            bool effectiveEpsilon = false;

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

            if (model.Value == null)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Null model");
                Message = "Input 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 = "Input Error";
                return;
            }
            if (beams == null || beams.Count == 0)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "No Beams");
                Message = "Input Error";
                return;
            }

            _resultsDataTree = null;
            _tauBDataTree = null;
            _forcesDataTree = null;

            ReadAdditionalParams(DA);

            var paramMeCheckStresses = Params.Output[0] as GH_PersistentParam<GH_StressAnalysisResultMeCheck>;
            if (paramMeCheckStresses != null && !_run && paramMeCheckStresses.PersistentDataCount > 0)
            {
                DA.SetDataTree(0, paramMeCheckStresses.PersistentData);
                return;
            }

            if (_run)
            {
                try
                {
                    paramMeCheckStresses.PersistentData.Clear();

                    (ResultBeamForces[] OnlySteel, ResultBeamForces[] NInfinite, ResultBeamForces[] Ninstant)[][] forces = new
                        (ResultBeamForces[] OnlySteel, ResultBeamForces[] NInfinite, ResultBeamForces[] Ninstant)[beams.Count][];

                    (ResultBeamForces[] OnlySteel, ResultBeamForces[] NInfinite, ResultBeamForces[] Ninstant)[][] forcesForOutput = new
                        (ResultBeamForces[] OnlySteel, ResultBeamForces[] NInfinite, ResultBeamForces[] Ninstant)[beams.Count][];

                    (double[] StrainInfiniteTime, double[] StrainIstantTime)[][] strains = new
                        (double[] StrainInfiniteTime, double[] StrainIstantTime)[beams.Count][];

                    SectionCheckerModelCode2010[][] sectionCheckerModelCode2010s = new SectionCheckerModelCode2010[beams.Count][];
                    (double psiInfinite, double psiInstant)[][] psis = new (double psiInfinite, double psiInstant)[beams.Count][];

                    for (int i = 0; i < beams.Count; i++)
                    {
                        GH_CompositeBeam beam = beams[i];

                        bool constantSection = false;

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

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

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

                        MaterialModel start_concreteModel = beam.Value.StartBeamProperty.ConcreteMaterial;
                        MaterialModel start_steelModel = beam.Value.StartBeamProperty.SteelMaterial;
                        MaterialModel start_rebarModel = beam.Value.StartBeamProperty.RebarMaterial;

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

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

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

                        MaterialModel end_concreteModel = beam.Value.EndBeamProperty.ConcreteMaterial;
                        MaterialModel end_steelModel = beam.Value.EndBeamProperty.SteelMaterial;
                        MaterialModel end_rebarModel = beam.Value.EndBeamProperty.RebarMaterial;

                        double homogenizedFactorInfinite = beam.Value.StartBeamProperty.NInfinite;
                        double homogenizedFactorInstant = beam.Value.StartBeamProperty.NInstant;

                        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;
                            }
                        }

                        var normStationsSet = new HashSet<double>();
                        for (int c = 0; c < model.Value.Combinations.Count; c++)
                        {
                            BaseCaseCombinationModel combo = model.Value.Combinations[c];
                            foreach (KeyValuePair<CaseModel, double> kvp in combo.Values)
                            {
                                CaseModel loadCase = kvp.Key;
                                double coeff = kvp.Value;

                                var considerStationResult = new List<BeamForceResultModel>();
                                for (int ll = 0; ll < beam.Value.Results.Count; ll++)
                                {
                                    if (beam.Value.Results[ll] is BeamForceResultModel beamForceResultModel)
                                    {
                                        double dist = 0;
                                        if (beamForceResultModel.NormalizedLength)
                                            dist = beamForceResultModel.Station;
                                        else
                                            dist = beamForceResultModel.Station / beam.Value.Length;

                                        if (dist < 0)
                                            dist = 0;
                                        if (dist > 1.0)
                                            dist = 1.0;

                                        normStationsSet.Add(dist);
                                    }
                                }
                            }
                        }

                        List<double> stations = [.. normStationsSet];

                        psis[i] = new (double psiInfinite, double psiInstant)[stations.Count];
                        sectionCheckerModelCode2010s[i] = new SectionCheckerModelCode2010[stations.Count];

                        if (constantSection)
                        {
                            try
                            {
                                for (int k = 0; k < stations.Count; k++)
                                {
                                    var sectionH = new SectionH(start_h, start_wt, start_tfw, start_tft, start_bfw, start_bft, beam.Value.BeamId);

                                    var concreteMaterialEN1992 = new ConcreteMaterialEN1992(start_concreteModel.Name, start_concreteModel.SpecificCompressiveStrength,
                                        ConcreteMaterial.CompressionStressStrainDiagrams.ParabolaRectangle);
                                    var steelMaterialEN1992 = new SteelMaterialEN1992(start_steelModel.Name, start_steelModel.Modulus, start_steelModel.MinimumYieldStress, start_steelModel.MinimumTensileStress,
                                        0.1, SteelMaterial.StressStrainCurveType.ElasticPerfectPlastic, SteelMaterial.SteelTypes.Structural);
                                    var rebarMaterial = new SteelMaterialEN1992(start_rebarModel.Name, start_rebarModel.Modulus, start_rebarModel.MinimumTensileStress, start_rebarModel.ExpectedTensileStress,
                                        0.1, SteelMaterial.StressStrainCurveType.ElasticPerfectPlastic, SteelMaterial.SteelTypes.Rebar);

                                    int numberRebars = 3;
                                    double rebarAreaTop = start_topRebarsAreaTot / numberRebars;
                                    double rebarAreaBottom = start_bottomRebarsAreaTot / numberRebars;
                                    double rebarDiameterTop = Math.Sqrt(rebarAreaTop * 4 / Math.PI);
                                    double rebarDiameterBottom = Math.Sqrt(rebarAreaBottom * 4 / Math.PI);

                                    RebarSectionCircular rebarTop = rebarDiameterTop != 0 ? new RebarSectionCircular("", rebarDiameterTop, rebarMaterial) : null;
                                    RebarSectionCircular rebarBottom = rebarDiameterBottom != 0 ? new RebarSectionCircular("", rebarDiameterBottom, rebarMaterial) : null;

                                    var standardEC2 = new StandardEN1992p11();
                                    var standardEC3 = new StandardEN1993p11();

                                    double phiInfinite = ReinforcedConcreteSection.CalculateHomogenizedFactorPhi(homogenizedFactorInfinite, steelMaterialEN1992, concreteMaterialEN1992);
                                    double phiInstant = ReinforcedConcreteSection.CalculateHomogenizedFactorPhi(homogenizedFactorInstant, steelMaterialEN1992, concreteMaterialEN1992);

                                    var reinforcedConcreteSection = new ReinforcedConcreteSection(start_b, start_hh, concreteMaterialEN1992, rebarTop, start_b / numberRebars,
                                        start_topRebarsCover, rebarBottom, start_b / numberRebars, sectionH, steelMaterialEN1992, start_topRebarsCover, start_verticalOffset, beam.Value.StartBeamProperty.Name);

                                    reinforcedConcreteSection.SteelSections[0].Section.ThinWalls[SectionH.ThinWallIndex.TopFlange].SubjectToLocalInstability = topFlangeInstab;
                                    reinforcedConcreteSection.SteelSections[0].Section.ThinWalls[SectionH.ThinWallIndex.Web].SubjectToLocalInstability = webFlangeInstab;
                                    reinforcedConcreteSection.SteelSections[0].Section.ThinWalls[SectionH.ThinWallIndex.BottomFlange].SubjectToLocalInstability = bottomFlangeInstab;
                                    CoordinateSystem cs = GetCoordinateSystem(reinforcedConcreteSection);

                                    var options = new SectionCheckerModelCode2010.SectionOptionsModelCode2010(cs,
                                        SectionSolver.FailureAnalysisTypes.ConstantN, SectionSolver.FailureDomainTypes.Plastic, SectionSolver.StressAnalysisTypes.Linear, phiInfinite, 0, false, 64, effectiveEpsilon)
                                    {
                                        LocalBucklingMaxIterations = _class4MaxIterations
                                    };

                                    var sectionCheckerAttribute = new SectionCheckerAttribute(reinforcedConcreteSection, null, null);

                                    psis[i][k] = (phiInfinite, phiInstant);
                                    sectionCheckerModelCode2010s[i][k] = new SectionCheckerModelCode2010(sectionCheckerAttribute, options, standardEC2, false, -1, standardEC3);
                                }
                            }
                            catch (Exception)
                            {

                            }
                        }
                        else
                        {
                            for (int k = 0; k < stations.Count; k++)
                            {
                                double currentStation = stations[k];
                                double h = Interpolation.GetLinearInterpolation(0, 1.0, start_h, end_h, currentStation);
                                double wt = Interpolation.GetLinearInterpolation(0, 1.0, start_wt, end_wt, currentStation);
                                double tfw = Interpolation.GetLinearInterpolation(0, 1.0, start_tfw, end_tfw, currentStation);
                                double tft = Interpolation.GetLinearInterpolation(0, 1.0, start_tft, end_tft, currentStation);
                                double bfw = Interpolation.GetLinearInterpolation(0, 1.0, start_bfw, end_bfw, currentStation);
                                double bft = Interpolation.GetLinearInterpolation(0, 1.0, start_bft, end_bft, currentStation);

                                var sectionH = new SectionH(h, wt, tfw, tft, bfw, bft, beam.Value.BeamId);

                                double fck = Interpolation.GetLinearInterpolation(0, 1.0, start_concreteModel.SpecificCompressiveStrength, end_concreteModel.SpecificCompressiveStrength, currentStation);
                                double mysS = Interpolation.GetLinearInterpolation(0, 1.0, start_steelModel.MinimumYieldStress, end_steelModel.MinimumYieldStress, currentStation);
                                double mtsS = Interpolation.GetLinearInterpolation(0, 1.0, start_steelModel.MinimumTensileStress, end_steelModel.MinimumTensileStress, currentStation);
                                double eS = Interpolation.GetLinearInterpolation(0, 1.0, start_steelModel.Modulus, end_steelModel.Modulus, currentStation);
                                double mysR = Interpolation.GetLinearInterpolation(0, 1.0, start_rebarModel.MinimumYieldStress, end_rebarModel.MinimumYieldStress, currentStation);
                                double mtsR = Interpolation.GetLinearInterpolation(0, 1.0, start_rebarModel.MinimumTensileStress, end_rebarModel.MinimumTensileStress, currentStation);
                                double eR = Interpolation.GetLinearInterpolation(0, 1.0, start_rebarModel.Modulus, end_rebarModel.Modulus, currentStation);

                                var concreteMaterialEN1992 = new ConcreteMaterialEN1992(start_concreteModel.Name, fck,
                                    ConcreteMaterial.CompressionStressStrainDiagrams.ParabolaRectangle);
                                var steelMaterialEN1992 = new SteelMaterialEN1992(start_steelModel.Name, eS, mysS, mtsS,
                                    0.1, SteelMaterial.StressStrainCurveType.ElasticPerfectPlastic, SteelMaterial.SteelTypes.Structural);
                                var rebarMaterial = new SteelMaterialEN1992(start_rebarModel.Name, eR, mysR, mtsR,
                                    0.1, SteelMaterial.StressStrainCurveType.ElasticPerfectPlastic, SteelMaterial.SteelTypes.Rebar);

                                double rebarsAreaTop = Interpolation.GetLinearInterpolation(0, 1.0, start_topRebarsAreaTot, end_topRebarsAreaTot, currentStation);
                                double rebarsAreaBottom = Interpolation.GetLinearInterpolation(0, 1.0, start_bottomRebarsAreaTot, end_bottomRebarsAreaTot, currentStation);

                                int numberRebars = 3;
                                double rebarAreaTop = rebarsAreaTop / numberRebars;
                                double rebarAreaBottom = rebarsAreaBottom / numberRebars;
                                double rebarDiameterTop = Math.Sqrt(rebarAreaTop * 4 / Math.PI);
                                double rebarDiameterBottom = Math.Sqrt(rebarAreaBottom * 4 / Math.PI);

                                RebarSectionCircular rebarTop = rebarDiameterTop != 0 ? new RebarSectionCircular("", rebarDiameterTop, rebarMaterial) : null;
                                RebarSectionCircular rebarBottom = rebarDiameterBottom != 0 ? new RebarSectionCircular("", rebarDiameterBottom, rebarMaterial) : null;

                                var standardEC2 = new StandardEN1992p11();
                                var standardEC3 = new StandardEN1993p11();

                                double phiInfinite = ReinforcedConcreteSection.CalculateHomogenizedFactorPhi(homogenizedFactorInfinite, steelMaterialEN1992, concreteMaterialEN1992);
                                double phiInstant = ReinforcedConcreteSection.CalculateHomogenizedFactorPhi(homogenizedFactorInstant, steelMaterialEN1992, concreteMaterialEN1992);

                                var reinforcedConcreteSection = new ReinforcedConcreteSection(start_b, start_hh, concreteMaterialEN1992, rebarTop, start_b / numberRebars,
                                    start_topRebarsCover, rebarBottom, start_b / numberRebars, sectionH, steelMaterialEN1992, start_topRebarsCover, start_verticalOffset, beam.Value.StartBeamProperty.Name);

                                reinforcedConcreteSection.SteelSections[0].Section.ThinWalls[SectionH.ThinWallIndex.TopFlange].SubjectToLocalInstability = topFlangeInstab;
                                reinforcedConcreteSection.SteelSections[0].Section.ThinWalls[SectionH.ThinWallIndex.Web].SubjectToLocalInstability = webFlangeInstab;
                                reinforcedConcreteSection.SteelSections[0].Section.ThinWalls[SectionH.ThinWallIndex.BottomFlange].SubjectToLocalInstability = bottomFlangeInstab;
                                CoordinateSystem cs = GetCoordinateSystem(reinforcedConcreteSection);

                                var options = new SectionCheckerModelCode2010.SectionOptionsModelCode2010(cs,
                                    SectionSolver.FailureAnalysisTypes.ConstantN, SectionSolver.FailureDomainTypes.Plastic, SectionSolver.StressAnalysisTypes.Linear, phiInfinite, 0, false, 64, effectiveEpsilon)
                                {
                                    LocalBucklingMaxIterations = _class4MaxIterations
                                };

                                var sectionCheckerAttribute = new SectionCheckerAttribute(reinforcedConcreteSection, null, null);

                                psis[i][k] = (phiInfinite, phiInstant);
                                sectionCheckerModelCode2010s[i][k] = new SectionCheckerModelCode2010(sectionCheckerAttribute, options, standardEC2, false, -1, standardEC3);
                            }
                        }
                    }

                    var wholeStation = new List<double>[beams.Count];
                    var cumulativeDistances = new double[beams.Count];
                    for (int i = 1; i < beams.Count; i++)
                        cumulativeDistances[i] = cumulativeDistances[i - 1] + beams[i - 1].Value.Length;

                    //for (int i = 0; i < beams.Count; i++)
                    Parallel.For(0, beams.Count, i =>
                    {
                        GH_CompositeBeam beam = beams[i];

                        try
                        {
                            CompositeBeamModel considerBeam = beam.Value;// model.Value.Beams.FirstOrDefault(b => b.BeamId == beam.Value.BeamId);

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

                            if (considerBeam != null)
                            {
                                var stationBuffer = new HashSet<double>();
                                for (int k = 0; k < considerBeam.Results.Count; k++)
                                {
                                    var beamForceResultModel = considerBeam.Results[k] as BeamForceResultModel;
                                    double dist = Math.Round(beamForceResultModel.NormalizedLength ? beamForceResultModel.Station * beam.Value.Length : beamForceResultModel.Station, 15);
                                    stationBuffer.Add(dist);
                                }
                                List<double> stationB = [.. stationBuffer];
                                List<double> station = [.. stationB.OrderBy(b => b)];
                                wholeStation[i] = [.. station];
                                for (int s = 0; s < wholeStation[i].Count; s++)
                                    wholeStation[i][s] += cumulativeDistances[i];

                                forces[i] = new (ResultBeamForces[] OnlySteel, ResultBeamForces[] NInfinite, ResultBeamForces[] Ninstant)[station.Count];
                                forcesForOutput[i] = new (ResultBeamForces[] OnlySteel, ResultBeamForces[] NInfinite, ResultBeamForces[] Ninstant)[station.Count];
                                strains[i] = new (double[] StrainInfiniteTime, double[] StrainIstantTime)[station.Count];

                                for (int k = 0; k < station.Count; k++)
                                {
                                    double consideredStation = station[k];

                                    ResultBeamForces[] resultBeamForcesOnlySteels = new ResultBeamForces[model.Value.Combinations.Count];
                                    ResultBeamForces[] resultBeamForcesInfinites = new ResultBeamForces[model.Value.Combinations.Count];
                                    ResultBeamForces[] resultBeamForcesInstants = new ResultBeamForces[model.Value.Combinations.Count];
                                    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)
                                        {
                                            var resultBeamForcesOnlySteel = new ResultBeamForces(0, 0, 0, 0, 0, 0, cs, k + 1, combo.Name);
                                            var resultBeamForcesInfinite = new ResultBeamForces(0, 0, 0, 0, 0, 0, cs, k + 1, combo.Name);
                                            var resultBeamForcesInstant = new ResultBeamForces(0, 0, 0, 0, 0, 0, cs, k + 1, combo.Name);
                                            var resultBeamForcesOnlySteelForOutput = new ResultBeamForces(0, 0, 0, 0, 0, 0, cs, k + 1, combo.Name);
                                            var resultBeamForcesInfiniteForOutput = new ResultBeamForces(0, 0, 0, 0, 0, 0, cs, k + 1, combo.Name);
                                            var resultBeamForcesInstantForOutput = new ResultBeamForces(0, 0, 0, 0, 0, 0, cs, k + 1, combo.Name);

                                            double strainNIstant = 0;
                                            double strainNInfinite = 0;

                                            for (int ss = 0; ss < considerBeam.Loads.Count; ss++)
                                            {
                                                if (considerBeam.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;
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }

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

                                                var considerStationResult = new List<BeamForceResultModel>();
                                                for (int ll = 0; ll < considerBeam.Results.Count; ll++)
                                                {
                                                    if (considerBeam.Results[ll] is BeamForceResultModel beamForceResultModel)
                                                    {
                                                        double dist = 0;
                                                        if (beamForceResultModel.NormalizedLength)
                                                            dist = beamForceResultModel.Station * beam.Value.Length;
                                                        else
                                                            dist = beamForceResultModel.Station;

                                                        if (Math.Abs(dist - consideredStation) < GeometryBase.Tolerance)
                                                            considerStationResult.Add(beamForceResultModel);
                                                    }
                                                }


                                                BeamForceResultModel ff = considerStationResult.FirstOrDefault(w => w.LoadCase.Name == loadCase.Name);
                                                if (ff != null)
                                                {
                                                    var fff = new ResultBeamForces(ff.AxialForce, 0, ff.Vy, 0, ff.My, 0, cs);
                                                    var fffForOutput = new ResultBeamForces(ff.AxialForce, ff.Vx, ff.Vy, ff.Torque, ff.My, ff.Mx, cs);

                                                    fff *= coeff;
                                                    fffForOutput *= coeff;

                                                    if (((LoadCaseModel)ff.LoadCase).Stages.FirstOrDefault().BridgePhase == LoadCaseModel.BridgeStage.BridgePhases.OnlySteel)
                                                    {
                                                        resultBeamForcesOnlySteel += fff;
                                                        resultBeamForcesOnlySteelForOutput += fffForOutput;
                                                    }
                                                    if (((LoadCaseModel)ff.LoadCase).Stages.FirstOrDefault().BridgePhase == LoadCaseModel.BridgeStage.BridgePhases.NInfinite)
                                                    {
                                                        resultBeamForcesInfinite += fff;
                                                        resultBeamForcesInfiniteForOutput += fffForOutput;
                                                    }
                                                    if (((LoadCaseModel)ff.LoadCase).Stages.FirstOrDefault().BridgePhase == LoadCaseModel.BridgeStage.BridgePhases.NInstant)
                                                    {
                                                        resultBeamForcesInstant += fff;
                                                        resultBeamForcesInstantForOutput += fffForOutput;
                                                    }
                                                }
                                            }

                                            resultBeamForcesOnlySteels[c] = resultBeamForcesOnlySteel;
                                            resultBeamForcesInfinites[c] = resultBeamForcesInfinite;
                                            resultBeamForcesInstants[c] = resultBeamForcesInstant;

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

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

                                    forces[i][k] = (resultBeamForcesOnlySteels, resultBeamForcesInfinites, resultBeamForcesInstants);
                                    forcesForOutput[i][k] = (resultBeamForcesOnlySteelsForOutput, resultBeamForcesInfinitesForOutput, resultBeamForcesInstantsForOutput);
                                    strains[i][k] = (strainNInfiniteArray, strainNIstantArray);
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Fail to get forces: {e.Message}");
                        }
                    });
                    //}

                    var stressAnalysisResults = new StressAnalysisResult[beams.Count][][];
                    _tauBDataTree = new double[beams.Count][][];
                    List<(int iIdx, int jIdx, int kIdx)> failsCalc = [];

                    int parallelMaxDegree = -1;
                    //for (int i = 0; i < beams.Count; i++)
                    Parallel.For(0, beams.Count, new ParallelOptions() { MaxDegreeOfParallelism = parallelMaxDegree }, i =>
                    {
                        var beam = beams[i].Value;
                        var checker = sectionCheckerModelCode2010s[i];
                        stressAnalysisResults[i] = new StressAnalysisResult[forces[i].Length][];
                        _tauBDataTree[i] = new double[forces[i].Length][];
                        for (int j = 0; j < forces[i].Length; j++)
                        {
                            double psiInfinite = psis[i][j].psiInfinite;
                            double psiInstant = psis[i][j].psiInstant;
                            double[] strainIstant = strains[i][j].StrainIstantTime;
                            double[] strainInfinite = strains[i][j].StrainInfiniteTime;

                            (ResultBeamForces[] OnlySteel, ResultBeamForces[] NInfinite, ResultBeamForces[] Ninstant) = forces[i][j];
                            checker[j].GetLinearAnalysisResultByStages(out StressAnalysisResult[] result, out double[] tauB, psiInfinite, psiInstant, OnlySteel,
                                NInfinite, Ninstant, beam.StartBeamProperty.ConsiderConcrete, strainInfinite, strainIstant, beam.StartBeamProperty.ConsiderRebar);

                            stressAnalysisResults[i][j] = new StressAnalysisResult[result.Length];
                            _tauBDataTree[i][j] = new double[result.Length];
                            for (int k = 0; k < result.Length; k++)
                            {
                                stressAnalysisResults[i][j][k] = result[k];
                                _tauBDataTree[i][j][k] = tauB[k];
                                if (result[k] is null)
                                    failsCalc.Add((i, j, k));
                            }
                        }
                    });
                    //}

                    var orderedFailsCalc = failsCalc
                        .OrderBy(t => t.iIdx)
                        .ThenBy(t => t.jIdx)
                        .ThenBy(t => t.kIdx)
                        .ToList();

                    if (orderedFailsCalc.Count > 0)
                    {
                        AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Calculations failed: {orderedFailsCalc.Count}");
                        foreach (var (iIdx, jIdx, kIdx) in orderedFailsCalc)
                            AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Beam: {iIdx}, Station: {jIdx}, Combination: {kIdx}, Distance: {wholeStation[iIdx][jIdx]}");
                    }

                    GH_Structure<GH_StressAnalysisResultMeCheck> resultsDataTree = new();
                    _resultsDataTree = new StressAnalysisResultMeCheck[stressAnalysisResults.Length][][];
                    for (int i = 0; i < stressAnalysisResults.Length; i++)
                    {
                        _resultsDataTree[i] = new StressAnalysisResultMeCheck[stressAnalysisResults[i].Length][];
                        for (int j = 0; j < stressAnalysisResults[i].Length; j++)
                        {
                            IEnumerable<StressAnalysisResultMeCheck> stressAnalysisResultMeChecks = stressAnalysisResults[i][j].Select(sar => sar.MeCheckResults);
                            resultsDataTree.AppendRange(stressAnalysisResultMeChecks.Select(s => new GH_StressAnalysisResultMeCheck(s)), new GH_Path(i, j));
                            _resultsDataTree[i][j] = [.. stressAnalysisResultMeChecks];
                        }
                    }

                    DataTree<double> tauBResults = new();
                    for (int i = 0; i < _tauBDataTree.Length; i++)
                        for (int j = 0; j < _tauBDataTree[i].Length; j++)
                            tauBResults.AddRange(_tauBDataTree[i][j], new GH_Path(i, j));

                    DataTree<ResultBeamForces> forcesResult = new();
                    _forcesDataTree = new ResultBeamForces[forcesForOutput.Length][][];
                    for (int i = 0; i < forcesForOutput.Length; i++)
                    {
                        _forcesDataTree[i] = new ResultBeamForces[forcesForOutput[i].Length][];
                        for (int j = 0; j < forcesForOutput[i].Length; j++)
                        {
                            var list = new List<ResultBeamForces>();
                            for (int k = 0; k < forcesForOutput[i][j].OnlySteel.Length; k++)
                                list.Add(forcesForOutput[i][j].OnlySteel[k] + forcesForOutput[i][j].NInfinite[k] + forcesForOutput[i][j].Ninstant[k]);

                            forcesResult.AddRange(list, new GH_Path(i, j));
                            _forcesDataTree[i][j] = [.. list];
                        }
                    }
                    DA.SetDataTree(0, resultsDataTree);
                    //paramMeCheckStresses.PersistentData.MergeStructure(resultsDataTree);
                    DA.SetDataTree(1, tauBResults);
                    DA.SetDataTree(2, forcesResult);

                    if (!ComputeStiffeners(DA, beams, sectionCheckerModelCode2010s))
                    {
                        _run = false;
                        return;
                    }

                    Message = "Done";
                    _run = false;
                }
                catch (Exception e)
                {
                    _resultsDataTree = null;
                    _tauBDataTree = null;
                    _forcesDataTree = null;
                    ResetData();

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

            _run = false;
        }

        protected abstract void LoadReadedData(IGH_DataAccess DA);
        protected abstract void ReadAdditionalParams(IGH_DataAccess DA);
        protected abstract bool ComputeStiffeners(IGH_DataAccess DA, List<GH_CompositeBeam> beams, SectionCheckerModelCode2010[][] checkers, double tolerance = 10);
        protected abstract void ResetData();

        protected static CoordinateSystem GetCoordinateSystem(ReinforcedConcreteSection reinforcedConcreteSection)
        {
            return new CoordinateSystem(reinforcedConcreteSection.Centroid, new Vector3d(-1, 0, 0), new Vector3d(0, -1, 0));
        }

        //public override bool Write(GH_IWriter writer)
        //{
        //    bool success = base.Write(writer);
        //    if (!success)
        //        return false;

        //    if (_resultsDataTree != null)
        //        writer.SetString("ResultsDataTree", JsonConvert.SerializeObject(_resultsDataTree));
        //    if (_tauBDataTree != null)
        //        writer.SetString("TauBDataTree", JsonConvert.SerializeObject(_tauBDataTree));
        //    if (_forcesDataTree != null)
        //        writer.SetString("ForcesDataTree", JsonConvert.SerializeObject(_forcesDataTree));

        //    return true;
        //}

        //public override bool Read(GH_IReader reader)
        //{
        //    bool success = base.Read(reader);
        //    if (!success)
        //        return false;

        //    try
        //    {
        //        string results = string.Empty;
        //        if (reader.TryGetString("ResultsDataTree", ref results) && !string.IsNullOrWhiteSpace(results))
        //            _resultsDataTree = JsonConvert.DeserializeObject<StressAnalysisResultMeCheck[][][]>(results);
        //        string tauB = string.Empty;
        //        if (reader.TryGetString("TauBDataTree", ref tauB) && !string.IsNullOrWhiteSpace(tauB))
        //            _tauBDataTree = JsonConvert.DeserializeObject<double[][][]>(tauB);
        //        string forces = string.Empty;
        //        if (reader.TryGetString("ForcesDataTree", ref forces) && !string.IsNullOrWhiteSpace(forces))
        //            _forcesDataTree = JsonConvert.DeserializeObject<ResultBeamForces[][][]>(forces);
        //    }
        //    catch
        //    {
        //        _resultsDataTree = null;
        //        _tauBDataTree = null;
        //        _forcesDataTree = null;
        //    }

        //    _just_loaded = true;

        //    return true;
        //}

        /// <summary>
        /// Provides an Icon for the component.
        /// </summary>
        protected override System.Drawing.Bitmap Icon => Properties.Resources.SuperElementCheckIcon2;
    }
}
303 files24 directories