import dlubal.api.rfem as rfem
import dlubal.api.common as common
from app.helpers.data_frame_helper import PandasDisplayHelper
from app.helpers.rfem_helper import close_and_create_project_by_name
import app.helpers.print_helper as print_helper
from math import inf
import math

# Set pandas display option to make e.g. sure that all columns are shown in printout of table
PandasDisplayHelper.set_display_options()

def set_concrete_design_global_settings(rfem_app: rfem.Application):
    # Get global settings tree table for Concrete Design add-on
    design_settings: rfem.GlobalSettingsTreeTable = rfem_app.get_design_settings(
        addon=rfem.DesignAddons.CONCRETE_DESIGN
    )
    # print_helper.header1(f'DesignAddons.CONCRETE_DESIGN:\n{design_settings}')

def set_concrete_design_configurations(rfem_app: rfem.Application):
    # Retrieve the Design ULS Configuration TreeTable for Concrete add-on
    concrete_uls_config_no3 = rfem.concrete_design_objects.ConcreteDesignUlsConfiguration(
        no=3,
        name='Slabs',
        assigned_to_surfaces=[4, 5],
    )
    print(f'*****************************************************************')
    print_helper.header1_top('ConcreteDesignUlsConfiguration no=3 for Slabs')

    # Use empty SettingsSurfaceEc2TreeTable from new ULS Configuration
    settings_surface_ec2_uls_current = concrete_uls_config_no3.settings_surface_ec2
    chapter = 'surface_internal_forces'
    key1 = 'surface_internal_forces_no_optimization'
    design_path = [
        chapter,
        key1,
    ]
    design_value = False
    print(f"{design_path}: {design_value}")
    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )

    key1 = 'surface_internal_forces_optimization'
    design_path = [
        chapter,
        key1,
    ]
    design_value = True
    print(f"{design_path}: {design_value}")
    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )
    print_helper.header1_bottom()

    # Create the new ULS Configuration in the model
    rfem_app.create_object(obj=concrete_uls_config_no3)

    # Create a new ULS Configuration for Walls
    print_helper.header1_top(f'ConcreteDesignUlsConfiguration no=2 for Walls')
    concrete_uls_config_no2 = rfem.concrete_design_objects.ConcreteDesignUlsConfiguration(
        no=2,
        name='Walls',
        assigned_to_surfaces=[1, 2, 3, 6],
    )

    # Use empty SettingsSurfaceEc2TreeTable from new ULS Configuration
    settings_surface_ec2_uls_current = concrete_uls_config_no2.settings_surface_ec2
    chapter = 'surface_internal_forces'
    key1 = 'surface_internal_forces_no_optimization'
    design_path = [
        chapter,
        key1,
    ]
    design_value = True
    print(f"{design_path}: {design_value}")
    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )
    key1 = 'surface_internal_forces_optimization'
    design_path = [
        chapter,
        key1,
    ]
    design_value = False
    print(f"{design_path}: {design_value}")
    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )
    chapter = 'limits_of_reinforcement_areas'
    key1 = 'minimum_longitudinal_reinforcement_acc_to_standard'
    design_path = [
        chapter,
        key1,
    ]
    design_value = common.get_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
    )
    print(f"{design_path}: {design_value}")
    design_value = True
    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )
    key2 = 'minimum_longitudinal_reinforcement_for_plates'
    design_path = [
        chapter,
        key1,
        key2,
    ]
    design_value = common.get_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
    )
    print(f"{design_path}: {design_value}")
    design_value = False
    # print(f"{design_path}: {design_value}")
    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )
    key2 = 'minimum_longitudinal_reinforcement_for_walls'
    design_path = [
        chapter,
        key1,
        key2,
    ]
    design_value = common.get_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
    )
    print(f"{design_path}: {design_value}")
    design_value = True
    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )

    key3 = 'direction_of_main_compression_reinforcement'
    key4 = 'reinforcement_direction_with_the_main_compression_force'
    design_path = [
        chapter,
        key1,
        key2,
        key3,
        key4
    ]
    design_value = common.get_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
    )
    print(f"{design_path}: {design_value}")
    design_value = True
    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )
    key4 = 'defined_in_reinforcement_direction'
    design_path = [
        chapter,
        key1,
        key2,
        key3,
        key4
    ]
    design_value = common.get_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
    )
    print(f"{design_path}: {design_value}")
    design_value = False

    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )

    key1 = 'maximum_longitudinal_reinforcement_acc_to_standard'
    design_path = [
        chapter,
        key1,
    ]
    design_value = common.get_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
    )
    print(f"{design_path}: {design_value}")
    design_value = True
    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )
    key2 = 'maximum_longitudinal_reinforcement_for_plates'
    design_path = [
        chapter,
        key1,
        key2,
    ]
    design_value = False
    print(f"{design_path}: {design_value}")
    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )
    key2 = 'maximum_longitudinal_reinforcement_for_walls'
    design_path = [
        chapter,
        key1,
        key2,
    ]
    design_value = True
    print(f"{design_path}: {design_value}")
    common.set_tree_value(
        tree=settings_surface_ec2_uls_current,
        path=design_path,
        value=design_value
    )
    print_helper.header1_bottom()

    # Create the new ULS Configuration in the model
    rfem_app.create_object(obj=concrete_uls_config_no2)

