#! python3

#===============================================================================
# Imports
#===============================================================================

from win32com.shell import shell, shellcon
import mhrc.automation, os, logging
import mhrc.automation.handler
import xml.etree.ElementTree as ET


#===============================================================================
# PSCAD Event Handler
#===============================================================================

class BuildEventHandler(mhrc.automation.handler.BuildEvent):

    def __init__(self):
        super().__init__()
        self._start = {}

    def _build_event(self, msg, event):
        elapsed = int(msg.get('elapsed'))
        etype = event.find('type')
        phase = etype.get('name')
        project = event.find('project')
        prj_name = project.get('name') if project is not None else "[All]"

        key = "{} {}".format(prj_name, phase)
        if etype.get('status') == 'BEGIN':
            self._start[key] = elapsed
        else:
            msec = elapsed - self._start[key]
            LOG.info("%s: %d ms", key, msec)

        return super()._build_event(msg, event)


#===============================================================================
# Logging
#===============================================================================

# Log 'INFO' messages & above.  Include level & module name.
logging.basicConfig(level=logging.INFO,
                    format="%(levelname)-8s %(name)-26s %(message)s")

# Ignore INFO msgs from automation (eg, mhrc.automation.controller, ...)
logging.getLogger('mhrc.automation').setLevel(logging.WARNING)

LOG = logging.getLogger('main')


#===============================================================================
# Detect "Public Documents" folder and find PSCAD's "Examples" folder
#===============================================================================

# Find "Public Documents" folder
#   Probably "C:\Users\Public\Documents"
public_dir = shell.SHGetFolderPath(0, shellcon.CSIDL_COMMON_DOCUMENTS, None, 0)
LOG.debug("Public Documents  : %s", public_dir)

# Find the "Tutorial" workspace
#   Probably "<Public Documents>\PSCAD\4.6.3\Examples\tutorial\Tutorial.pswx"
tutorial_dir = next(root for root, _, files in os.walk(
    os.path.join(public_dir, "PSCAD") ) if "Tutorial.pswx" in files)
LOG.debug("Tutorial directory: %s", tutorial_dir)


#===============================================================================
# Determine PSCAD version to launch
#===============================================================================

controller = mhrc.automation.controller()
versions = controller.get_paramlist_names('pscad')
LOG.debug("PSCAD Versions: %s", versions)

# Skip any 'Alpha' versions, if other choices exist
vers = [ver for ver in versions if 'Alpha' not in ver]
if len(vers) > 0:
    versions = vers

# Skip any 'Beta' versions, if other choices exist
vers = [ver for ver in versions if 'Beta' not in ver]
if len(vers) > 0:
    versions = vers

# Skip any 32-bit versions, if other choices exist
vers = [ver for ver in versions if 'x86' not in ver]
if len(vers) > 0:
    versions = vers

LOG.debug("   After filtering: %s", versions)

# Of any remaining versions, choose the "lexically largest" one.
version = sorted(versions)[-1]
LOG.debug("   Selected PSCAD version: %s", version)


#===============================================================================
# Determine FORTRAN version to use
#===============================================================================

# Get all installed FORTRAN compiler versions
fortrans = controller.get_paramlist_names('fortran')
LOG.debug("FORTRAN Versions: %s", fortrans)

# Skip 'GFortran' compilers, if other choices exist
vers = [ver for ver in fortrans if 'GFortran' not in ver]
if len(vers) > 0:
    fortrans = vers

LOG.debug("   After filtering: %s", fortrans)

# Order the remaining compilers, choose the last one (highest revision)
fortran = sorted(fortrans)[-1]
LOG.debug("   Selected FORTRAN version: %s", fortran)


#===============================================================================
# Determine Matlab version to use
#===============================================================================

# Get all installed Matlab versions
matlabs = controller.get_paramlist_names('matlab')
LOG.debug("Matlab Versions: %s", matlabs)

# Get the highest installed version of Matlab:
matlab = sorted(matlabs)[-1] if matlabs else None
LOG.debug("   Selected Matlab version: %s", matlab)


#===============================================================================
# Launch PSCAD, load Tutorial, and run all projects or simulation sets
#===============================================================================

# Launch PSCAD
LOG.info("Launching: %s  FORTRAN=%r   Matlab=%r", version, fortran, matlab)
pscad = mhrc.automation.launch_pscad(pscad_version=version, minimize=True,
                                     fortran_version=fortran,
                                     matlab_version=matlab)

if pscad:

    try:
        # Load only the 'voltage divider' project
        pscad.load(os.path.join(tutorial_dir, "vdiv.pscx"))

        workspace = pscad.workspace()

        # Get the list of simulation sets in the workspace
        sim_sets = workspace.list_simulation_sets()
        if len(sim_sets) > 0:
            LOG.info("Simulation sets: %s", sim_sets)

            # For each simulation set ...
            for sim_set_name in sim_sets:
                # ... run it
                LOG.info("Running simulation set '%s'", sim_set_name)
                sim_set = workspace.simulation_set(sim_set_name)
                sim_set.run()
                LOG.info("Simulation set '%s' complete", sim_set_name)
        else:
            # Get a list of all projects
            projects = pscad.list_projects()

            # Filter out libraries; only keep cases.
            cases = [prj for prj in projects if prj['type'] == 'Case']

            # For each case ...
            for case in cases:
                project = pscad.project(case['name'])

                LOG.info("Running '%s' (%s)", case['name'], case['description'])
                project.run( BuildEventHandler() )
                LOG.info("Run '%s' complete", case['name'])

                messages = project.messages()
                for msg in messages:
                    print("%s  %s  %s" % (msg.scope, msg.status, msg.text))

    finally:
        # Exit PSCAD
        pscad.quit()

else:
    LOG.error("Failed to launch PSCAD")

