import paraview.modules.vtkRemotingMicroservices
import paraview.modules.vtkRemotingServerManagerDefinitions
from paraview.modules.vtkRemotingServerManager import vtkClientSession, vtkSMProxy
from paraview.modules.vtkRemotingPythonAsyncCore import (
    vtkPythonObservableWrapperUtilities,
)
from paraview.modules.vtkRemotingServerManagerCore import vtkSMPropertyTypes
from paraview.modules.vtkUIGenerationCore import vtkLayoutGenerator

PROPERTY_TYPES = {
    vtkSMPropertyTypes.INVALID: "none",
    vtkSMPropertyTypes.INT: "int32",
    vtkSMPropertyTypes.DOUBLE: "float64",
    vtkSMPropertyTypes.ID_TYPE: "int32",
    vtkSMPropertyTypes.STRING: "string",
    vtkSMPropertyTypes.PROXY: "proxy",
    vtkSMPropertyTypes.INPUT: "proxy",
}


class ProxyAdapter(
    paraview.modules.vtkRemotingServerManagerDefinitions.vtkProxyAdapter
):
    def as_dict(self):
        group, name = self.GetGroup(), self.GetName()
        _tags = []
        proxy_type = f"{group}__{name}"
        proxy_definition = {"_tags": _tags}

        _ordered_groups = []
        _ordered_properties = []

        for group in self.GetPropertyGroups():
            _ordered_groups.append(group.GetName())
            group_definition = {
                "label": group.GetXMLLabel(),
                "widget": group.GetPanelWidget(),
                "visibility": group.GetPanelVisibility(),
            }
            proxy_definition[f"_group_{group.GetName()}"] = group_definition

        for property in self.GetProperties():
            property_definition = ProxyAdapter._get_property_adapter_as_dict(property)

            if property_definition:
                proxy_definition[property.GetName()] = property_definition
                _ordered_properties.append(property.GetName())

        proxy_definition["_ordered_properties"] = _ordered_properties
        proxy_definition["_ordered_groups"] = _ordered_groups
        return {proxy_type: proxy_definition}

    @staticmethod
    def _get_property_domains_as_dict(property):
        result = []

        for domain in property.GetDomains():
            domain_class = domain.GetClassName()
            domain_name = domain.GetName()

            # keep, name, widget, ui_attributes = domains.get_domain_widget(domain)
            domain_entry = {
                "name": domain_name,
                "type": "ParaViewDomain",
                "pv_class": domain_class,
                "pv_name": domain_name,
                # "widget": widget,
                # "pv_widget": widget, move logic from pqProxyWidget or show propertyproxy wiget if it os a special case
                # "ui_attributes": ui_attributes, # look into pqProxyWidget each case
            }
            # if keep:
            #    result.append(domain_entry)
            result.append(domain_entry)

        return result

    @staticmethod
    def _get_property_adapter_as_dict(property):
        property_definition = {}

        # if (
        #    property.GetInformationOnly()
        #    or property.GetIsInternal()
        #    # or property.GetClassName() not in PROPERTY_TYPES
        # ):
        if property.GetPanelVisibility() == "never" or property.GetIsInternal():
            return False

        # if len(property.GetLabel()) > 0:
        property_definition["_label"] = property.GetLabel()
        property_definition["_group"] = (
            property.GetPropertyGroup().GetName()
            if property.GetPropertyGroup()
            else None
        )
        property_definition["_help"] = property.GetDocumentationDescriptionAsString()
        property_definition["type"] = PROPERTY_TYPES[property.GetType()]
        property_definition["size"] = property.GetSize()
        property_definition["ui_attributes"] = {
            "panel_visibility": property.GetPanelVisibility(),
            "widget": property.GetPanelWidget().GetWidgetClassName(),
            "layout": property.GetPanelWidget().GetWidgetLayout(),
            "decorators": list(property.GetDecorators()),
        }
        print("decorators", *property.GetDecorators())

        # Skip proxy property with n proxies (??? FIXME ???)
        # currently simput don't properly manage list of proxy as property...
        # i.e. views.RenderView.Representations
        # if property_definition["type"] == "proxy" and property_definition["size"] != 1:
        #    print(
        #        "Skip multi-proxy property",
        #        # property.GetParent().GetXMLName(), // TODO
        #        property.GetName(),
        #    )
        #    return False

        # Domains
        property_definition["domains"] = [
            *ProxyAdapter._get_property_domains_as_dict(property),
        ]
        #    *property_domains_yaml(property),
        #    *merge_decorators(
        #        *property_widget_decorator_yaml(property),
        #        *property_widget_decorator_advanced_yaml(property),
        #    ),
        # ]

        return property_definition


class DefinitionManager(
    paraview.modules.vtkRemotingMicroservices.vtkPTSDefinitionManager
):
    """
    A thin Python Wrapper around vtkPTSProxyDefintionsModel
    """

    def __init__(self, session):

        if not isinstance(session, vtkClientSession):
            raise TypeError(" 'session' argument is not of type vtkClientSession")

        super().SetSession(session)

    async def GetDefinition(self, *args):
        definition = None
        if len(args) == 1 and isinstance(args[0], vtkSMProxy):
            definition = await vtkPythonObservableWrapperUtilities.GetFuture(
                super().GetDefinition(args[0])
            )
        elif len(args) == 2 and isinstance(args[0], str) and isinstance(args[1], str):
            definition = await vtkPythonObservableWrapperUtilities.GetFuture(
                super().GetDefinition(args[0], args[1])
            )
        else:
            types = [type(arg) for arg in args]
            raise RuntimeError(f"Unsupported argument types {*types,}")

        result = ProxyAdapter()
        # we do not yet Python overrides yet so we construct a new ProxyAdapter
        result.Move(definition)
        return result

    async def GetLayout(self, *args):
        if len(args) == 1 and isinstance(args[0], vtkSMProxy):
            return await vtkPythonObservableWrapperUtilities.GetFuture(
                super().GetLayout(args[0])
            )
        elif len(args) == 2 and isinstance(args[0], str) and isinstance(args[1], str):
            return await vtkPythonObservableWrapperUtilities.GetFuture(
                super().GetLayout(args[0], args[1])
            )
        else:
            types = [type(arg) for arg in args]
            raise RuntimeError(f"Unsupported argument types {*types,}")

    @staticmethod
    def vtkPVXMLElement_to_string(xmlElement):
        return vtkLayoutGenerator.XMLToString(xmlElement)
