using FeMM.Common.Helpers;
using FeMM.Common.Models;
using FeMM.Grasshopper.Helpers;
using GH_IO.Serialization;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Parameters;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using System.Text;
using Rh = Rhino;

namespace FeMM.Grasshopper.Components.Tools
{
#if NETCOREAPP
    [SupportedOSPlatform("windows")]
#endif
    public class SurfaceMeshComponent : GH_Component
    {
        protected List<string> _output;
        protected List<string> _errors;
        protected List<string> _warnings;

        protected bool _justCompleted;
        private bool _run;
        private bool _isRunning; // true if the st7Mesh in running in background

        private List<Mesh> _meshes;

        public SurfaceMeshComponent()
            : base("Surface mesh", "SM", "Create a mesh with Straus7 AutoMesh", CategoryNameConstants.CATEGORY_FEMM, CategoryNameConstants.SUBCATEGORY_TOOLS)
        {
            _output = [];

            _errors = [];
            _warnings = [];

            _meshes = null;
            _run = false;
        }

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

        protected override void RegisterInputParams(GH_InputParamManager pManager)
        {
            pManager.AddSurfaceParameter("Surfaces", "Ss", "The surface to mesh", GH_ParamAccess.list);
            pManager.AddIntegerParameter("Units", "U", "The measure units", GH_ParamAccess.item, 2);

            int i = 0;
            foreach (ModelModel.ModelUnits name in Enum.GetValues(typeof(ModelModel.ModelUnits)))
                ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue(name.GetDescription(), i++);

            pManager.AddIntegerParameter("CleanGeometryToleranceType", "TT", "Clean geometry tolerance type", GH_ParamAccess.item, 0);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("Relative", 0);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("Absolute", 1);

            pManager.AddNumberParameter("CleanGeometryEdgeMergeAngle", "EMA", "Clean geometry merging angle for adjacent edges", GH_ParamAccess.item, 160);
            pManager.AddNumberParameter("CleanGeometryFeatureLength", "FL", "Clean geometry feature length", GH_ParamAccess.item, 0.0005);

            pManager.AddIntegerParameter("MeshMode", "MM", "Meshing mode, either Auto or Custom", GH_ParamAccess.item, 0);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("Auto", 0);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("Custom", 1);

            pManager.AddIntegerParameter("SizeMode", "SM", "Mesh size option, either Percentage or Absolute", GH_ParamAccess.item, 1);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("Percentage", 0);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("Absolute", 1);

            pManager.AddIntegerParameter("Target Element", "TE", "Number of nodes in target element; one of 3, 4, 6 or 8", GH_ParamAccess.item, 4);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("3", 3);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("4", 4);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("6", 6);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("8", 8);

            pManager.AddBooleanParameter("ApplyTransitioning", "T", "Apply edge transitioning when placing boundary nodes", GH_ParamAccess.item, true);

            pManager.AddNumberParameter("MeshSize", "MS", "Mesh size, scaled based on SizeMode", GH_ParamAccess.item);
            pManager.AddNumberParameter("LengthRatio", "LR", "Maximum allowable ratio between the largest and smallest edge on each face", GH_ParamAccess.item, 0.1);
            pManager.AddNumberParameter("MaximumIncrease", "MI", "Rate of increase in edge length between neighbouring elements, in the range 0 to 1", GH_ParamAccess.item, 0.40);

            pManager.AddIntegerParameter("CleanMeshToleranceType", "TT", "Clean mesh tolerance type", GH_ParamAccess.item, 0);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("Relative", 0);
            ((Param_Integer)pManager[pManager.ParamCount - 1]).AddNamedValue("Absolute", 1);
            pManager.AddNumberParameter("CleanMeshTolerance", "CMT", "Zip tolerance", GH_ParamAccess.item, 0.0001);
            pManager.AddBooleanParameter("Always Run", "AR", "Always run", GH_ParamAccess.item, false);
        }

        protected override void RegisterOutputParams(GH_OutputParamManager pManager)
        {
            pManager.AddMeshParameter("Meshes", "Ms", "Mesh of surfaces", GH_ParamAccess.list);
            pManager.AddTextParameter("Output", "O", "Test", GH_ParamAccess.item);
        }

