using Dlubal.WS.Rfem6.Application;
using Dlubal.WS.Rfem6.Model;
using System;
using System.IO;
using System.ServiceModel;
using System.Text.Json.Serialization.Metadata;
using ApplicationClient = Dlubal.WS.Rfem6.Application.RfemApplicationClient;
using ModelClient = Dlubal.WS.Rfem6.Model.RfemModelClient;

namespace SetConstructionStages
{
    internal class Program
    {
        public static EndpointAddress Address { get; set; } = new EndpointAddress("http://localhost:8081");


        private static BasicHttpBinding Binding
        {
            get
            {
                BasicHttpBinding binding = new BasicHttpBinding
                {
                    // Send timeout is set to 180 seconds.
                    SendTimeout = new TimeSpan(0, 0, 180),
                    UseDefaultWebProxy = true,
                };

                return binding;
            }
        }

        //private static RfemApplicationClient application = null;
        private static ApplicationClient application = null;

        static void Main(string[] args)
        {

            string CurrentDirectory = Directory.GetCurrentDirectory();
            #region Application Settings
            try
            {
                application_information ApplicationInfo;
                try
                {
                    // connects to RFEM6 or RSTAB9 application
                    application = new ApplicationClient(Binding, Address);

                }
                catch (Exception exception)
                {
                    if (application != null)
                    {
                        if (application.State != CommunicationState.Faulted)
                        {
                            application.Close();
                        }
                        else
                        {
                            application.Abort();
                        }

                        application = null;
                    }
                }
                finally
                {
                    ApplicationInfo = application.get_information();
                    Console.WriteLine("Name: {0}, Version:{1}, Type: {2}, language: {3} ", ApplicationInfo.name, ApplicationInfo.version, ApplicationInfo.type, ApplicationInfo.language_name);
                }
                #endregion

                // creates new model
                string modelUrl = application.get_active_model();

                // connects to RFEM6/RSTAB9 model
                ModelClient model = new ModelClient(Binding, new EndpointAddress(modelUrl));

                int[] node_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_NODE, 0);
                int[] line_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_LINE, 0);
                int[] member_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_MEMBER, 0);
                int[] surface_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_SURFACE, 0);
                long n_elements = 0;
                double perc_elements = 0;
                long percents = 0;

                //  print percentage scala
                Console.WriteLine("0%----------------------25%----------------------50%----------------------75%-------------------100%");
                
                //  get all nodes
                Console.WriteLine("1. Get all nodes:");
                n_elements = 0;
                percents = 1;
                perc_elements = node_nums.Length / 100.0;
                node[] nds = new node[node_nums.Length];
                for (int i = 0; i < node_nums.Length; ++i)
                {
                    nds[i] = model.get_node(node_nums[i]);
                    n_elements++;
                    if (n_elements > perc_elements)
                    {
                        Console.Write("=");
                        percents++;
                        perc_elements = node_nums.Length / 100.0 * percents;
                    }
                }
                Console.WriteLine("");

                Console.WriteLine("2. Get selected nodes");
                //  get all selected objects
                object_location[] obj_locs = model.get_all_selected_objects();

                //  get all selected node numbers
                double[] floor_heights = new double[1];
                foreach (object_location obj in obj_locs)
                {
                    if (obj.type == object_types.E_OBJECT_TYPE_NODE)
                    {
                        for (int i = 0; i < nds.Length; ++i)
                        {
                            if (nds[i].no == obj.no)
                            {
                                floor_heights[floor_heights.Length - 1] = nds[i].coordinate_3;
                                Array.Resize(ref floor_heights, floor_heights.Length + 1);
                                break;
                            }
                        }
                    }
                }
                Array.Resize(ref floor_heights, floor_heights.Length - 1);

                //  sort array
                //  z-axis is negative, most positive value is ground
                Array.Sort(floor_heights);
                Array.Reverse(floor_heights);
                //  ground and first level are one, remove first entry
                double[] tmp_arr = new double[floor_heights.Length - 1];
                Array.Copy(floor_heights, 1, tmp_arr, 0, floor_heights.Length - 1);

                floor_heights = null;
                floor_heights = tmp_arr;

                //  get all lines
                Console.WriteLine("3. Get all lines:");
                n_elements = 0;
                percents = 1;
                perc_elements = line_nums.Length / 100.0;
                line[] lns = new line[line_nums.Length];
                for (int i = 0; i < line_nums.Length; ++i)
                {
                    lns[i] = model.get_line(line_nums[i]);
                    n_elements++;
                    if (n_elements > perc_elements)
                    {
                        Console.Write("=");
                        percents++;
                        perc_elements = line_nums.Length / 100.0 * percents;
                    }
                }
                Console.WriteLine("");


                //  get all members
                Console.WriteLine("4. Get all members:");
                n_elements = 0;
                percents = 1;
                perc_elements = member_nums.Length / 100.0;
                member[] mems = new member[member_nums.Length];
                for (int i = 0; i < member_nums.Length; ++i)
                {
                    mems[i] = model.get_member(member_nums[i]);
                    n_elements++;
                    if (n_elements > perc_elements)
                    {
                        Console.Write("=");
                        percents++;
                        perc_elements = member_nums.Length / 100.0 * percents;
                    }
                }
                Console.WriteLine("");

                //  get all surfaces
                Console.WriteLine("5. Get all surfaces:");
                n_elements = 0;
                percents = 1;
                perc_elements = surface_nums.Length / 100.0;
                surface[] srfs = new surface[surface_nums.Length];
                for (int i = 0; i < surface_nums.Length; ++i)
                {
                    srfs[i] = model.get_surface(surface_nums[i]);
                    n_elements++;
                    if (n_elements > perc_elements)
                    {
                        Console.Write("=");
                        percents++;
                        perc_elements = surface_nums.Length / 100.0 * percents;
                    }
                }
                Console.WriteLine("");

                //for(int i = 0; i < floor_heights.Length; ++i)
                //{
                //    Console.WriteLine("Height " + i + " is " + floor_heights[i]);
                //}

                //  create floor number arrays
                List<List<int>> nds_floor_numbers = new List<List<int>>();
                List<List<int>> lns_floor_numbers = new List<List<int>>();
                List<List<int>> mems_floor_numbers = new List<List<int>>();
                List<List<int>> srfs_floor_numbers = new List<List<int>>();

                //  init lists
                for (int i = 0; i < floor_heights.Length; ++i)
                {
                    nds_floor_numbers.Add(new List<int>());
                    lns_floor_numbers.Add(new List<int>());
                    mems_floor_numbers.Add(new List<int>());
                    srfs_floor_numbers.Add(new List<int>());
                }


                //  loop through nodes and set their floor
                Console.WriteLine("6. Loop through nodes and get their floor");
                for (int i = 0; i < nds.Length; ++i)
                {
                    for (int j = 0; j < floor_heights.Length; ++j)
                    {
                        if (nds[i].coordinate_3 >= floor_heights[j] - 0.001)
                        {
                            nds[i].comment = j.ToString();
                            //Console.WriteLine("node " + nds[i] + " floor " + j + ";" + nds[i].coordinate_3 + ";" + (floor_heights[j] - 0.001));
                            nds_floor_numbers[j].Add(nds[i].no);
                            //model.set_node(nds[i]);
                            break;
                        }
                    }

                }

                //  loop through lines, get their nodes and set their floor
                Console.WriteLine("7. Loop through lines and get their floor");
                for (int i = 0; i < lns.Length; ++i)
                {
                    //  get nodes of line
                    int[] ln_node_nums = lns[i].definition_nodes;
                    Int32 floor_max = 0;
                    //  loop through nodes of line
                    for (int j = 0; j < ln_node_nums.Length; ++j)
                    {
                        //  loop through nodes
                        for (int l = 0; l < nds.Length; ++l)
                        {
                            if (nds[l].no == ln_node_nums[j])
                            {
                                Int32 floor = Int32.Parse(nds[l].comment);
                                if (floor > floor_max)
                                {
                                    floor_max = floor;
                                    break;
                                }
                            }
                        }

                    }
                    //  enter maxiumum floor in line
                    lns[i].comment = floor_max.ToString();
                    lns_floor_numbers[floor_max].Add(lns[i].no);
                    //model.set_line(lns[i]);

                }
                //  loop through members, get their line and set their floor
                Console.WriteLine("8. Loop through members and get their floor");
                for (int i = 0; i < mems.Length; ++i)
                {
                    //  get line number of member
                    int mem_ln_num = mems[i].line;

                    //  loop through lines
                    for (int j = 0; j < lns.Length; ++j)
                    {
                        if (lns[j].no == mem_ln_num)
                        {
                            mems[i].comment = lns[j].comment;
                            mems_floor_numbers[Int32.Parse(lns[j].comment)].Add(mems[i].no);
                            break;
                        }
                    }

                }
                //  loop through surfaces, get their lines and set their floor
                Console.WriteLine("9. Loop through surfaces and get their floor");
                for (int i = 0; i < srfs.Length; ++i)
                {
                    //  get lines of surface
                    int[] srf_line_nums = srfs[i].boundary_lines;
                    Int32 floor_max = 0;
                    //  loop through lines of surface
                    for (int j = 0; j < srf_line_nums.Length; ++j)
                    {
                        //  loop through lines
                        for (int l = 0; l < lns.Length; ++l)
                        {
                            if (lns[l].no == srf_line_nums[j])
                            {
                                Int32 floor = Int32.Parse(lns[l].comment);
                                if (floor > floor_max)
                                {
                                    floor_max = floor;
                                    break;
                                }
                            }
                        }

                    }
                    //  enter maxiumum floor in surface
                    srfs[i].comment = floor_max.ToString();
                    srfs_floor_numbers[floor_max].Add(srfs[i].no);

                    //model.set_surface(srfs[i]);

                }

                Console.WriteLine("10. Set construction stages");
                n_elements = 0;
                try
                {
                    model.begin_modification("set construction stages");
                    //  create construction stages
                    for (int i = 0; i < floor_heights.Length; ++i)
                    {
                        construction_stage cstg = new construction_stage();

                        cstg.no = i + 1;
                        cstg.continue_on_construction_stage = i;
                        cstg.continue_on_construction_stageSpecified = true;

                        cstg.are_members_enabled_to_modify = true;
                        cstg.are_members_enabled_to_modifySpecified = true;
                        cstg.added_members = mems_floor_numbers[i].ToArray();
                        cstg.active_members = cstg.added_members;

                        cstg.are_surfaces_enabled_to_modify = true;
                        cstg.are_surfaces_enabled_to_modifySpecified = true;
                        cstg.added_surfaces = srfs_floor_numbers[i].ToArray();
                        cstg.active_surfaces = cstg.added_surfaces;

                        cstg.name = "floor " + i;
                        cstg.user_defined_name_enabled = true;
                        cstg.user_defined_name_enabledSpecified = true;

                        model.set_construction_stage(cstg);

                    }
                }
                catch (Exception exception)
                {
                    model.cancel_modification();
                    Console.WriteLine("Something happen when creation of geometry" + exception.Message);
                    throw;
                }
                finally
                {
                    try
                    {
                        model.finish_modification();
                    }
                    catch (Exception exception)
                    {
                        Console.WriteLine("Something happen when finishing creation of geometry" + exception.Message);
                        throw;
                    }
                }

            }
            catch (Exception exception)
            {
                if (application != null)
                {
                    if (application.State != CommunicationState.Faulted)
                    {
                        application.Close();
                    }
                    else
                    {
                        application.Abort();
                    }

                    application = null;
                }
            }
            finally
            {
            }
        }
    }
}

