# Copyright (c) 2017-2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0


from typing import Dict, List, Sequence

from ..damlast.daml_lf_1 import DottedName, Module, ModuleRef
from ..damlast.util import module_name, package_ref


class ModuleHierarchy:
    START = "START"
    ITEM = "ITEM"
    END = "END"

    def __init__(self, package_id, module_name: "Sequence[str]" = ()):
        self.ref = ModuleRef(package_id, DottedName(module_name))
        self._items = dict()  # type: Dict[str, ModuleHierarchy]
        self._modules = list()  # type: List[Module]

    def __getitem__(self, item: str):
        """
        Retrieve the sub-:class:`ModuleBuilder` prefixed by a name.
        :param item:
        :return:
        """
        mb = self._items.get(item)
        if mb is None:
            mn = module_name(self.ref)
            # TODO: Revisit in dazl 7.0.0 when the internal structures of ModuleRef and DottedName change
            mb = ModuleHierarchy(
                package_ref(self.ref), (f"{mn}.{item}" if mn else str(mn)).split(".")
            )
            self._items[item] = mb
        return mb

    def add_module(self, module: "Module") -> None:
        obj = self
        components = str(module.name).split(".")
        for component in components:
            obj = obj[component]
        obj._modules.append(module)

    def __iter__(self):
        yield ModuleHierarchy.START, self.ref, None
        for child in self._items.values():
            yield from child
        for mod in self._modules:
            yield ModuleHierarchy.ITEM, self.ref, mod
        yield ModuleHierarchy.END, self.ref, None

    # def lines(self, depth: int = 0) -> Generator[str, None, None]:
    #     prefix = (depth * 4) * ' '
    #     for key, mb in self._items.items():
    #         yield ''
    #         yield ''
    #         yield f'{prefix}class {key}:'
    #         for leaf, template in mb._leaves.items():
    #             template_fn = template.data_type.name.full_name
    #             slot_names = tuple(name for name, _ in template.data_type.named_args)
    #
    #             yield f'{prefix}    class {leaf}(metaclass=TemplateMeta, template_name={template_fn!r}):'
    #             yield f'{prefix}        """'
    #             yield f'{prefix}        Example usage:'
    #             yield f'{prefix}            create({template_fn},'
    #             yield f'{prefix}                {python_example_object(self.store, template.data_type)}'
    #             yield f'{prefix}            )'
    #             for choice in template.choices:
    #                 yield f'{prefix}            exercise(cid, {choice.name!r}, {python_example_object(self.store, choice.data_type)})'
    #             yield f'{prefix}        """'
    #             yield f'{prefix}        __slots__ = {slot_names!r}'
    #             yield f'{prefix}'
    #             if slot_names:
    #                 yield f'{prefix}        def __init__(self, {", ".join(slot_names)}):'
    #                 for slot_name in slot_names:
    #                     yield f'{prefix}            self.{slot_name} = {slot_name}'
    #                 yield ''
    #                 yield f'{prefix}        def _asdict(self) -> dict:'
    #                 yield f'{prefix}            return {{'
    #                 for slot_name in slot_names:
    #                     yield f'{prefix}                {slot_name!r}: self.{slot_name},'
    #                 yield f'{prefix}                }}'
    #             else:
    #                 yield f'{prefix}        def _asdict(self) -> dict:'
    #                 yield f'{prefix}            return {{}}'
    #
    #             for choice in template.choices:
    #                 choice_data_type = self.store.resolve_type_reference(choice.data_type) if isinstance(choice.data_type, TypeReference) else choice.data_type
    #                 choice_slot_names = tuple(name for name, _ in choice_data_type.named_args) if isinstance(choice_data_type, RecordType) else ()
    #                 yield ''
    #                 yield f'{prefix}        class {choice.name}(metaclass=ChoiceMeta, template_name={template_fn!r}, choice_name={choice.name!r}):'
    #                 yield f'{prefix}            __slots__ = {choice_slot_names!r}'
    #
    #                 if choice_slot_names:
    #                     yield ''
    #                     yield f'{prefix}            def __init__(self, {", ".join(choice_slot_names)}):'
    #                     for choice_slot_name in choice_slot_names:
    #                         yield f'{prefix}                self.{choice_slot_name} = {choice_slot_name}'
    #                     yield ''
    #                     yield f'{prefix}            def _asdict(self) -> dict:'
    #                     yield f'{prefix}                return {{'
    #                     for choice_slot_name in choice_slot_names:
    #                         yield f'{prefix}                    {choice_slot_name!r}: self.{choice_slot_name},'
    #                     yield f'{prefix}                    }}'
    #                 else:
    #                     yield f'{prefix}            def _asdict(self) -> dict:'
    #                     yield f'{prefix}                return {{}}'
    #             yield f''
    #         for line in mb.lines(depth + 1):
    #             yield line

    def __repr__(self):
        return f"ModuleBuilder(ref={self.ref}, items={self._items})"