        protected override void SolveInstance(IGH_DataAccess DA)
        {
            var surfaces = new List<GH_Surface>();
            int units = 0;
            int meshMode = 0;
            int sizeMode = 0;
            bool transitioning = true;

            int tolType = 0;
            double mergeAngle = 0;
            double featLen = 0;
            int targetElement = 0;

            double meshSize = 0;
            double lenRatio = 0;
            double maxIncr = 0;

            int meshTolType = 0;
            double meshCleanTol = 0;

            bool alwaysRun = false;

            var offsets = new List<double>();
            int count = 0;

            if (!DA.GetDataList(count++, surfaces))
                return;
            if (!DA.GetData(count++, ref units))
                return;
            if (!DA.GetData(count++, ref tolType))
                return;
            if (!DA.GetData(count++, ref mergeAngle))
                return;
            if (!DA.GetData(count++, ref featLen))
                return;
            if (!DA.GetData(count++, ref meshMode))
                return;
            if (!DA.GetData(count++, ref sizeMode))
                return;
            if (!DA.GetData(count++, ref targetElement))
                return;
            if (!DA.GetData(count++, ref transitioning))
                return;
            if (!DA.GetData(count++, ref meshSize))
                return;
            if (!DA.GetData(count++, ref lenRatio) && meshMode == 1)
                return;
            if (!DA.GetData(count++, ref maxIncr) && meshMode == 1)
                return;
            if (!DA.GetData(count++, ref meshTolType))
                return;
            if (!DA.GetData(count++, ref meshCleanTol))
                return;
            if (!DA.GetData(count++, ref alwaysRun))
                return;

            _meshes = [];

            Message = "";
            if (_run || alwaysRun)
            {
                _warnings = [];
                _errors = [];

                try
                {
                    Message = "Running. Wait...";

                    var layerIds = new List<int>();
                    var objectIds = new List<Guid>();

                    //at each surface one layer, to reconstruct the mesh topology after
                    for (int i = 0; i < surfaces.Count; i++)
                    {
                        int layerId = Rh.RhinoDoc.ActiveDoc.Layers.Add($"tmp_{i}_{Guid.NewGuid()}", System.Drawing.Color.Black);
                        if (layerId < 0)
                            throw new NotSupportedException("Layer already present");
                        layerIds.Add(layerId);

                        Guid objectId = Rh.RhinoDoc.ActiveDoc.Objects.AddBrep(surfaces[i].Value);
                        Rh.DocObjects.RhinoObject rhinoObject = Rh.RhinoDoc.ActiveDoc.Objects.FindId(objectId);

                        rhinoObject.Attributes.LayerIndex = layerId;
                        rhinoObject.CommitChanges();
                        rhinoObject.Select(true);
                        objectIds.Add(objectId);
                    }

                    string unitOpt = "";
                    unitOpt = (ModelModel.ModelUnits)units switch
                    {
                        ModelModel.ModelUnits.Nmm => "_i",// mm
                        ModelModel.ModelUnits.SI or ModelModel.ModelUnits.kNm => "_e",// m
                        _ => throw new NotSupportedException("Unknown units system"),
                    };
                    string igesFile = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetTempFileName()) + ".igs");
                    // string igesFile = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetTempFileName()) + ".stp");

                    if (Rh.RhinoApp.RunScript($"! -_Export {igesFile} _S {unitOpt} _Enter _Enter", true))
                    {
                        _output.Add($"Temporary file {igesFile} created succesfully");
                    }
                    else
                    {
                        throw new IOException($"Failed to export geometry file: {igesFile}{Environment.NewLine}");
                    }

                    var worker = new BackgroundWorker();

                    var parameters = new Hashtable
                    {
                        { "surfCount", surfaces.Count },
                        { "units", units },
                        { "iges_file", igesFile },

                        { "tol_type", tolType },
                        { "merge_angle", mergeAngle },
                        { "feat_len", featLen },
                        { "target_element", targetElement},

                        { "mesh_mode", meshMode },
                        { "size_mode", sizeMode },
                        { "transitioning", transitioning },
                        { "mesh_size", meshSize },
                        { "len_ratio", lenRatio },
                        { "max_incr", maxIncr },

                        { "mesh_tol_type", meshTolType },
                        { "mesh_tol", meshCleanTol }
                    };

                    PerformSt7Mesh(parameters);

                    // Delete the temporary objects
                    foreach (Guid id in objectIds)
                        Rh.RhinoDoc.ActiveDoc.Objects.Delete(id, true);
                    foreach (int layer_idx in layerIds)
                        Rh.RhinoDoc.ActiveDoc.Layers.Delete(layer_idx, true);

                    _run = false;
                }
                catch (Exception ex)
                {
                    _errors.Add(ex.Message);
                    _isRunning = false;
                }
            }

