using FeMM.Common.Models;
using FeMM.Grasshopper.DataTypes.FeMM;
using FeMM.Grasshopper.Helpers;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Special;
using Rhino.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;

namespace FeMM.Grasshopper.Components.Results
{
#if NETCOREAPP
    [SupportedOSPlatform("windows")]
#endif
    public class DisplacementModelComponent : GH_Component
    {
        /// <summary>
        /// Initializes a new instance of the DecomposeModelComponent class.
        /// </summary>
        public DisplacementModelComponent()
          : base("Displacement Model", "DM", "Apply the displacements to a FEMM model", CategoryNameConstants.CATEGORY_FEMM, CategoryNameConstants.SUBCATEGORY_RESULTS)
        {
        }

        /// <summary>
        /// Registers all the input parameters for this component.
        /// </summary>
        protected override void RegisterInputParams(GH_InputParamManager pManager)
        {
            pManager.AddGenericParameter("Model", "M", "The FEM model", GH_ParamAccess.item);
            int n = pManager.AddTextParameter("Analysis Results", "AR", "The analysis result cases or combination to returns", GH_ParamAccess.item);
            pManager[n].Optional = true;
            n = pManager.AddTextParameter("StepType", "ST", "The analysis result cases or combination to returns", GH_ParamAccess.item, "1");
            pManager[n].Optional = true;
            n = pManager.AddTextParameter("Step Num", "SN", "The analysis result cases or combination to returns", GH_ParamAccess.item, "");
            pManager[n].Optional = true;
            pManager.AddNumberParameter("Scale Factor", "SF", "The scale factor multiplier", GH_ParamAccess.item, 1);
        }

        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_OutputParamManager pManager)
        {
            pManager.AddGenericParameter("Model", "M", "The displaced model", GH_ParamAccess.item);
        }

        /// <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)
        {
            GH_Model model = null;
            double multipl = 1;

            if (!DA.GetData("Model", ref model))
                return;
            if (model.Value == null)
                return;

            CaseModel[] cases = [.. model.Value.Elements.SelectMany(e => e.Results).Select(l => l.LoadCase)];
            double[] stepNums = [.. model.Value.Elements.SelectMany(e => e.Results).Select(l => l.StepNum)];
            string[] stepTypes = [.. model.Value.Elements.SelectMany(e => e.Results).Select(l => l.StepType)];

            // Connect the ValueList component on the analysis results input item
            IGH_Param[] keys_analysisResultLists = [.. Params.Input[1].Sources.Where(s => s.GetType() == typeof(GH_ValueList))];
            for (int i = 0; i < keys_analysisResultLists.Length; i++)
            {
                GH_ValueList vallist = (GH_ValueList)keys_analysisResultLists[i];
                if (vallist.ListMode != GH_ValueListMode.CheckList)
                    vallist.ListMode = GH_ValueListMode.CheckList;

                List<string> names = [.. cases.Select(lc => lc.Name).Distinct()];
                ComponentsHelper.SetValueList(vallist, names);
                vallist.NickName = "Results";
            }
            if (keys_analysisResultLists.Length > 1)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Only one case can be selected");
                return;
            }

