'''
    This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de).

    PM4Py 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.

    PM4Py 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 PM4Py.  If not, see <https://www.gnu.org/licenses/>.
'''
import tempfile
import uuid
from copy import deepcopy, copy
from enum import Enum

from graphviz import Graph

from pm4py.objects.process_tree.utils import generic
from pm4py.util import exec_utils
from typing import Optional, Dict, Any, Union
from pm4py.objects.process_tree.obj import ProcessTree
import graphviz
from pm4py.util import vis_utils, constants


class Parameters(Enum):
    FORMAT = "format"
    ENABLE_DEEPCOPY = "enable_deepcopy"
    FONT_SIZE = "font_size"
    BGCOLOR = "bgcolor"
    NUM_EVENTS_PROPERTY = "num_events_property"
    NUM_CASES_PROPERTY = "num_cases_property"


# maps the operators to the ProM strings
operators_mapping = {"->": "seq", "X": "xor", "+": "and", "*": "xor loop", "O": "or", "<>": "interleaving"}

# root node parameter
ROOT_NODE_PARAMETER = "@@root_node"


def repr_tree_2(tree, viz, parameters):
    num_events_property = exec_utils.get_param_value(Parameters.NUM_EVENTS_PROPERTY, parameters, "num_events")
    num_cases_property = exec_utils.get_param_value(Parameters.NUM_CASES_PROPERTY, parameters, "num_cases")
    root_node = parameters[ROOT_NODE_PARAMETER]

    root_node_num_cases = root_node._properties[num_cases_property]
    this_node_num_cases = tree._properties[num_cases_property] if num_cases_property in tree._properties else 0
    this_node_num_events = tree._properties[num_events_property] if num_events_property in tree._properties else 0

    font_size = exec_utils.get_param_value(Parameters.FONT_SIZE, parameters, 15)
    font_size = str(font_size)

    this_node_id = str(id(tree))

    if tree.operator is None:
        if tree.label is None:
            viz.node(this_node_id, "tau", style='filled', fillcolor='black', shape='point', width="0.075", fontsize=font_size)
        else:
            node_color = vis_utils.get_trans_freq_color(this_node_num_cases, 0, root_node_num_cases)
            node_label = str(tree) + "\nC=%d E=%d" % (this_node_num_cases, this_node_num_events)
            viz.node(this_node_id, node_label, fontsize=font_size, style="filled", fillcolor=node_color)
    else:
        node_color = vis_utils.get_trans_freq_color(this_node_num_cases, 0, root_node_num_cases)
        viz.node(this_node_id, operators_mapping[str(tree.operator)], fontsize=font_size, style="filled", fillcolor=node_color)

        for child in tree.children:
            repr_tree_2(child, viz, parameters)

    if tree.parent is not None:
        viz.edge(str(id(tree.parent)), this_node_id, dirType='none')


def apply(tree: ProcessTree, parameters: Optional[Dict[Union[str, Parameters], Any]] = None) -> graphviz.Graph:
    """
    Obtain a Process Tree representation through GraphViz

    Parameters
    -----------
    tree
        Process tree
    parameters
        Possible parameters of the algorithm

    Returns
    -----------
    gviz
        GraphViz object
    """
    if parameters is None:
        parameters = {}

    parameters = copy(parameters)
    parameters[ROOT_NODE_PARAMETER] = tree

    filename = tempfile.NamedTemporaryFile(suffix='.gv')

    bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR)

    viz = Graph("pt", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor})
    viz.attr('node', shape='ellipse', fixedsize='false')

    image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png")

    enable_deepcopy = exec_utils.get_param_value(Parameters.ENABLE_DEEPCOPY, parameters, False)

    if enable_deepcopy:
        # since the process tree object needs to be sorted in the visualization, make a deepcopy of it before
        # proceeding
        tree = deepcopy(tree)
        generic.tree_sort(tree)

    repr_tree_2(tree, viz, parameters)

    viz.attr(overlap='false')
    viz.attr(splines='false')
    viz.format = image_format.replace("html", "plain-ext")

    return viz
