import json
from typing import Optional, Tuple

from PySide2.QtCore import QTimer
from PySide2.QtWidgets import QAction
from axipy import (
    Plugin, view_manager, CoordSystem, Pnt, mainwindow, Map, Layer, provider_manager, Notifications, DataObject
)


class RosreestrTms(Plugin):

    def __init__(self) -> None:
        self.action = None
        self.menu = None
        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.start()
        self.timer.timeout.connect(self.init_menu)

    def init_menu(self) -> None:
        if mainwindow.is_valid:
            act_name = "TmsDataProviderMenuAction"
            actions = mainwindow.widget.findChildren(QAction, act_name)
            if len(actions):
                self.menu = actions[0].menu()
                self.action = QAction("Публичная кадастровая карта")
                self.menu.insertAction(self.menu.actions()[0], self.action)
                self.action.triggered.connect(self.show_map)
                self.timer.stop()
            else:
                print("Действие {} не найдено".format(act_name))

    def unload(self) -> None:
        if self.action is not None:
            self.menu.removeAction(self.action)

    @staticmethod
    def zoom_for_level(level: int) -> float:
        return 40000000 / pow(2, level)

    @staticmethod
    def get_levels(data: dict) -> Tuple[int, int]:
        if "level" in data:
            levels = data["level"]
            return levels["min"], levels["max"]
        return 0, 21

    def open_tms(self, data: dict) -> DataObject:
        s = data["size"] if "size" in data else 1024
        w = data["watermark"] if "watermark" in data else ''
        p = data["prj"] if "prj" in data else None
        _min, _max = self.get_levels(data)
        attempts = data["maxAttempts"] if "maxAttempts" in data else 0
        return provider_manager.tms.open(
            data["url"],
            size=(s, s),
            watermark=w,
            minLevel=_min,
            maxLevel=_max,
            prj=p,
            maxAttempts=attempts
        )

    @staticmethod
    def open_rest(data) -> DataObject:
        f = data["format"] if "format" in data else "PNG32"
        sr = data["imageSR"] if "imageSR" in data else 102100
        lays = data["layer"] if "layer" in data else ''
        attempts = data["maxAttempts"] if "maxAttempts" in data else 0
        return provider_manager.rest.open(data["url"], fmt=f, imageSR=sr, layers=lays, maxAttempts=attempts)

    def process_layer(self, title: str, data: dict) -> Optional[Layer]:
        if "type" in data:
            t = None
            if data["type"] == "tms":
                t = self.open_tms(data)
            elif data["type"] == "rest":
                t = self.open_rest(data)
            if t is not None:
                if "name" in data:
                    t.name = data["name"]
                layer = Layer.create(t)
                if layer is not None:
                    layer.title = title
                    if "visibled" in data:
                        layer.visible = data["visibled"]
                    if data["type"] == "rest" and "level" in data:
                        _min, _max = self.get_levels(data)
                        layer.zoom_restrict = True
                        layer.min_zoom = self.zoom_for_level(_max)
                        layer.max_zoom = self.zoom_for_level(_min)
                    return layer

    def map_from_json(self, js: dict) -> Map:
        m = Map()
        if "layers" in js:
            for lay in js["layers"]:
                for n, v in lay.items():
                    if isinstance(v, list):
                        cnt = len(m.layers)
                        for v_ in v:
                            for n1, v1 in v_.items():
                                layer = self.process_layer(n1, v1)
                                if layer is not None:
                                    m.layers.add(layer)
                        if len(m.layers) > cnt:
                            m.layers.group(list(range(cnt, len(m.layers))), n)
                    elif isinstance(v, dict):
                        layer = self.process_layer(n, v)
                        if layer is not None:
                            m.layers.add(layer)
        return m

    @staticmethod
    def parse_json_file(filename: str) -> dict:
        file = open(filename, 'r', encoding="UTF-8")
        json_string = file.read()
        file.close()
        return json.loads(json_string)

    def show_map(self) -> None:
        Notifications.push(
            "Предупреждение",
            "Отрисовка данных кадастровой карты на некоторых масштабах может производиться с временной задержкой."
        )
        js = self.parse_json_file(str(self.plugin_dir / "sites.json"))
        m = self.map_from_json(js)
        mapview = view_manager.create_mapview(m)
        mapview.coordsystem = CoordSystem.from_prj('Earth Projection 10, 104, "m", 0')
        mapview.center = Pnt(4180000, 7470000)
        mapview.set_zoom(300000)
