# -*- coding: utf-8 -*-
# serialization.py

"""
Utility function to serialize data and meta classes for export, to a HDF5 file, for example.
"""

__author__ = "Ingo Breßler"
__contact__ = "dev@ingobressler.net"
__license__ = "GPLv3+"
__date__ = "2023/02/07"
__status__ = "beta"

import xml.etree.ElementTree
from pathlib import Path, PurePosixPath

import graphviz


def type2str(obj):
    return ".".join((type(obj).__module__, type(obj).__name__))


def dumpKV(obj: object, path: PurePosixPath = None, lvl: int = 0, dbg: bool = False):
    """Serializes the given hierarchical DACHS structure as key-value pairs (a dict).

    :param objlst: A hierarchical instance for traversal.
    :param path: Optional, a word to prepend to all generated keys, a top-level name.
        It is replaced by the *ID* attribute if available.
    :param lvl: The current level of invocation, tracks the recursion depth for debugging.
    :param dbg: Enable debug output.
    """
    indent = "".join(["  " for _ in range(lvl)])
    if dbg:
        from pprint import pformat

        print(indent, f"{obj=}")
    if not path:  # may be undefined for the top-level object
        path = getattr(obj, "ID", None)
    path = PurePosixPath(path) if path else PurePosixPath("")
    if dbg:
        print(indent, f"path from id: '{path}', {type(obj)}")

    # find any children to traverse
    children = [(storeKey, getattr(obj, storeKey)) for storeKey in getattr(obj, "_storeKeys", [])]
    if not len(children):
        if type(obj) in (list, tuple):
            children = list(enumerate(obj))
        if type(obj) in (dict,):
            children = list(obj.items())
    # translate children path names from type names to their stored ID
    children = [(getattr(child, "ID", num), child) for num, child in children]
    if dbg:
        print(indent, f"{children=}")
    if not len(children):  # no children found
        # omit this object if empty? TODO
        return {path: obj}
    # first store some meta info for later graphKV stage extended info
    pathlst = {path / "type": type2str(obj)}
    for name, child in children:
        if dbg:
            print(indent, f"{lvl}>", name)
        subpath = path / str(name)
        items = dumpKV(child, subpath, lvl + 1, dbg=dbg)
        if dbg:
            indentCount = (lvl + 1) * 2 + 1
            print(indent, f"{lvl}<{{" + pformat(items, indent=indentCount, width=120)[1:].lstrip())
        pathlst.update(items)
    return pathlst


def graphKV(paths):
    docsPath = Path("dist/docs/reference/autosummary")
    graphName = list(paths.keys())[0].parts[0]

    # pprint.pprint(paths, width=120)
    def nodeFromParts(parts, i, paths):
        node = PurePosixPath(*parts[: i + 1])
        try:
            return node, paths[node / "type"]
        except KeyError:
            return node, type2str(paths[node])

    nodes = dict(
        nodeFromParts(path.parts, i, paths)
        for path in paths
        for i in range(len(path.parts))
        if path.parts[-1] != "type"  # filter type info, evaluated for node type info
    )
    edges = {
        (PurePosixPath(*path.parts[: i + 1]), PurePosixPath(*path.parts[: i + 2]))
        for path in paths
        for i in range(len(path.parts) - 1)
        if path.parts[-1] != "type"  # filter type info, evaluated for node type info
    }
    graph = graphviz.Digraph(graphName, format="svg", graph_attr=dict(rankdir="LR"))

    def sanitize(name):
        return str(name).replace(":", ".")

    for nodepath, nodetype in nodes.items():
        lbl = f"{nodepath.name}({nodetype.split('.')[-1]})"
        url, color = "", ""
        if nodetype.startswith("dachs."):
            url = docsPath / (nodetype + ".html")
            color = "blue"
        graph.node(sanitize(nodepath), label=lbl, URL=str(url), fontcolor=color)
    for tail, head in edges:
        graph.edge(sanitize(tail), sanitize(head))
    outfn = graph.render(graph.name, cleanup=True)
    fixSVG(outfn)


def fixSVG(fn):
    """Apply some modifications to SVG generated by Graphviz.

    Convert to PNG with::
        convert -background transparent AutoMOF5.svg AutoMOF5.png
    """
    fn = Path(fn)
    xml.etree.ElementTree.register_namespace("", "http://www.w3.org/2000/svg")
    tree = xml.etree.ElementTree.parse(fn)
    # remove background color from first polygon element
    elem = [elem for elem in tree.iter() if elem.tag.split("}")[-1] == "polygon"][0]
    elem.set("fill", "none")
    tree.write(fn)