def set_steel_design_global_settings(rfem_app: rfem.Application):
    # Get global settings tree table for Steel Design add-on
    design_settings: rfem.GlobalSettingsTreeTable = rfem_app.get_design_settings(
        addon=rfem.DesignAddons.STEEL_DESIGN
    )
    print_helper.header1(f'DesignAddons.STEEL_DESIGN:\n{design_settings}')
    # *************************************************************************
    chapter = 'configuration'
    key1='configuration_uls'
    design_path = [
        chapter,
        key1
    ]
    # get_tree_value is not necessary just for demonstration
    # it is the best way to test the path, if it is wrong None is returned
    design_val = common.get_tree_value(
        tree=design_settings,
        path=design_path
    )
    print(f"{design_path}: {design_val}")
    design_val = True
    common.set_tree_value(
        tree=design_settings,
        path=design_path,
        value=design_val,
    )

    key1 = 'configuration_sls'
    design_path = [
        chapter,
        key1
    ]
    design_val = common.get_tree_value(
        tree=design_settings,
        path=design_path
    )
    print(f'{design_path}: {design_val}')
    design_val=False
    common.set_tree_value(
        tree=design_settings,
        path=design_path,
        value=design_val,
    )

    key1 = 'configuration_fire_resistance'
    design_path = [
        chapter,
        key1
    ]
    design_val = common.get_tree_value(
        tree=design_settings,
        path=design_path
    )
    print(f"{design_path}: {design_val}")
    design_val=False
    common.set_tree_value(
        tree=design_settings,
        path=design_path,
        value=design_val,
    )
    # Set specific value from the global settings
    rfem_app.set_design_settings(
        addon=rfem.DesignAddons.STEEL_DESIGN,
        global_settings_tree_table=design_settings
    )

    # Make nodes 21 and 23 to be ON LINE nodes
    nodes_list = rfem_app.get_object_list(
        objs=[rfem.structure_core.Node()]
    )
    for node in nodes_list:
        if isinstance(node, rfem.structure_core.Node):
            if node.no in (21, 23):
                node.type=rfem.structure_core.Node.TYPE_ON_LINE

    rfem_app.update_object_list(nodes_list)



    # *********************************************************************************************************************************
    obj: rfem.types_for_members.MemberTransverseStiffener=(
        rfem.types_for_members.MemberTransverseStiffener(
            no=3
        )
    )
    print(f'obj before modification:\n{obj}')
    obj.members.append(4)

    components_row = obj.components.rows.add()
    components_row.no = 1
    components_row.stiffener_type = rfem.types_for_members.MemberTransverseStiffener.ComponentsRow.STIFFENER_TYPE_STIFFENER_COMPONENT_TYPE_FLAT
    components_row.position = 1.572
    components_row.position_type = rfem.types_for_members.MemberTransverseStiffener.ComponentsRow.POSITION_TYPE_STIFFENER_COMPONENT_POSITION_DOUBLE_SIDED
    components_row.multiple = True
    components_row.note = 'My added row'
    components_row.multiple_offset = 1.667
    components_row.thickness = 0.008

    print(f'obj after modification:\n{obj.components.rows}')

    rfem_app.create_object(obj=obj)

    # Create Steel Effective Lengths objects
    effective_lengths=[
        rfem.steel_design.SteelEffectiveLengths(
            no=1,
            flexural_buckling_about_y=False,
            members=[2, 3]
        ),
        rfem.steel_design.SteelEffectiveLengths(
            no=2,
            members=[5,6,7,8]
        ),
    ]
    effective_length=rfem.steel_design.SteelEffectiveLengths(
        no=3,
        members=[4],
        intermediate_nodes=True
    )

    effective_lengths.append(effective_length)
    rfem_app.create_object_list(
        objs=effective_lengths
    )

    obj: rfem.steel_design.SteelEffectiveLengths = rfem_app.get_object(
        obj=rfem.steel_design.SteelEffectiveLengths(no=3)
    )
    obj.intermediate_nodes = True
    components_row = rfem.steel_design.SteelEffectiveLengths.NodalSupportsRow()
    components_row.support_type = rfem.steel_design.SteelEffectiveLengths.NodalSupportsRow.SUPPORT_TYPE_FIXED_IN_Y
    components_row.eccentricity_type = rfem.steel_design.SteelEffectiveLengths.NodalSupportsRow.ECCENTRICITY_TYPE_AT_UPPER_FLANGE
    obj.nodal_supports.rows.insert(1, components_row)
    components_row = rfem.steel_design.SteelEffectiveLengths.NodalSupportsRow()
    components_row.support_type = rfem.steel_design.SteelEffectiveLengths.NodalSupportsRow.SUPPORT_TYPE_FIXED_IN_Y
    components_row.eccentricity_type = rfem.steel_design.SteelEffectiveLengths.NodalSupportsRow.ECCENTRICITY_TYPE_AT_UPPER_FLANGE
    obj.nodal_supports.rows.insert(1, components_row)
    print(f'components_row.nodes: {components_row.nodes}')
    rfem_app.update_object(
        obj=obj
    )