            if (_warnings.Count != 0)
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, string.Join(Environment.NewLine, [.. _warnings]));

            if (_errors.Count != 0)
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, string.Join(Environment.NewLine, [.. _errors]));

            DA.SetDataList(0, _meshes);
            DA.SetData(1, string.Join(Environment.NewLine, [.. _output]));
        }

        /// <summary>
        /// The main calculation function.
        /// </summary>
        private void PerformSt7Mesh(Hashtable parameters)
        {
            int surfCount = Convert.ToInt32(parameters["surfCount"]);
            ModelModel.ModelUnits units = (ModelModel.ModelUnits)Convert.ToInt32(parameters["units"]);

            int tolType = Convert.ToInt32(parameters["tol_type"]);
            double mergeAngle = Convert.ToDouble(parameters["merge_angle"]);
            double featLen = Convert.ToDouble(parameters["feat_len"]);

            int meshMode = Convert.ToInt32(parameters["mesh_mode"]);
            int sizeMode = Convert.ToInt32(parameters["size_mode"]);
            int targetElement = Convert.ToInt32(parameters["target_element"]);
            bool transitioning = Convert.ToBoolean(parameters["transitioning"]);
            double meshSize = Convert.ToDouble(parameters["mesh_size"]);
            double lenRatio = Convert.ToDouble(parameters["len_ratio"]);
            double maxIncr = Convert.ToDouble(parameters["max_incr"]);
            string igesFile = parameters["iges_file"].ToString();

            int meshTolType = Convert.ToInt32(parameters["mesh_tol_type"]);
            double meshTol = Convert.ToDouble(parameters["mesh_tol"]);

            int modelId = 1;
            try
            {
                St7API.St7.St7Init();

                // Create a new model
                if (St7API.St7.St7NewFile(modelId, Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetTempFileName()) + ".st7"), Path.GetTempPath()) != 0)
                    throw new Exception("Failed to create new model");

                // Set the measure units
                int[] st7Units = new int[St7API.St7.kLastUnit];
                switch (units)
                {
                    case ModelModel.ModelUnits.Nmm:
                        st7Units[St7API.St7.ipLENGTHU] = St7API.St7.luMILLIMETRE;
                        break;
                    case ModelModel.ModelUnits.SI:
                    case ModelModel.ModelUnits.kNm:
                        st7Units[St7API.St7.ipLENGTHU] = St7API.St7.luMETRE;
                        break;
                    default:
                        throw new NotSupportedException("Unknown units");
                }

                //Other units not important here, setted always as:
                st7Units[St7API.St7.ipFORCEU] = St7API.St7.fuNEWTON;
                st7Units[St7API.St7.ipSTRESSU] = St7API.St7.suPASCAL;
                st7Units[St7API.St7.ipMASSU] = St7API.St7.muKILOGRAM;
                st7Units[St7API.St7.ipTEMPERU] = St7API.St7.tuKELVIN;
                st7Units[St7API.St7.ipENERGYU] = St7API.St7.euJOULE;

                if (St7API.St7.St7SetUnits(modelId, st7Units) != 0)
                {
                    St7API.St7.St7CloseFile(modelId);
                    throw new Exception("Failed to set the units");
                }

                // Import the file IGES
                int[] importIgsInts = new int[6];
                importIgsInts[St7API.St7.ipGeomImportProperty] = 1;
                importIgsInts[St7API.St7.ipGeomImportCurvesToBeams] = St7API.St7.btFalse;
                importIgsInts[St7API.St7.ipGeomImportGroupsAs] = St7API.St7.ggAssemblies;
                importIgsInts[St7API.St7.ipGeomImportColourAsProperty] = St7API.St7.btTrue;
                importIgsInts[St7API.St7.ipGeomImportMatchExistingProperty] = St7API.St7.btTrue;

                switch (units)
                {
                    case ModelModel.ModelUnits.Nmm:
                    case ModelModel.ModelUnits.SI:
                        importIgsInts[St7API.St7.ipGeomImportLengthUnit] = St7API.St7.luGeomMillimetre;
                        break;
                    case ModelModel.ModelUnits.kNm:
                        importIgsInts[St7API.St7.ipGeomImportLengthUnit] = St7API.St7.luGeomMetre;
                        break;
                    default:
                        throw new NotSupportedException("Unknown units");
                }

                double[] importIgsDoubles = new double[1];
                importIgsDoubles[St7API.St7.ipGeomImportTol] = 0.000001;

                if (St7API.St7.St7ImportIGES(modelId, igesFile, importIgsInts, importIgsDoubles, St7API.St7.ieQuietRun) != 0)
                // if (St7API.St7.St7ImportSTEP(modelId, igesFile, importIgsInts, importIgsDoubles, St7API.St7.ieProgressRun) != 0)
                {
                    St7API.St7.St7CloseFile(modelId);
                    if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                    {
                        File.Delete(igesFile);
                        _output.Add($"Temporary file {igesFile} deleted succesfully.");
                    }
                    throw new Exception("Failed to import the IGES file");
                }

                // Clean Geometry
                int[] cleanGeomInts = new int[5];
                cleanGeomInts[St7API.St7.ipGeometryFeatureType] = tolType == 0 ? St7API.St7.ztRelative : St7API.St7.ztAbsolute;
                cleanGeomInts[St7API.St7.ipGeometryActOnWholeModel] = St7API.St7.btTrue;
                cleanGeomInts[St7API.St7.ipGeometryFreeEdgesOnly] = St7API.St7.btFalse;
                cleanGeomInts[St7API.St7.ipGeometryDuplicateFaces] = St7API.St7.dfLeaveAll;

                double[] cleanGeomDoubles = new double[3];
                cleanGeomDoubles[St7API.St7.ipGeometryFeatureLength] = featLen;
                cleanGeomDoubles[St7API.St7.ipGeometryEdgeMergeAngle] = mergeAngle;

                if (St7API.St7.St7SetCleanGeometryOptions(modelId, cleanGeomInts, cleanGeomDoubles) != 0)
                {
                    St7API.St7.St7CloseFile(modelId);
                    if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                    {
                        File.Delete(igesFile);
                        _output.Add($"Temporary file {igesFile} deleted succesfully.");
                    }
                    throw new Exception("Failed to set the clean geometry data");
                }

                int changes = 0;
                if (St7API.St7.St7CleanGeometry(modelId, ref changes, St7API.St7.ieQuietRun) != 0)
                {
                    St7API.St7.St7CloseFile(modelId);
                    if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                    {
                        File.Delete(igesFile);
                        _output.Add($"Temporary file {igesFile} deleted succesfully.");
                    }
                    throw new Exception("Failed to clean geometry");
                }
                changes = 0;
                if (St7API.St7.St7CleanGeometry(modelId, ref changes, St7API.St7.ieQuietRun) != 0)
                {
                    St7API.St7.St7CloseFile(modelId);
                    if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                    {
                        File.Delete(igesFile);
                        _output.Add($"Temporary file {igesFile} deleted succesfully.");
                    }
                    throw new Exception("Failed to clean geometry");
                }
                // Generate mesh
                int[] meshInts = new int[10];
                if (meshMode == 0)
                    meshInts[St7API.St7.ipSurfaceMeshMode] = St7API.St7.mmAuto;
                else
                    meshInts[St7API.St7.ipSurfaceMeshMode] = St7API.St7.mmCustom;

                if (sizeMode == 0)
                    meshInts[St7API.St7.ipSurfaceMeshSizeMode] = St7API.St7.smPercentage;
                else
                    meshInts[St7API.St7.ipSurfaceMeshSizeMode] = St7API.St7.smAbsolute;

                meshInts[St7API.St7.ipSurfaceMeshTargetNodes] = targetElement;
                meshInts[St7API.St7.ipSurfaceMeshTargetPropertyID] = -1;
                meshInts[St7API.St7.ipSurfaceMeshAutoCreateProperties] = St7API.St7.btTrue;
                meshInts[St7API.St7.ipSurfaceMeshMinEdgesPerCircle] = 12;
                meshInts[St7API.St7.ipSurfaceMeshApplyTransitioning] = transitioning ? St7API.St7.btTrue : St7API.St7.btFalse;
                meshInts[St7API.St7.ipSurfaceMeshAllowUserStop] = St7API.St7.btFalse;

                double[] meshDoublesp = new double[4];
                meshDoublesp[St7API.St7.ipSurfaceMeshSize] = meshSize;
                meshDoublesp[St7API.St7.ipSurfaceMeshLengthRatio] = lenRatio;
                meshDoublesp[St7API.St7.ipSurfaceMeshMaximumIncrease] = maxIncr;
                meshDoublesp[St7API.St7.ipSurfaceMeshOnEdgesLongerThan] = 0;

                if (St7API.St7.St7SurfaceMesh(modelId, meshInts, meshDoublesp, St7API.St7.ieQuietRun) != 0)
                {
                    St7API.St7.St7CloseFile(modelId);
                    if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                    {
                        File.Delete(igesFile);
                        _output.Add($"Temporary file {igesFile} deleted succesfully.");
                    }
                    throw new Exception("Failed to mesh");
                }

                // Clean mesh
                int[] cleanMeshInts = new int[20];
                cleanMeshInts[St7API.St7.ipMeshToleranceType] = meshTolType == 0 ? St7API.St7.ztRelative : St7API.St7.ztAbsolute;
                cleanMeshInts[St7API.St7.ipActOnWholeModel] = St7API.St7.btTrue;
                cleanMeshInts[St7API.St7.ipZipNodes] = St7API.St7.btTrue;
                cleanMeshInts[St7API.St7.ipRemoveDuplicateElements] = St7API.St7.btTrue;
                cleanMeshInts[St7API.St7.ipFixElementConnectivity] = St7API.St7.btTrue;
                cleanMeshInts[St7API.St7.ipDeleteFreeNodes] = St7API.St7.btTrue;
                cleanMeshInts[St7API.St7.ipDoBeams] = St7API.St7.btTrue;
                cleanMeshInts[St7API.St7.ipDoPlates] = St7API.St7.btTrue;
                cleanMeshInts[St7API.St7.ipDoBricks] = St7API.St7.btTrue;
                cleanMeshInts[St7API.St7.ipDoLinks] = St7API.St7.btTrue;
                cleanMeshInts[St7API.St7.ipZeroLengthLinks] = St7API.St7.btTrue;
                cleanMeshInts[St7API.St7.ipZeroLengthBeams] = St7API.St7.btFalse;
                cleanMeshInts[St7API.St7.ipNodeAttributeKeep] = St7API.St7.naHigher;
                cleanMeshInts[St7API.St7.ipNodeCoordinates] = St7API.St7.ncAverage;
                cleanMeshInts[St7API.St7.ipAllowDifferentProps] = St7API.St7.btFalse;

                var cleanMeshDoubles = new double[] { meshTol }
