# SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-or-later OR CERN-OHL-S-2.0+ OR Apache-2.0
from pathlib import Path
from typing import Dict, Tuple, Collection, Union, Iterable, Optional, Callable

from pdkmaster.task import OpenPDKTree, TaskManager as _PDKMasterTaskManager
from pdkmaster.technology import technology_ as _tch
from pdkmaster.design import library as _lbry

from .spice_ import SpiceNetlistFactory


__all__ = ["SPICEExportTaskT", "TaskManager"]


class _SPICEExportTask:
    def __init__(self, *,
        manager: "TaskManager",
        extra_filedep: Tuple[Union[str, Path], ...],
        extra_filedep_lib: Dict[str, Tuple[Path, ...]],
        extra_taskdep: Tuple[str, ...],
    ) -> None:
        self._mng = manager
        self._extra_filedep = extra_filedep
        self._extra_filedep_lib = extra_filedep_lib
        self._extra_taskdep = extra_taskdep
        pass

    def _gen_spice(self, lib_name: str):
        mng = self._mng
        openpdk_tree = mng._openpdk_tree

        lib = mng.lib4name(lib_name)

        lib_spice_dir = openpdk_tree.views_dir(lib_name=lib_name, view_name="spice")
        lib_spice_dir.mkdir(parents=True, exist_ok=True)
        with lib_spice_dir.joinpath(f"{lib_name}.spi").open("w") as f:
            f.write(mng.netlistfab.export_library(
                lib, header="* Autogenerated file; changes will be overwritten."
            ))
        with lib_spice_dir.joinpath(f"{lib_name}_baser.spi").open("w") as f:
            f.write(mng.netlistfab.export_library(
                lib, header="* Autogenerated file; changes will be overwritten.",
                use_semiconres=False,
            ))
        with lib_spice_dir.joinpath(f"{lib_name}_lvs.spi").open("w") as f:
            f.write(mng.netlistfab.export_library(
                lib, header="* Autogenerated file; changes will be overwritten.",
                incl_dummysubcircuits=True,
            ))

    def task_func(self):
        """Generate SPICE files"""
        mng = self._mng
        openpdk_tree = mng._openpdk_tree

        for lib in mng.cell_list.keys():
            spice_dir = openpdk_tree.views_dir(lib_name=lib, view_name="spice")

            f = Path(__file__)
            deps = (f, f.parent.joinpath("spice_.py"))
            yield {
                "name": lib,
                "doc": f"Creating spice files for library {lib}",
                "file_dep": (
                    *self._extra_filedep, *self._extra_filedep_lib.get(lib, ()), *deps,
                ),
                "task_dep": self._extra_taskdep,
                "targets": (
                    spice_dir.joinpath(f"{lib}.spi"),
                    spice_dir.joinpath(f"{lib}_lvs.spi"),
                    spice_dir.joinpath(f"{lib}_baser.spi"),
                ),
                "actions": (
                    (self._gen_spice, (lib,)),
                ),
            }
SPICEExportTaskT = _SPICEExportTask


class TaskManager(_PDKMasterTaskManager):
    def __init__(self, *,
        tech_cb: Callable[[], _tch.Technology],
        lib4name_cb: Callable[[str], _lbry.Library],
        cell_list: Dict[str, Collection[str]],
        top_dir: Path,
        openpdk_tree: OpenPDKTree,
        netlistfab_cb: Callable[[], SpiceNetlistFactory],
    ) -> None:
        super().__init__(
            tech_cb=tech_cb, lib4name_cb=lib4name_cb, cell_list=cell_list,
            top_dir=top_dir, openpdk_tree=openpdk_tree,
        )
        self._netlistfab_cb = netlistfab_cb

    @property
    def netlistfab(self) -> SpiceNetlistFactory:
        return self._netlistfab_cb()

    def create_export_task(self, *,
        extra_filedep: Iterable[Union[str, Path]]=(),
        extra_filedep_lib: Dict[str, Tuple[Path, ...]]={},
        extra_taskdep: Iterable[str]=(),
    ) -> SPICEExportTaskT:
        return _SPICEExportTask(
            manager=self,
            extra_filedep=tuple(extra_filedep), extra_filedep_lib=extra_filedep_lib,
            extra_taskdep=tuple(extra_taskdep),
        )