def set_steel_design_configurations(rfem_app: rfem.Application):
    # Step1: Retrieve the Design ULS Configuration TreeTable for Steel add-on
    steel_uls_config: rfem.steel_design_objects.SteelDesignUlsConfiguration = rfem_app.get_object(
        obj=rfem.steel_design_objects.SteelDesignUlsConfiguration(no=1)
    )
    print_helper.header1(f"steel_uls_config:\n{steel_uls_config}")

    settings_ec3 = steel_uls_config.settings_ec3
    print_helper.header1(f"settings_ec3:\n{settings_ec3}")

    # Step 2: Retrieve a specific value from the configuration
    # Path to the specific value
    elastic_design_path=[
        "options",
        "options_elastic_design_root",
        "options_elastic_design"
    ]
    # Get and print specific value
    elastic_design_val = common.get_tree_value(
        tree=settings_ec3,
        path=elastic_design_path
    )
    print(f"Elastic Design: {elastic_design_val}")

    # Step 3: Modify the value
    common.set_tree_value(
        tree=settings_ec3,
        path=elastic_design_path,
        value=True
    )

    # Apply the updated configuration to the model
    # ToDo: Temporarily clear the 'standard_parameters_tree' field - Currently required before updating the object
    steel_uls_config.ClearField("standard_parameters")
    steel_uls_config.ClearField("standard_parameters_tree")
    rfem_app.update_object(
        obj=steel_uls_config
    )

def set_mesh_settings(rfem_app: rfem.Application):
    # Get current mesh settings
    mesh_settings: rfem.mesh.MeshSettings = rfem_app.get_mesh_settings()

    # Print mesh settings to get an overview of all available properties
    print_helper.header1(f'mesh_settings:\n{mesh_settings}')

    # Modify mesh settings as needed
    mesh_settings.general_target_length_of_fe=0.4

    # mesh_settings.general_maximum_distance_between_node_and_line=0.002
    # mesh_settings.surfaces_integrate_also_unutilized_objects = True

    # Set mesh settings
    rfem_app.set_mesh_settings(mesh_settings=mesh_settings)

def set_base_data(rfem_app: rfem.Application):
    base_data = rfem_app.get_base_data()
    # print(f'base_data:\n{base_data}')
    # Location
    base_data.location.country_iso = base_data.Location.DEU
    base_data.location.street = "Kreuzstraße 16"
    base_data.location.zip = "80331"
    base_data.location.town_city = "München"

    # General settings
    base_data.general_settings.global_axes_orientation = rfem.BaseData.GeneralSettings.GLOBAL_AXES_ORIENTATION_ZDOWN

    # addons
    base_data.addons.structure_stability_active = False
    base_data.addons.stress_strain_analysis_active = False
    base_data.addons.concrete_design_active = True
    base_data.addons.steel_design_active = True
    # ToDo: Enable when load wizard add on is available
    base_data.addons.load_wizards_active = False

    # Standards
    base_data.standards.concrete_design_standard = rfem.BaseData.Standards.CONCRETE_DESIGN_STANDARD_NATIONAL_ANNEX_AND_EDITION_EN_1992_CEN_2014_11_STANDARD
    base_data.standards.steel_design_standard = rfem.BaseData.Standards.STEEL_DESIGN_STANDARD_NATIONAL_ANNEX_AND_EDITION_EN_1993_CEN_2015_06_STANDARD

    base_data.general_settings.member_representatives_active = True
    base_data.general_settings.member_set_representatives_active = True
    base_data.general_settings.surface_cells_active = True

    # combinations settings
    base_data.combinations_settings.combination_wizard_active = True
    base_data.combinations_settings.combination_name_according_to_action_category = True

    rfem_app.set_base_data(base_data=base_data)

def calculate_all(rfem_app: rfem.Application):
    # ToDo: Add logic and checks
    # Perform plausibility check
    plausibility = rfem_app.plausibility_check(
        type=common.PlausibilityCheckType.PLAUSIBILITY_CHECK_CALCULATION,
        skip_warnings=True
    )

    # Check plausibility
    if not plausibility.succeeded:
        print(f'Plausibility check failed.')
        return

    # Calculate all
    rfem_app.calculate_all(skip_warnings=True)

    result_table: common.Table = rfem_app.get_result_table(
        table=rfem.results.ResultTable.ERRORS_AND_WARNINGS_TABLE,
        loading=rfem.ObjectId(
            no=1,
            object_type=rfem.OBJECT_TYPE_DESIGN_SITUATION
        )
    )
    print(f"\nResultTable.ERRORS_AND_WARNINGS_TABLE:\n{result_table.data}")

    result_table: common.Table = rfem_app.get_result_table(
        table=rfem.results.ResultTable.STATIC_ANALYSIS_MEMBERS_INTERNAL_FORCES_TABLE,
        loading=rfem.ObjectId(
            no=1,
            object_type=rfem.OBJECT_TYPE_DESIGN_SITUATION
        )
    )
    print(f"\nResultTable.STATIC_ANALYSIS_MEMBERS_INTERNAL_FORCES_TABLE:\n{result_table.data}")

    result_table: common.Table = rfem_app.get_result_table(
        table=rfem.results.ResultTable.STATIC_ANALYSIS_NODES_SUPPORT_FORCES_TABLE,
        loading=rfem.ObjectId(
            no=1,
            object_type=rfem.OBJECT_TYPE_DESIGN_SITUATION
        )
    )
    print(f"\nResultTable.STATIC_ANALYSIS_NODES_SUPPORT_FORCES_TABLE:\n{result_table.data}")

    result_table: common.Table = rfem_app.get_results(
        results_type=rfem.results.STATIC_ANALYSIS_MEMBERS_INTERNAL_FORCES,
        filters=[
            rfem.results.ResultsFilter(column_id='member_no', filter_expression='1'),
            rfem.results.ResultsFilter(column_id='loading', filter_expression='DS1'),
        ]
    )
    print(f"\nresults.STATIC_ANALYSIS_NODES_SUPPORT_FORCES_TABLE:\n{result_table.data}")

