#
# Copyright © 2020 MSC.Software Corporation
#  
# Permission to use Scripts
#  
# Permission is granted to MSC.Software customers with valid licenses of the MSC Apex™ software 
# to use script files posted on this page (collectively “Scripts”) solely for customers’ own authorized 
# internal use in conjunction with licensed use of the MSC Apex software.  
# No other use is permitted.  No license to MSC Apex or other software is granted under this Permission.
#  
# SCRIPTS ARE PROVIDED ON AN "AS-IS" BASIS, WITH NO WARRANTIES OF ANY KIND.  USE OF THE SCRIPTS IS AT CUSTOMER'S SOLE RISK.
#  
# IN NO EVENT SHALL MSC.SOFTWARE CORPORATION, ITS AFFILIATES, SUBSIDIARIES, SUPPLIERS, AGENTS, REPRESENTATIVES OR EMPLOYEES BE LIABLE FOR ANY 
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, TORT, 
# STRICT LIABILITY, OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION USE OF THE SCRIPTS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
#  
# Customer shall indemnify, defend and hold MSC.Software Corporation, its affiliates, subsidiaries, suppliers, agents, 
# employees or representatives (collectively, “Indemnified Parties”) harmless from and against any third-party claims 
# brought against Indemnified Parties rising out of or in connection with Customer’s use of the Scripts.
#
##################################################

## Author: Javier Lázaro Ortega

## Email: javier.lazaroortega@hexagon.com

## MSC Apex Version: 2021

##################################################
# import remoting
# remoting.launchApplication()
# pids = remoting.getApplicationPids()
# print(pids)
# exit()
# remoting.attachApplication( pids[0] )

import apex
import os

from docxtpl import DocxTemplate

src_dir = os.path.dirname(os.path.realpath(__file__))
templat_path = os.path.join(src_dir,"report_template.docx")
report_dir = os.path.join(src_dir,"report.docx")

model = apex.currentModel()

###########################
def parts_info():
    list = []
    parts = model.getParts( recursive = True )
    for part in parts:
        list.append(process_part(part))
    return list
###########################
def prepare_assembly_part_tree_view(dictionary={}):
    # Disable showing output dialog at the end
    # apex.disableShowOutput()

    model = apex.currentModel()
    model_tree = process_assembly(model)
    text_tree = print_model_tree(model_tree)
    return text_tree
# Get the node and element counts from the Assembly
def process_assembly(assembly):
    assembly_dict = {}
    assembly_dict["name"] = assembly.name
    assembly_dict["node_count"] = getTotalNodesCountFromAssembly(assembly)
    assembly_dict["element_count"] = getTotalElementsCountFromAssembly(assembly)
    sub_assembly_dicts = []
    part_dicts = []
    for sub_assembly in assembly.assemblies:
        sub_assembly_dicts.append(process_assembly(sub_assembly))
    for part in assembly.parts:
        part_dicts.append(process_part(part))
    assembly_dict["sub_assembies"] = sub_assembly_dicts
    assembly_dict["parts"] = part_dicts

    return assembly_dict
# Get the node and element counts from the Part
def process_part(part):
    part_dict={}
    part_dict["name"] = part.name
    part_dict["node_count"] = getTotalNodesCountFromPart(part)
    part_dict["element_count"] = getTotalElementsCountFromPart(part)
    return part_dict
# Count total nodes from all entities under assembly
def getTotalNodesCountFromAssembly(assembly):
    nodes = 0
    for part in assembly.parts:
        nodes += getTotalNodesCountFromPart(part)
    return nodes
# Count total elements from all entities under assembly
def getTotalElementsCountFromAssembly(assembly):
    nodes = 0
    for part in assembly.parts:
        nodes += getTotalElementsCountFromPart(part)
    return nodes
# Count total nodes from all entities under part
def getTotalNodesCountFromPart(part):
    nodes = 0
    for entity in part.meshes:
        nodes += entity.nodes.len()
    return nodes
# Count total elements from all entities under part
def getTotalElementsCountFromPart(part):
    elements = 0
    for entity in part.meshes:
        elements += entity.elements.len()
    return elements
def print_model_tree(model_tree, indent = 0, text_tree = []):
    if "name" in model_tree:
        # info = "\t" + "Nodes: "+str(model_tree["node_count"]) +"\t"+ "Elements: "+str(model_tree["element_count"])
        info = ""
        text_tree.append(indent*"\t" + "+" + model_tree["name"] + info + "\n")
        indent += 1
        if "parts" in model_tree:
            for part in model_tree["parts"]:
                # info = "\t" + "Nodes: "+str(part["node_count"]) +"\t"+ "Elements: "+str(part["element_count"])
                text_tree.append(indent*"\t" + "-" + part["name"] + info + "\n")
        if "sub_assembies" in model_tree:
            for assembly_tree in model_tree["sub_assembies"]:
                text_tree = print_model_tree(assembly_tree, indent, text_tree)
    return text_tree
###########################
def elementsTopology():

    numElms      = 0
    numSolidElms = 0
    numShellElms = 0
    numHexElms   = 0
    numBeamElms  = 0
    numNodes     = 0
    for part in model.getParts( recursive = True ):
        try:
            meshBodies = part.getMeshes()
            for mesh in meshBodies:
                elms = mesh.getElements()
                nodes = mesh.getNodes()
                numElms  += elms.len()
                numNodes += nodes.len()
                try:
                    elms = mesh.asSolidMesh().getElements()
                except:
                    try:
                        elms = mesh.asHexMesh().getElements()
                    except:
                        try:
                            elms = mesh.asSurfaceMesh().getElements()
                        except:
                            try:
                                elms = mesh.asCurveMesh().getElements()
                            except:
                                k = 1
                            else:
                                numBeamElms += elms.len()
                        else:
                            numShellElms += elms.len()
                    else:
                        numHexElms += elms.len()
                else:
                    numSolidElms += elms.len()
        except:
            print("Entity count omitted for Part: "+part.getFullName()[27:])

    ret_dict = {}
    ret_dict["num_nodes"]         = numNodes
    ret_dict["num_elements"]      = numElms
    ret_dict["beam_elements"]     = numBeamElms
    ret_dict["shell_elements"]    = numShellElms
    ret_dict["tet_elements"]      = numSolidElms
    ret_dict["hexwedge_elements"] = numHexElms

    return ret_dict

def layeredPanels():

    list = []
    panels = apex.attribute.getLayeredPanels([{}])
    if len(panels) == 0: return {}
    for panel in panels:
        dict = {}
        dict["name"] = panel.pathName.split("/")[-1]
        zones = panel.getZones()
        dict["zones"] = []
        for zone in zones:
            plies = zone.getPlies()
            if plies == None: continue #fix
            ply_list = []
            for ply in plies:
                ply_list.append({"angle": str(ply.angle), "thickness": str(ply.thickness), "material": str(ply.material.name), "name": ply.name}) #coverageRegion
            dict["zones"].append(ply_list)
        list.append(dict)
    return list


def main(dictionary={}):
    dict = {}

    dict["model"] = model.name
    dict["model_path"] = model.pathName
    dict["text_tree"] = prepare_assembly_part_tree_view()
    dict["all_parts"] = parts_info()
    dict["topology"] = elementsTopology()
    dict["panels"] = layeredPanels()
    if len(dict["panels"]) > 0:  dict["include_panels"] = True


    tpl = DocxTemplate(templat_path)
    tpl.render(dict)
    tpl.save(dictionary["reportPath"])