;
                if (St7API.St7.St7SetCleanMeshOptions(modelId, cleanMeshInts, cleanMeshDoubles) != 0)
                {
                    St7API.St7.St7CloseFile(modelId);
                    if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                    {
                        File.Delete(igesFile);
                        _output.Add($"Temporary file {igesFile} deleted succesfully.");
                    }
                    throw new Exception("Filed to set the clean mesh data");
                }
                if (St7API.St7.St7CleanMesh(modelId) != 0)
                {
                    St7API.St7.St7CloseFile(modelId);
                    if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                    {
                        File.Delete(igesFile);
                        _output.Add($"Temporary file {igesFile} deleted succesfully.");
                    }
                    throw new Exception("Failed to clean mesh");
                }

                // Totals nodes and plates
                int numNodes = 0;
                int numPlates = 0;
                if (St7API.St7.St7GetTotal(modelId, St7API.St7.tyNODE, ref numNodes) != 0 || St7API.St7.St7GetTotal(modelId, St7API.St7.tyPLATE, ref numPlates) != 0)
                {
                    St7API.St7.St7CloseFile(modelId);
                    if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                    {
                        File.Delete(igesFile);
                        _output.Add($"Temporary file {igesFile} deleted succesfully.");
                    }
                    throw new Exception("Failed to retrieve the total of generated elements");
                }
                // Read nodes
                var nodes = new Dictionary<int, Point3d>();
                for (int n = 1; n <= numNodes; n++)
                {
                    double[] pos = [0, 0, 0];
                    if (St7API.St7.St7GetNodeXYZ(modelId, n, pos) != 0)
                    {
                        St7API.St7.St7CloseFile(modelId);
                        if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                        {
                            File.Delete(igesFile);
                            _output.Add($"Temporary file {igesFile} deleted succesfully.");
                        }
                        throw new Exception($"Unable to read node: {n}");
                    }

                    nodes.Add(n, new Point3d(pos[0], pos[1], pos[2]));
                }

                // Read plates
                var surfaceFaceNodeId = new List<List<int[]>>(); // indice facce per ogni surface
                var groupIdListPosition = new Dictionary<int, int>();
                int surfCounter = 0;
                for (int p = 1; p <= numPlates; p++)
                {
                    int surfId = -1;
                    int groupId = 0;
                    var groupName = new StringBuilder(St7API.St7.kMaxStrLen);

                    if (St7API.St7.St7GetEntityGroup(modelId, St7API.St7.tyPLATE, p, ref groupId) != 0)
                    {
                        St7API.St7.St7CloseFile(modelId);
                        if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                        {
                            File.Delete(igesFile);
                            _output.Add($"Temporary file {igesFile} deleted succesfully.");
                        }
                        throw new Exception($"Unable to read group of plate: {p}");
                    }

                    if (St7API.St7.St7GetGroupIDName(modelId, groupId, groupName, St7API.St7.kMaxStrLen) != 0)
                    {
                        St7API.St7.St7CloseFile(modelId);
                        if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                        {
                            File.Delete(igesFile);
                            _output.Add($"Temporary file {igesFile} deleted succesfully.");
                        }
                        throw new Exception($"Unable to get group name of group: {groupId}");
                    }

                    if (!groupIdListPosition.ContainsKey(groupId))
                    {
                        groupIdListPosition.Add(groupId, surfCounter);
                        surfaceFaceNodeId.Add([]);
                        surfCounter++;
                    }
                    surfId = groupIdListPosition[groupId];

                    int[] connection = new int[St7API.St7.kMaxElementNode];
                    if (St7API.St7.St7GetElementConnection(modelId, St7API.St7.tyPLATE, p, connection) == 0)
                    {
                        if (connection[0] == 3 || connection[0] == 4)
                            surfaceFaceNodeId[surfId].Add([.. connection.Take(connection[0] + 1).Skip(1)]);
                    }
                    else
                    {
                        St7API.St7.St7CloseFile(modelId);
                        if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                        {
                            File.Delete(igesFile);
                            _output.Add($"Temporary file {igesFile} deleted succesfully.");
                        }
                        throw new Exception($"Unable to read plate: {p}");
                    }
                }

                //Creating meshes
                _meshes = [];
                for (int i = 0; i < surfaceFaceNodeId.Count; i++)
                {
                    var mesh = new Mesh();
                    var verticesMap = new List<int>();

                    if (surfaceFaceNodeId[i] != null)
                    {
                        if (surfaceFaceNodeId[i].Count > 0)
                        {
                            for (int j = 0; j < surfaceFaceNodeId[i].Count; j++)
                            {
                                int[] face = surfaceFaceNodeId[i][j];
                                var faceNodes = new List<int>();
                                for (int k = 0; k < face.Length; k++)
                                {
                                    int nodeId = face[k];
                                    int vertexId = verticesMap.IndexOf(nodeId);
                                    if (vertexId < 0)
                                    {
                                        mesh.Vertices.Add(nodes[nodeId]);
                                        vertexId = mesh.Vertices.Count - 1;
                                        verticesMap.Add(nodeId);
                                    }
                                    faceNodes.Add(vertexId);
                                }

                                if (faceNodes.Count == 3)
                                {
                                    mesh.Faces.AddFace(faceNodes[0], faceNodes[1], faceNodes[2]);
                                }
                                else if (faceNodes.Count == 4)
                                {
                                    mesh.Faces.AddFace(faceNodes[0], faceNodes[1], faceNodes[2], faceNodes[3]);
                                }
                                else
                                {
                                    throw new NotSupportedException("Plate is not a tri or quad");
                                }
                            }

                            mesh.Compact();
                            mesh.RebuildNormals();
                            mesh.UnifyNormals();
                            mesh.Normals.ComputeNormals();
                        }
                    }
                    else
                    {
                        _warnings.Add($"Surface {i} has not been meshed successful");
                        _output.Add($"Surface {i} has not been meshed successful");
                    }

                    _meshes.Add(mesh);
                }

                St7API.St7.St7SaveFile(modelId);

                if (St7API.St7.St7CloseFile(modelId) != 0)
                    throw new Exception("Failed to close st7 file");

                St7API.St7.St7Release();

                _output.Add($"Mesh generated succesfully.");
                _output.Add($"Total amount of nodes = {numNodes}");
                _output.Add($"Total amount of plates = {numPlates}");

                Message = "Completed";
                _justCompleted = true;
                _isRunning = false;
            }
            catch (Exception ex)
            {
                _errors.Add(ex.Message);
                if (modelId >= 0)
                    St7API.St7.St7CloseFile(modelId);
                if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
                {
                    File.Delete(igesFile);
                    _output.Add($"Temporary file {igesFile} deleted succesfully.");
                }
            }
            finally
            {

            }

            if (!string.IsNullOrEmpty(igesFile) && File.Exists(igesFile))
            {
                File.Delete(igesFile);
                _output.Add($"Temporary file {igesFile} deleted succesfully.");
            }
        }

        public override bool Read(GH_IReader reader)
        {
            if (base.Read(reader))
            {
                if (reader.GetBoolean("hasMeshes"))
                {
                    _meshes = [];
                    for (int i = 0; i < reader.GetInt32("count"); i++)
                    {
                        var data = new GH_Mesh();
                        data.Read(reader.FindChunk(i.ToString()));
                        _meshes.Add(data.Value);
                    }
                }

                return true;
            }
            return false;
        }

        public override bool Write(GH_IWriter writer)
        {
            if (base.Write(writer))
            {
                bool b = _meshes != null && _meshes.Count > 0;
                writer.SetBoolean("hasMeshes", b);
                if (b)
                {
                    writer.SetInt32("count", _meshes.Count);
                    for (int i = 0; i < _meshes.Count; i++)
                    {
                        new GH_Mesh(_meshes[i]).Write(writer.CreateChunk(i.ToString()));
                    }
                }
                return true;
            }
            return false;
        }

        public override void ExpireSolution(bool recompute)
        {
            base.ExpireSolution(recompute);

            _errors.Clear();
            _warnings.Clear();
            _output.Clear();
        }

        protected override System.Drawing.Bitmap Icon => Properties.Resources.SurfaceMeshIcon;

        public override Guid ComponentGuid => new("071BF3ED-B38F-4BFC-AE79-062AE6381BD9");
    }
}
303 files24 directories