def define_structure() -> list:
    """Define and return a list of structural objects."""

    list_of_materials = []
    list_of_sections = []
    list_of_thicknesses = []
    list_of_nodes = []
    list_of_types_for_nodes = []
    list_of_lines = []
    list_of_types_for_lines = []
    list_of_members = []
    list_of_types_for_members = []
    list_of_surfaces = []
    list_of_types_for_surfaces = []
    list_of_result_objects = []
    list_of_imperfections = []

    # # Define types_for_nodes
    # Nodal Supports
    list_of_types_for_nodes.extend([
        rfem.types_for_nodes.NodalSupport(
            no=1,
            user_defined_name_enabled=True,
            name="Hinged",
            nodes=[6, 7, 8],
            spring=common.Vector3d(x=inf, y=inf, z=inf),
            rotational_restraint=common.Vector3d(x=0, y=0, z=inf),
        ),
        rfem.types_for_nodes.NodalSupport(
            no=2,
            user_defined_name_enabled=True,
            name="Fixed",
            spring=common.Vector3d(x=inf, y=inf, z=inf),
            rotational_restraint=common.Vector3d(x=inf, y=inf, z=inf),
        ),
        rfem.types_for_nodes.NodalSupport(
            no=3,
            user_defined_name_enabled=True,
            name="Roller",
            spring=common.Vector3d(x=0, y=0, z=inf),
            rotational_restraint=common.Vector3d(x=0, y=0, z=inf),
        ),
        rfem.types_for_nodes.NodalSupport(
            no=4,
            user_defined_name_enabled=True,
            name="Roller in X'",
            spring=common.Vector3d(x=0, y=inf, z=inf),
            rotational_restraint=common.Vector3d(x=0, y=0, z=inf),
        ),
        rfem.types_for_nodes.NodalSupport(
            no=5,
            user_defined_name_enabled=True,
            name="Roller in Y'",
            spring=common.Vector3d(x=inf, y=0, z=inf),
            rotational_restraint=common.Vector3d(x=0, y=0, z=inf),
        ),
        # Nodal Mesh Refinements
        rfem.types_for_nodes.NodalMeshRefinement(
            no=1,
            nodes=[14],
            circular_radius=1.5,
            circular_target_inner_length=0.2,
        ),
    ])
    # # Define types_for_lines
    # Line Supports
    list_of_types_for_lines.extend([
        rfem.types_for_lines.LineSupport(
            no=1,
            user_defined_name_enabled=True,
            name="Hinged",
            lines=[1, 2, 3],
            spring=common.Vector3d(x=inf, y=inf, z=inf)
        ),
        rfem.types_for_lines.LineSupport(
            no=2,
            user_defined_name_enabled=True,
            name="Rigid",
            spring=common.Vector3d(x=inf, y=inf, z=inf),
            rotational_restraint=common.Vector3d(x=inf, y=inf, z=inf)
        ),
    ])
    # # Define types_for_members
    # Member Hinges
    list_of_types_for_members.extend([
        rfem.types_for_members.MemberHinge(
            no=1,
            axial_release_n=inf,
            axial_release_vy=inf,
            axial_release_vz=inf,
            moment_release_mt=inf,
        ),
        # Member Eccentricities
        rfem.types_for_members.MemberEccentricity(
            no=1,
            name="Relative to Section | Middle - Top | Transverse offset from section : Member No. 5 | Middle - Bottom | Axial offset from adjoining members",
            transverse_offset_active=True,
            axial_offset_active=True,
            vertical_section_alignment=rfem.types_for_members.MemberEccentricity.VERTICAL_SECTION_ALIGNMENT_TOP,
            transverse_offset_reference_member=5,
            transverse_offset_vertical_alignment=rfem.types_for_members.MemberEccentricity.TRANSVERSE_OFFSET_VERTICAL_ALIGNMENT_BOTTOM,
        ),
    ])

    # # Define result_objects
    # Result Sections
    list_of_result_objects.append(
        rfem.result_objects.ResultSection(
            no=1,
            assigned_to_all_surfaces=False,
            assigned_to_surfaces=[5, 4],
            first_point=common.Vector3d(x=2.5, y=0, z=-3),
            second_point=common.Vector3d(x=2.5, y=-10, z=-3.5),
        )
    )

    # # Define Imperfections
    # ImperfectionCases
    load_combinations_for_imperfection_case = [1, 2, 3, 4, 5, 6, 7, 8]
    list_of_imperfections.extend([
        rfem.imperfections.ImperfectionCase(
            no=1,
            name="-Y direction",
            assigned_to_load_combinations=load_combinations_for_imperfection_case,
            is_active=True,
        ),
        # Member Imperfections | IC2
        rfem.imperfections.ImperfectionCase(
            no=2,
            name="+Y direction",
            assigned_to_load_combinations=load_combinations_for_imperfection_case,
            is_active=True,
        ),
        # Member Imperfections | IC1
        # Imperfection Type: EN 1992-1 - Concrete Member Imperfections
        rfem.imperfections.MemberImperfection(
            no=1,
            members=[1],
            imperfection_case=1,
            definition_type=rfem.imperfections.MemberImperfection.DEFINITION_TYPE_EN_1992_1,
            imperfection_direction=rfem.imperfections.MemberImperfection.IMPERFECTION_DIRECTION_LOCAL_Y_NEGATIVE,
            height=3,
        ),
        # Imperfection Type: EN 1993-1 - Steel Member Imperfections
        rfem.imperfections.MemberImperfection(
            no=2,
            members=[2,3],
            imperfection_case=1,
            definition_type=rfem.imperfections.MemberImperfection.DEFINITION_TYPE_EN_1993_1,
            imperfection_direction=rfem.imperfections.MemberImperfection.IMPERFECTION_DIRECTION_LOCAL_Z,
            height=3.5,
        ),
        rfem.imperfections.MemberImperfection(
            no=3,
            imperfection_type=rfem.imperfections.MemberImperfection.IMPERFECTION_TYPE_INITIAL_BOW,
            members=[3],
            imperfection_case=1,
            definition_type=rfem.imperfections.MemberImperfection.DEFINITION_TYPE_EN_1993_1,
            section_design=rfem.imperfections.MemberImperfection.SECTION_DESIGN_ELASTIC,
        ),
        rfem.imperfections.MemberImperfection(
            no=4,
            imperfection_type=rfem.imperfections.MemberImperfection.IMPERFECTION_TYPE_INITIAL_BOW,
            members=[2],
            imperfection_case=1,
            definition_type=rfem.imperfections.MemberImperfection.DEFINITION_TYPE_EN_1993_1,
            imperfection_direction=rfem.imperfections.MemberImperfection.IMPERFECTION_DIRECTION_LOCAL_Z_NEGATIVE,
            section_design=rfem.imperfections.MemberImperfection.SECTION_DESIGN_ELASTIC,
        ),
        # # Member Imperfections | IC2
        # # Imperfection Type: EN 1992-1 - Concrete Member Imperfections
        rfem.imperfections.MemberImperfection(
            no=1,
            members=[1],
            imperfection_case=2,
            definition_type=rfem.imperfections.MemberImperfection.DEFINITION_TYPE_EN_1992_1,
            imperfection_direction=rfem.imperfections.MemberImperfection.IMPERFECTION_DIRECTION_LOCAL_Y,
            height=3,
        ),
        # Imperfection Type: EN 1993-1 - Steel Member Imperfections
        rfem.imperfections.MemberImperfection(
            no=2,
            members=[2, 3],
            imperfection_case=2,
            definition_type=rfem.imperfections.MemberImperfection.DEFINITION_TYPE_EN_1993_1,
            imperfection_direction=rfem.imperfections.MemberImperfection.IMPERFECTION_DIRECTION_LOCAL_Z_NEGATIVE,
            height=3.5,
        ),
        rfem.imperfections.MemberImperfection(
            no=3,
            imperfection_type=rfem.imperfections.MemberImperfection.IMPERFECTION_TYPE_INITIAL_BOW,
            members=[3],
            imperfection_case=2,
            definition_type=rfem.imperfections.MemberImperfection.DEFINITION_TYPE_EN_1993_1,
            section_design=rfem.imperfections.MemberImperfection.SECTION_DESIGN_ELASTIC,
        ),
        rfem.imperfections.MemberImperfection(
            no=4,
            imperfection_type=rfem.imperfections.MemberImperfection.IMPERFECTION_TYPE_INITIAL_BOW,
            members=[2],
            imperfection_case=2,
            definition_type=rfem.imperfections.MemberImperfection.DEFINITION_TYPE_EN_1993_1,
            imperfection_direction=rfem.imperfections.MemberImperfection.IMPERFECTION_DIRECTION_LOCAL_Z_NEGATIVE,
            section_design=rfem.imperfections.MemberImperfection.SECTION_DESIGN_ELASTIC,
        ),
    ])

    # Define materials
    list_of_materials.extend([
        rfem.structure_core.Material(
            no=1,
            name='C30/37 | EN 1992-1-1:2004/A1:2014'
        ),
        rfem.structure_core.Material(
            no=2,
            name='S235 | DIN EN 1992-1-1/NA/A1:2015-12'
        ),
        rfem.structure_core.Material(
            no=3,
            name='B500S(B) | DIN EN 1992-1-1/NA/A1:2015-12'
        ),
    ])

    # Define sections
    list_of_sections.extend([
        rfem.structure_core.Section(
            no=1,
            name='CIRCLE_M1 0.3',
            material=1
        ),
        rfem.structure_core.Section(
            no=2,
            name="HEA 200",
            material=2
        ),
        rfem.structure_core.Section(
            no=3,
            name="IPE 200",
            material=2
        ),
        rfem.structure_core.Section(
            no=4,
            name="R_M1 0.3/0.4",
            material=1,
        ),
    ])

    # Define thicknesses
    list_of_thicknesses.extend([
        rfem.structure_core.Thickness(
            no=1,
            material=1,
            uniform_thickness=0.2,
            comment='Slabs',
            user_defined_name_enabled=True
        ),
        rfem.structure_core.Thickness(
            no=2,
            material=1,
            uniform_thickness=0.2,
            comment='Walls',
            user_defined_name_enabled=True,
        ),
    ])

    # Define nodes & lines for surfaces
    list_of_nodes.extend([
        rfem.structure_core.Node(
            no=1,
            coordinate_1=5.0,
            coordinate_2=0.0,
            coordinate_3=0.0
        ),
        rfem.structure_core.Node(
            no=2,
            coordinate_1=0.0,
            coordinate_2=0.0,
            coordinate_3=0.0
        ),
        rfem.structure_core.Node(
            no=3,
            coordinate_1=0.0,
            coordinate_2=-5.0,
            coordinate_3=0.0
        ),
        rfem.structure_core.Node(
            no=4,
            coordinate_1=0.0,
            coordinate_2=-10.0,
            coordinate_3=0.0
        ),
        rfem.structure_core.Node(
            no=5,
            coordinate_1=5.0,
            coordinate_2=-10.0,
            coordinate_3=0.0
        ),
        rfem.structure_core.Node(
            no=6,
            coordinate_1=5.0,
            coordinate_2=-5.0,
            coordinate_3=0.0
        ),
        rfem.structure_core.Node(
            no=7,
            coordinate_1=10.0,
            coordinate_2=-5.0,
            coordinate_3=0.0
        ),
        rfem.structure_core.Node(
            no=8,
            coordinate_1=10.0,
            coordinate_2=-10.0,
            coordinate_3=0.0
        ),
        rfem.structure_core.Node(
            no=9,
            coordinate_1=5.0,
            coordinate_2=0,
            coordinate_3=-3.0
        ),
        rfem.structure_core.Node(
            no=10,
            coordinate_1=0,
            coordinate_2=0,
            coordinate_3=-3.0
        ),
        rfem.structure_core.Node(
            no=11,
            coordinate_1=0,
            coordinate_2=-5,
            coordinate_3=-3.0
        ),
        rfem.structure_core.Node(
            no=12,
            coordinate_1=0,
            coordinate_2=-5,
            coordinate_3=-3.5
        ),
        rfem.structure_core.Node(
            no=13,
            coordinate_1=0,
            coordinate_2=-10,
            coordinate_3=-3.5
        ),
        rfem.structure_core.Node(
            no=14,
            coordinate_1=5,
            coordinate_2=-10,
            coordinate_3=-3.5
        ),
        rfem.structure_core.Node(
            no=15,
            coordinate_1=5,
            coordinate_2=-5,
            coordinate_3=-3.5
        ),
        rfem.structure_core.Node(
            no=16,
            coordinate_1=10,
            coordinate_2=-5,
            coordinate_3=-3.5
        ),
        rfem.structure_core.Node(
            no=17,
            coordinate_1=10,
            coordinate_2=-10,
            coordinate_3=-3.5
        ),
        rfem.structure_core.Node(
            no=19,
            coordinate_1=5,
            coordinate_2=-5,
            coordinate_3=-3
        ),
        rfem.structure_core.Node(
            no=20,
            coordinate_1=5,
            coordinate_2=-8.333,
            coordinate_3=-3.5
        ),
        rfem.structure_core.Node(
            no=21,
            coordinate_1=10,
            coordinate_2=-8.333,
            coordinate_3=-3.5
        ),
        rfem.structure_core.Node(
            no=22,
            coordinate_1=5,
            coordinate_2=-6.666,
            coordinate_3=-3.5
        ),
        rfem.structure_core.Node(
            no=23,
            coordinate_1=10,
            coordinate_2=-6.666,
            coordinate_3=-3.5
        ),
    ])

    # Define lines
    list_of_lines.extend([
        rfem.structure_core.Line(
            no=1,
            definition_nodes=[1, 2]
        ),
        rfem.structure_core.Line(
            no=2,
            definition_nodes=[2, 3]
        ),
        rfem.structure_core.Line(
            no=3,
            definition_nodes=[4, 5]
        ),
        rfem.structure_core.Line(
            no=4,
            definition_nodes=[9, 10]
        ),
        rfem.structure_core.Line(
            no=5,
            definition_nodes=[10, 11]
        ),
        rfem.structure_core.Line(
            no=6,
            definition_nodes=[1, 9]
        ),
        rfem.structure_core.Line(
            no=7,
            definition_nodes=[2, 10]
        ),
        rfem.structure_core.Line(
            no=8,
            definition_nodes=[3, 11]
        ),
        rfem.structure_core.Line(
            no=9,
            definition_nodes=[13, 14]
        ),
        rfem.structure_core.Line(
            no=11,
            definition_nodes=[4, 13]
        ),
        rfem.structure_core.Line(
            no=12,
            definition_nodes=[5, 14]
        ),
    ])
    list_of_members.extend([
        rfem.structure_core.Member(
            no=1,
            line=13,
            node_start=6,
            node_end=19,
            section_start=1
        ),
        rfem.structure_core.Member(
            no=2,
            line=14,
            node_start=7,
            node_end=16,
            section_start=2,
            rotation_angle=math.radians(90.0)
        ),
        rfem.structure_core.Member(
            no=3,
            line=15,
            node_start=8,
            node_end=17,
            section_start=2,
            rotation_angle=math.radians(90.0)
        )
    ])
    list_of_lines.extend([
        rfem.structure_core.Line(
            no=16,
            definition_nodes=[9, 19]
        ),
        rfem.structure_core.Line(
            no=17,
            definition_nodes=[19, 11]
        ),
    ])
    list_of_members.append(
        rfem.structure_core.Member(
            no=9,
            type=rfem.structure_core.Member.TYPE_RIB,
            line=20,
            node_start=15,
            node_end=14,
            section_start=4,
        )
    )
    list_of_lines.extend([
        rfem.structure_core.Line(
            no=22,
            definition_nodes=[13, 12]
        ),
        rfem.structure_core.Line(
            no=23,
            definition_nodes=[12, 15]
        ),
        rfem.structure_core.Line(
            no=24,
            definition_nodes=[11, 12]
        ),
        rfem.structure_core.Line(
            no=25,
            definition_nodes=[19, 15]
        ),
    ])
    list_of_members.extend([
        rfem.structure_core.Member(
            no=4,
            line=26,
            node_start=16,
            node_end=17,
            section_start=2,
            member_eccentricity_start=1,
            member_eccentricity_end=1,
        ),
        rfem.structure_core.Member(
            no=5,
            line=27,
            node_start=15,
            node_end=16,
            section_start=3
        ),
        rfem.structure_core.Member(
            no=6,
            line=28,
            node_start=14,
            node_end=17,
            section_start=3
        ),
        rfem.structure_core.Member(
            no=7,
            line=29,
            node_start=20,
            node_end=21,
            section_start=3
        ),
        rfem.structure_core.Member(
            no=8,
            line=30,
            node_start=22,
            node_end=23,
            section_start=3
        ),
    ])
    list_of_surfaces.extend([
        rfem.structure_core.Surface(
            no=1,
            boundary_lines=[1, 6, 4, 7],
            thickness=2,
            concrete_durability_top=1,
            concrete_durability_bottom=1,
            reinforcement_direction_top=1,
            reinforcement_direction_bottom=1,
        ),
        rfem.structure_core.Surface(
            no=2,
            boundary_lines=[2, 7, 5, 8],
            thickness=2,
            concrete_durability_top=1,
            concrete_durability_bottom=1,
            reinforcement_direction_top=1,
            reinforcement_direction_bottom=1,
        ),
        rfem.structure_core.Surface(
            no=3,
            boundary_lines=[3, 11, 9, 12],
            thickness=2,
            concrete_durability_top=1,
            concrete_durability_bottom=1,
            reinforcement_direction_top=1,
            reinforcement_direction_bottom=1,
        ),
        rfem.structure_core.Surface(
            no=4,
            boundary_lines=[16, 17, 5, 4],
            thickness=1,
            concrete_durability_top=1,
            concrete_durability_bottom=1,
            reinforcement_direction_top=1,
            reinforcement_direction_bottom=1,
        ),
        rfem.structure_core.Surface(
            no=5,
            boundary_lines=[20, 9, 22, 23],
            thickness=1,
            concrete_durability_top=1,
            concrete_durability_bottom=1,
            reinforcement_direction_top=1,
            reinforcement_direction_bottom=1,
        ),
        rfem.structure_core.Surface(
            no=6,
            boundary_lines=[17, 24, 23, 25],
            thickness=1,
            concrete_durability_top=1,
            concrete_durability_bottom=1,
            reinforcement_direction_top=1,
            reinforcement_direction_bottom=1,
        ),
    ])

    return (list_of_materials + list_of_sections + list_of_thicknesses
            + list_of_nodes + list_of_types_for_nodes
            + list_of_lines + list_of_types_for_lines
            + list_of_members + list_of_types_for_members
            + list_of_surfaces + list_of_types_for_surfaces
            + list_of_result_objects
            + list_of_imperfections)

