#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#  hammer-vlsi plugin for Mentor Graphics CalibreDRC.
#
#  See LICENSE for licence details.

from hammer_vlsi import HammerToolStep
from hammer_vlsi import HammerDRCTool, MentorCalibreTool
from hammer_logging import HammerVLSILogging

from typing import Dict, List

import os
import textwrap


class CalibreDRC(HammerDRCTool, MentorCalibreTool):


    def tool_config_prefix(self) -> str:
        return "drc.calibre"

    def drc_results_pre_waived(self) -> Dict[str, int]:
        return {}

    def fill_outputs(self) -> bool:
        return True

    # TODO: placeholder empty step
    def empty_step(self) -> bool:
        return True

    @property
    def steps(self) -> List[HammerToolStep]:
        steps = [self.generate_drc_run_file]
        return self.make_steps_from_methods(steps)

    def do_post_steps(self) -> bool:
        assert super().do_post_steps()
        return self.run_calibre()

    def globally_waived_drc_rules(self) -> List[str]:
        return []

    def run_calibre(self) -> bool:
        self.create_enter_script()

        # TODO John move to LVS
        # generate the hcells file if needed
        # self.generate_hcells_file()

        # set the command arguments
        args = [
            self.get_setting("drc.calibre.calibre_drc_bin"),
            "-64",  # always want to be in 64-bit mode
            "-drc",  # Calibre LVS and DRC usually use the same executable and use a flag to change tasks
        ]
        if self.get_setting("drc.calibre.hierarchical_mode"):
            args.append("-hier")
        turbo_cores = self.get_setting("drc.calibre.turbo_cores")  # type: int
        if turbo_cores > 0:
            args.append("-turbo")
            args.append("%d" % turbo_cores)
        if self.get_setting("drc.calibre.hyperthreaded"):
            args.append("-hyper")
        # TODO john move this to LVS
        # if len(self.hcells_list) > 0:
        #    args.append("-hcells")
        #    args.append(self.hcells_file)
        args.append(self.drc_run_file)

        HammerVLSILogging.enable_colour = False
        HammerVLSILogging.enable_tag = False
        self.run_executable(args, cwd=self.run_dir)  # TODO: check for errors and deal with them
        HammerVLSILogging.enable_colour = True
        HammerVLSILogging.enable_tag = True

        # TODO: check that drc run was successful

        # Create view_drc script.
        os.makedirs(self.generated_scripts_dir, exist_ok=True)

        layerprops_file = self.get_setting("mentor.layerprops")
        if layerprops_file is not None:
            layerprops = "-dl " + os.path.abspath(layerprops_file)
        else:
            layerprops = ""

        with open(self.view_drc_script, "w") as f:
            f.write("""
        cd {run_dir}
        source enter
        {calibredrv} {layerprops} -m {gds} -rve -drc {results}
            """.format(
                run_dir=self.run_dir,
                calibredrv=self.get_setting("drc.calibre.calibredrv_bin"),
                layerprops=layerprops,
                gds=self.layout_file,
                results=self.drc_results_db
            ))
        os.chmod(self.view_drc_script, 0o755)

        return True

    def generate_drc_run_file(self) -> bool:
        """ Generate the DRC run file self.drc_run_file and fill its contents """
        with open(self.drc_run_file, "w") as f:
            f.write(textwrap.dedent("""
            // Generated by HAMMER

            LAYOUT PATH "{gds}"
            LAYOUT PRIMARY "{top}"
            LAYOUT SYSTEM GDSII

            DRC RESULTS DATABASE "{results}" ASCII
            DRC MAXIMUM RESULTS {max_results}
            DRC MAXIMUM VERTEX 4096

            DRC CELL NAME YES CELL SPACE XFORM
            DRC SUMMARY REPORT "{report}" REPLACE HIER

            VIRTUAL CONNECT COLON {virtual_connect}
            VIRTUAL CONNECT REPORT NO
            """).format(
                gds=self.layout_file,
                top=self.top_module,
                results=self.drc_results_db,
                max_results=self.max_drc_results,
                report=self.drc_report_file,
                virtual_connect="YES" if self.virtual_connect_colon else "NO"
            )
            )
            # Include paths to all supplied decks
            for rule in self.get_drc_decks():
                f.write("INCLUDE \"{}\"\n".format(rule.path))
            # Note that an empty list means run all, and Calibre conveniently will do just that
            # if we don't specify any individual checks to run.
            if len(self.drc_rules_to_run()) > 0:
                f.write("\nDRC SELECT CHECK\n")
            for check in self.drc_rules_to_run():
                f.write("\t\"{}\"\n".format(check))
            f.write("\nDRC ICSTATION YES\n")
            f.write(self.get_additional_drc_text())
        return True

    @property
    def generated_scripts_dir(self) -> str:
        return os.path.join(self.run_dir, "generated-scripts")

    @property
    def view_drc_script(self) -> str:
        return os.path.join(self.generated_scripts_dir, "view_drc")

    @property
    def drc_run_file(self) -> str:
        return os.path.join(self.run_dir, "drc_run_file")

    @property
    def drc_results_db(self) -> str:
        return os.path.join(self.run_dir, "drc_results.db")

    @property
    def drc_report_file(self) -> str:
        return os.path.join(self.run_dir, "drc_results.rpt")

    @property
    def max_drc_results(self) -> int:
        return 1000  # TODO parameterize me

    # TODO do we also want virtual connect by name (e.g. for hierarchical blocks)
    @property
    def virtual_connect_colon(self) -> bool:
        return False  # TODO we might want this to be True for hierarchical blocks

    @property
    def env_vars(self) -> Dict[str, str]:
        """
        Get the list of environment variables required for this tool.
        Note to subclasses: remember to include variables from super().env_vars!
        """
        result = dict(super().env_vars)
        result.update({
            "MGC_HOME": self.get_setting("drc.calibre.MGC_HOME")
        })
        return result


tool = CalibreDRC
