# Copyright (C) 2014-2018 Enzien Audio, Ltd.
# Copyright (C) 2023-2026 Wasted Audio
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import argparse
import json
import os
import time

from typing import Optional
from pathlib import Path

from hvcc.core.hv2ir.HeavyException import HeavyException
from hvcc.core.hv2ir.HeavyParser import HeavyParser

from hvcc.types.compiler import CompilerResp, CompilerNotif, CompilerMsg


class hv2ir:

    @classmethod
    def compile(
        cls,
        hv_file: Path,
        ir_file: Path,
        patch_name: Optional[str] = None,
        verbose: bool = False
    ) -> CompilerResp:
        """ Compiles a HeavyLang file into a HeavyIR file.
            Returns a tuple of compile time in seconds, a notification dictionary,
            and a heavy object counter.
        """

        # keep track of the total compile time
        tick = time.time()

        hv_file = hv_file.expanduser().absolute()
        ir_file = ir_file.expanduser().absolute()

        try:
            # parse heavy file
            hv_graph = HeavyParser.graph_from_file(hv_file=hv_file, xname=patch_name)
        except HeavyException as e:
            return CompilerResp(
                stage="hv2ir",
                compile_time=time.time() - tick,
                notifs=CompilerNotif(
                    has_error=True,
                    exception=e,
                    errors=[CompilerMsg(message=e.message)],
                    warnings=[]
                ),
                in_file=hv_file,
                in_dir=hv_file.parent,
                out_file=ir_file,
                out_dir=ir_file.parent
            )

        try:
            # get a counter of all heavy objects
            hv_counter = hv_graph.get_object_counter(recursive=True)

            # prepare the graph for exporting
            hv_graph.prepare()

            # ensure that the output directory exists
            if not ir_file.parent.exists():
                os.makedirs(ir_file.parent)

            # generate Heavy.IR
            ir = hv_graph.to_ir()
        except HeavyException as e:
            return CompilerResp(
                stage="hv2ir",
                compile_time=time.time() - tick,
                notifs=CompilerNotif(
                    has_error=True,
                    exception=e,
                    errors=[CompilerMsg(message=e.message)],
                    warnings=[]
                ),
                in_file=hv_file,
                in_dir=hv_file.parent,
                out_file=ir_file,
                out_dir=ir_file.parent,
                obj_counter=hv_counter
            )

        if ir is not None:
            # write the hv.ir file
            with open(ir_file, "w") as f:
                json.dump(ir.model_dump(), f, indent=4)

            if verbose and ir is not None:
                if len(ir.signal.processOrder) > 0:
                    print("")
                    print("=== Signal Order ===")
                    for so in ir.signal.processOrder:
                        o = ir.objects[so.id]
                        if len(o.args) > 0:
                            print("{0} {{{1}}}".format(
                                o.type,
                                " ".join([f"{k}:{v}" for k, v in o.args.items()])))
                        else:
                            print(o.type)

        return CompilerResp(
            stage="hv2ir",
            compile_time=time.time() - tick,  # record the total compile time
            notifs=hv_graph.get_notices(),
            in_file=hv_file,
            in_dir=hv_file.parent,
            out_file=ir_file,
            out_dir=ir_file.parent,
            obj_counter=hv_counter,
            ir=ir
        )


def main() -> None:
    parser = argparse.ArgumentParser(
        description="A C-language compiler for the Heavy audio programming language.")
    parser.add_argument(
        "hv_path",
        help="The patch to the top-level patch to compile.")
    parser.add_argument(
        "--hv_ir_path",
        default="./heavy.hv.ir.json",
        help="The output path of the hv.ir.json file.")
    parser.add_argument(
        "--name",
        default="heavy",
        help="")
    parser.add_argument("-v", "--verbose", action="count")
    args = parser.parse_args()

    d = hv2ir.compile(
        hv_file=args.hv_path,
        ir_file=args.hv_ir_path,
        patch_name=args.name,
        verbose=args.verbose)

    if args.verbose:
        print(f"Total hv2ir time: {(d.compile_time * 1000):.2f}ms")


if __name__ == "__main__":
    main()