def define_loading() -> list:
    """Define and return a list of loading objects."""
    list_of_loadings = [
        # Define load Cases
        rfem.loading.LoadCase(
            no=1,
            name="Self-weight",
            static_analysis_settings=1,
        ),
        rfem.loading.LoadCase(
            no=2,
            name="Lower Slab",
            static_analysis_settings=1,
            action_category=rfem.loading.LoadCase.ACTION_CATEGORY_IMPOSED_LOADS_CATEGORY_A_DOMESTIC_RESIDENTIAL_AREAS_QI_A,
        ),
        rfem.loading.LoadCase(
            no=3,
            name="Upper Slab",
            static_analysis_settings=1,
            action_category=rfem.loading.LoadCase.ACTION_CATEGORY_IMPOSED_LOADS_CATEGORY_A_DOMESTIC_RESIDENTIAL_AREAS_QI_A,
        ),
        rfem.loading.LoadCase(
            no=4,
            name="Platform",
            static_analysis_settings=1,
            action_category=rfem.loading.LoadCase.ACTION_CATEGORY_IMPOSED_LOADS_CATEGORY_A_DOMESTIC_RESIDENTIAL_AREAS_QI_A,
        ),
        # Define combination wizard
        rfem.loading.CombinationWizard(
            no=1,
            static_analysis_settings=2,
        ),
        rfem.loading.CombinationWizard(
            no=2,
            static_analysis_settings=1,
        ),
        # Design Situations for steel design
        rfem.loading.DesignSituation(
            no=1,
            design_situation_type=rfem.loading.DesignSituation.DESIGN_SITUATION_TYPE_STR_PERMANENT_AND_TRANSIENT_6_10,
            combination_wizard=2,
            active=True,
            active_for_concrete_design=False,
            active_for_steel_design=True,
        ),
        rfem.loading.DesignSituation(
            no=2,
            design_situation_type=rfem.loading.DesignSituation.DESIGN_SITUATION_TYPE_SLS_CHARACTERISTIC,
            active=False,
            comment="Deactivate for steel design",
            active_for_concrete_design=False,
            active_for_steel_design=True,

        ),
        rfem.loading.DesignSituation(
            no=3,
            design_situation_type=rfem.loading.DesignSituation.DESIGN_SITUATION_TYPE_SLS_FREQUENT,
            active=False,
            comment="Deactivate for steel and concrete design",
            active_for_concrete_design=False,
            active_for_steel_design=True,
        ),
        rfem.loading.DesignSituation(
            no=4,
            design_situation_type=rfem.loading.DesignSituation.DESIGN_SITUATION_TYPE_SLS_QUASI_PERMANENT,
            active=False,
            active_for_concrete_design=False,
            active_for_steel_design=True,
        ),
        # Design Situations for concrete design
        rfem.loading.DesignSituation(
            no=5,
            design_situation_type=rfem.loading.DesignSituation.DESIGN_SITUATION_TYPE_STR_PERMANENT_AND_TRANSIENT_6_10,
            combination_wizard=1,
            active=True,
            active_for_concrete_design=True,
            active_for_steel_design=False,
        ),
        # Static analysis settings
        rfem.loading.StaticAnalysisSettings(
            no=1,
            analysis_type=rfem.loading.StaticAnalysisSettings.ANALYSIS_TYPE_SECOND_ORDER_P_DELTA,
            iterative_method_for_nonlinear_analysis=rfem.loading.StaticAnalysisSettings.ITERATIVE_METHOD_FOR_NONLINEAR_ANALYSIS_PICARD,
        ),
        rfem.loading.StaticAnalysisSettings(
            no=2,
            analysis_type=rfem.loading.StaticAnalysisSettings.ANALYSIS_TYPE_GEOMETRICALLY_LINEAR,
        ),
        # Define surface loads
        rfem.loads.SurfaceLoad(
            no=1,
            surfaces=[4, 5],
            load_case=1,
            uniform_magnitude=750,
        ),
        rfem.loads.SurfaceLoad(
            no=1,
            surfaces=[4],
            load_case=2,
            uniform_magnitude=1500,
        ),
        rfem.loads.SurfaceLoad(
            no=1,
            surfaces=[5],
            load_case=3,
            uniform_magnitude=1500,
        ),
        # Member Loads | LC4
        rfem.loads.MemberLoad(
            no=1,
            members=[7, 8],
            load_case=4,
            magnitude=1500,
            comment="Remove when load wizard is implemented!",
        ),
        rfem.loads.MemberLoad(
            no=2,
            members=[5, 6],
            load_case=4,
            magnitude=750,
            comment="Remove when load wizard is implemented!",
        ),
    ]

    return list_of_loadings