            IGH_Param[] keys_stepNumList = [.. Params.Input[2].Sources.Where(s => s.GetType() == typeof(GH_ValueList))];
            for (int i = 0; i < keys_stepNumList.Length; i++)
            {
                GH_ValueList vallist = (GH_ValueList)keys_stepNumList[i];
                if (vallist.ListMode != GH_ValueListMode.CheckList)
                    vallist.ListMode = GH_ValueListMode.CheckList;

                List<string> names = [.. stepTypes.Select(lc => lc).Distinct()];
                ComponentsHelper.SetValueList(vallist, names);
                vallist.NickName = "Step Type";
            }
            if (keys_stepNumList.Length > 1)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Only one step type can be selected");
                return;
            }

            IGH_Param[] keys_stepTypeList = [.. Params.Input[3].Sources.Where(s => s.GetType() == typeof(GH_ValueList))];
            for (int i = 0; i < keys_stepTypeList.Length; i++)
            {
                GH_ValueList vallist = (GH_ValueList)keys_stepTypeList[i];
                if (vallist.ListMode != GH_ValueListMode.CheckList)
                    vallist.ListMode = GH_ValueListMode.CheckList;

                List<string> names = [.. stepNums.Select(lc => lc.ToString()).Distinct()];
                ComponentsHelper.SetValueList(vallist, names);
                vallist.NickName = "Step Num";
            }
            if (keys_stepTypeList.Length > 1)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Only one step num can be selected");
                return;
            }

            string selectedCase = "";
            string stepNum = "";
            string stepType = "";
            if (!DA.GetData(1, ref selectedCase))
                return;
            DA.GetData(2, ref stepType);
            DA.GetData(3, ref stepNum);
            DA.GetData(4, ref multipl);

            ModelModel cloneModel = (ModelModel)model.Value.Clone();

            var oldNewPointMap = new Dictionary<Point3d, Point3d>();

            for (int i = 0; i < cloneModel.Nodes.Count; i++)
            {
                NodeModel node = cloneModel.Nodes[i];
                NodeDisplResultModel displ = null;
                List<NodeDisplResultModel> res = [.. node.Results.Where(k => k is NodeDisplResultModel).Where(j => j.LoadCase.Name == selectedCase).Select(j => (NodeDisplResultModel)j)];

                if (res != null)
                {
                    if (res.Count > 1)
                    {
                        if (!int.TryParse(stepNum, out int stp))                        
                            stp = 1;
                        
                        List<NodeDisplResultModel> list = [.. res.Where(j => j.StepNum == stp)];
                        if (list.Count != 0)
                        {
                            if (list.Count > 1)                            
                                displ = list.Where(k => k.StepType == stepType).FirstOrDefault();                            
                            else                            
                                displ = list.FirstOrDefault();                            
                        }
                        else
                        {
                            displ = res.Where(j => j.StepType == stepType).FirstOrDefault();
                        }
                    }
                    else
                        displ = res.FirstOrDefault();

                    if (displ != null)
                    {
                        Point3d newPoint = node.Position + displ.Displacement * multipl;
                        oldNewPointMap.Add(node.Position, newPoint);
                        node.Position = newPoint;
                    }
                }
            }

            for (int i = 0; i < cloneModel.Beams.Count; i++)
            {
                BeamModel beam = cloneModel.Beams[i];

                beam.PointFrom = oldNewPointMap[beam.PointFrom];
                beam.PointTo = oldNewPointMap[beam.PointTo];
            }

            for (int i = 0; i < cloneModel.Links.Count; i++)
            {
                LinkModel link = cloneModel.Links[i];

                link.PointFrom = oldNewPointMap[link.PointFrom];
                link.PointTo = oldNewPointMap[link.PointTo];
            }

            for (int i = 0; i < cloneModel.Plates.Count; i++)
            {
                PlateModel plate = cloneModel.Plates[i];

                for (int j = 0; j < plate.Points.Count; j++)
                    plate.Points[j] = oldNewPointMap[plate.Points[j]];
            }

            var nodes = new List<GH_Node>();
            var beams = new List<GH_Beam>();
            var plates = new List<GH_Plate>();
            var links = new List<GH_Link>();

            try
            {
                for (int i = 0; i < model.Value.Nodes.Count; i++)
                {
                    NodeModel node = model.Value.Nodes[i];
                    NodeModel newnode = cloneModel.Nodes[i];
                    nodes.Add(new GH_Node(newnode, model.Guids[node]));
                }

                for (int i = 0; i < model.Value.Beams.Count; i++)
                {
                    BeamModel beam = model.Value.Beams[i];
                    BeamModel newbeam = cloneModel.Beams[i];
                    beams.Add(new GH_Beam(newbeam, model.Guids[beam]));
                }

                for (int i = 0; i < model.Value.Links.Count; i++)
                {
                    var link = model.Value.Links[i];
                    var newLink = cloneModel.Links[i];
                    links.Add(new GH_Link(newLink, model.Guids[link]));
                }

                for (int i = 0; i < model.Value.Plates.Count; i++)
                {
                    PlateModel plate = model.Value.Plates[i];
                    PlateModel newplate = cloneModel.Plates[i];
                    plates.Add(new GH_Plate(newplate, model.Guids[plate]));
                }
            }
            catch (Exception e)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message);
            }

            DA.SetData(0, new GH_Model(model.Value.Units, nodes, beams, plates, links, [], []));
        }

        public override GH_Exposure Exposure => GH_Exposure.tertiary;

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

        /// <summary>
        /// Gets the unique ID for this component. Do not change this ID after release.
        /// </summary>
        public override Guid ComponentGuid => new("c934e5ca-e120-4b41-a757-2649fcbc7d52");
    }
}
303 files24 directories