def define_reinforcement():
    """Define and return a list of reinforcement objects."""
    list_of_reinforcement = []
    surface_reinforcement = rfem.reinforcement.SurfaceReinforcement(
        no=1,
        material=3,
        surfaces=[4, 5],
        definition_type=rfem.reinforcement.SurfaceReinforcement.DEFINITION_TYPE_REBAR_2_LAYERS,
        orthogonal_rebar_diameter = 0.01,
        orthogonal_rebar_spacing = 0.2,
        comment='Slabs'
    )
    list_of_reinforcement.append(surface_reinforcement)
    surface_reinforcement = rfem.reinforcement.SurfaceReinforcement(
        no=2,
        material=3,
        surfaces=[1,2,3,6],
        definition_type=rfem.reinforcement.SurfaceReinforcement.DEFINITION_TYPE_MESH,
        mesh_name = "Q257A",
        comment='Walls'
    )
    list_of_reinforcement.append(surface_reinforcement)
    reinforcement_direction = rfem.reinforcement.ReinforcementDirection(
        no=2,
        reinforcement_direction_type=rfem.reinforcement.ReinforcementDirection.REINFORCEMENT_DIRECTION_TYPE_FIRST_REINFORCEMENT_IN_Y,
        surfaces=[4, 5],
    )
    list_of_reinforcement.append(reinforcement_direction)

    return list_of_reinforcement

def main():
    with rfem.Application(port=9000) as rfem_app:
        # Get app info
        print_helper.header1(f'get_application_info:\n{rfem_app.get_application_info()}')

        # Get subscription info
        print_helper.header1(f'get_subscription_info:\n{rfem_app.get_subscription_info()}')

        # Close existing project with the same name and create a new one
        model_name = 'tutorial_structural_analysis_python'

        # Create new model
        close_and_create_project_by_name(model_name, rfem_app)

        # Set base data
        set_base_data(rfem_app)

        # Define mesh settings
        set_mesh_settings(rfem_app)

        # Create structure
        structures = define_structure()

        # Create loadings
        loadings = define_loading()

        # Create reinforcements
        reinforcements = define_reinforcement()

        # Create objects in RFEM
        rfem_app.create_object_list(structures + loadings + reinforcements)

        # Define concrete design global settings
        set_concrete_design_global_settings(rfem_app)

        # # Define concrete design configurations
        # set_concrete_design_configurations(rfem_app)

        # Define steel design global settings
        set_steel_design_global_settings(rfem_app)

        # Define steel design configurations
        set_steel_design_configurations(rfem_app)

        # # Calculate all
        # calculate_all(rfem_app)

if __name__ == '__main__':
    main()