class BackgroundLayersMenu(QMenu):
    def __init__(self, iface, parent=None):
        QMenu.__init__(self, parent)
        self.iface = iface

        self.add_google_layergroup()
        self.add_XYZ_layergroups()

    def add_google_layergroup(self):
        try:
            self._olLayerTypeRegistry = WebLayerTypeRegistry(self)
        except:
            return

        self._olLayerTypeRegistry.register(OlGooglePhysicalLayer())
        self._olLayerTypeRegistry.register(OlGoogleStreetsLayer())
        self._olLayerTypeRegistry.register(OlGoogleHybridLayer())
        self._olLayerTypeRegistry.register(OlGoogleSatelliteLayer())

        for group in self._olLayerTypeRegistry.groups():
            groupMenu = group.menu()
            for layer in self._olLayerTypeRegistry.groupLayerTypes(group):
                layer.addMenuEntry(groupMenu, self.iface.mainWindow())

            add_api_key_action = QAction("Set api Key", groupMenu)
            add_api_key_action.triggered.connect(self.showGoogleApiKeyDialog)
            groupMenu.addAction(add_api_key_action)

            self.addMenu(groupMenu)

        # Register plugin layer type
        self.pluginLayerType = OpenlayersPluginLayerType(
            self.iface, self.setReferenceLayer, self._olLayerTypeRegistry)
        QgsApplication.pluginLayerRegistry().addPluginLayerType(
            self.pluginLayerType)

    def add_XYZ_layergroups(self):
        xyz_layers = {
            "OpenStreetMap": {
                "OpenStreetMap":
                'http://tile.openstreetmap.org/{z}/{x}/{y}.png',
                "OSM Humanitarian Data Model":
                'http://a.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
            },
            "OpenTopoMap": {
                "OSM OpenTopoMap":
                'http://c.tile.opentopomap.org/{z}/{x}/{y}.png'
            },
            #            "OSM/Stamen": {
            #                "Stamen Toner / OSM": 'http://tile.stamen.com/toner/{z}/{x}/{y}.png',
            #                "Stamen Toner Lite / OSM": 'http://tile.stamen.com/toner-lite/{z}/{x}/{y}.png',
            #                "Stamen Watercolor / OSM": 'http://tile.stamen.com/watercolor/{z}/{x}/{y}.jpg',
            #                "Stamen Terrain / OSM": 'http://tile.stamen.com/terrain/{z}/{x}/{y}.png'
            #            },
            "OSM/Thunderforest": {
                "OpenCycleMap":
                'https://tile.thunderforest.com/cycle/{z}/{x}/{y}.png',
                "OCM Landscape":
                'https://tile.thunderforest.com/landscape/{z}/{x}/{y}.png',
                "OCM Public Transport":
                'https://tile.thunderforest.com/transport/{z}/{x}/{y}.png',
                "Outdoors":
                'https://tile.thunderforest.com/outdoors/{z}/{x}/{y}.png',
                "Transport Dark":
                'https://tile.thunderforest.com/transport-dark/{z}/{x}/{y}.png',
                "Spinal Map":
                'https://tile.thunderforest.com/spinal-map/{z}/{x}/{y}.png',
                "Pioneer":
                'https://tile.thunderforest.com/pioneer/{z}/{x}/{y}.png',
                "Mobile Atlas":
                'https://tile.thunderforest.com/mobile-atlas/{z}/{x}/{y}.png',
                "Neighbourhood":
                'https://tile.thunderforest.com/neighbourhood/{z}/{x}/{y}.png'
            },
            "Wikipedia Maps": {
                "Wikipedia Labelled Layer":
                'https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png',
                "Wikipedia Unlabelled Layer":
                'https://maps.wikimedia.org/osm/{z}/{x}/{y}.png'
            },
            "Bing Maps": {
                "Bing Roads":
                'http://ecn.t3.tiles.virtualearth.net/tiles/r{q}.jpeg?g=1',
                "Bing Aerial":
                'http://ecn.t3.tiles.virtualearth.net/tiles/a{q}.jpeg?g=1',
                "Bing Aerial with labels":
                'http://ecn.t3.tiles.virtualearth.net/tiles/h{q}.jpeg?g=1'
            }
        }

        for layer_type in xyz_layers:
            menu = QMenu(layer_type, self)
            for layer in xyz_layers[layer_type]:
                action = self.create_add_layer_action(
                    xyz_layers[layer_type][layer], layer, menu)
                menu.addAction(action)
            if "Thunderforest" in layer_type:
                add_api_key_action = QAction("Set api Key", menu)
                add_api_key_action.triggered.connect(
                    self.showThunderforestApiKeyDialog)
                menu.addAction(add_api_key_action)
            self.addMenu(menu)

    def create_add_layer_action(self, url, title, parent):
        action = QAction(title, parent)
        action.triggered.connect(
            lambda: self.addLayer(None, xyzUrl=url, displayName=title))
        return action

    def showThunderforestApiKeyDialog(self):
        apiKey = QSettings().value("qgis-cloud-plugin/thunderforestApiKey")
        newApiKey, ok = QInputDialog.getText(
            self.iface.mainWindow(), "API key",
            "Enter your API key (<a href=\"https://thunderforest.com/pricing/\">https://thunderforest.com</a>)",
            QLineEdit.Normal, apiKey)
        if ok:
            QSettings().setValue("qgis-cloud-plugin/thunderforestApiKey",
                                 newApiKey)

    def showGoogleApiKeyDialog(self):
        apiKey = QSettings().value("Plugin-OpenLayers/googleMapsApiKey")
        newApiKey, ok = QInputDialog.getText(self.iface.mainWindow(),
                                             "API key", "Enter your API key",
                                             QLineEdit.Normal, apiKey)
        if ok:
            QSettings().setValue("Plugin-OpenLayers/googleMapsApiKey",
                                 newApiKey)

    def addLayer(self, layerType, xyzUrl=None, displayName=None):
        if layerType is None:
            thunderforest_api_key = QSettings().value(
                "qgis-cloud-plugin/thunderforestApiKey")

            google_api_key = QSettings().value(
                "Plugin-OpenLayers/googleMapsApiKey")

            if "thunderforest" in xyzUrl and thunderforest_api_key:
                xyzUrl = xyzUrl + "?apikey=%s" % thunderforest_api_key
            elif "google" in xyzUrl and google_api_key:
                xyzUrl = xyzUrl + "?key=%s" % google_api_key

            layer = QgsRasterLayer('type=xyz' + '&url=' + xyzUrl, displayName,
                                   'wms')
        else:
            layer = OpenlayersLayer(self.iface, self._olLayerTypeRegistry)
            layer.setName(layerType.displayName)
            layer.setLayerType(layerType)

        if not layer.isValid():
            return

        coordRefSys = QgsCoordinateReferenceSystem()
        coordRefSys.createFromOgcWmsCrs("EPSG:3857")
        success = self.setMapCrs(coordRefSys)
        if success:
            QgsProject.instance().addMapLayer(layer, False)
            legendRootGroup = self.iface.layerTreeView().layerTreeModel(
            ).rootGroup()
            legendRootGroup.insertLayer(len(legendRootGroup.children()), layer)

            # last added layer is new reference
            self.setReferenceLayer(layer)

    def setReferenceLayer(self, layer):
        self.layer = layer

    def removeLayer(self, layerId):
        if self.layer is not None and self.layer.id() == layerId:
            self.layer = None

    def canvasCrs(self):
        mapCanvas = self.iface.mapCanvas()
        crs = mapCanvas.mapSettings().destinationCrs()
        return crs

    def setMapCrs(self, coordRefSys):
        mapCanvas = self.iface.mapCanvas()
        canvasCrs = self.canvasCrs()

        if canvasCrs != coordRefSys:
            coordTrans = QgsCoordinateTransform(canvasCrs, coordRefSys,
                                                QgsProject.instance())
            extMap = mapCanvas.extent()

            try:
                extMap = coordTrans.transform(
                    extMap, QgsCoordinateTransform.ForwardTransform)
                QgsProject.instance().setCrs(coordRefSys)
                mapCanvas.freeze(False)
                mapCanvas.setExtent(extMap)
                return True
            except:
                res = QMessageBox.critical(
                    self, self.tr("Error"),
                    self.
                    tr("""A serious error has occurred during the coordinate transformation. Please set the reference system of the project to the WGS84 / Pseudo-Mercartor Projektion (EPSG: 3857) and reload the layer."""
                       ))
                return False
        else:
            return True
class OpenlayersMenu(QMenu):
    def __init__(self, iface, parent=None):
        QMenu.__init__(self, parent)
        self.iface = iface
        self._olLayerTypeRegistry = WebLayerTypeRegistry(self)

        self._olLayerTypeRegistry.register(OlGooglePhysicalLayer())
        self._olLayerTypeRegistry.register(OlGoogleStreetsLayer())
        self._olLayerTypeRegistry.register(OlGoogleHybridLayer())
        self._olLayerTypeRegistry.register(OlGoogleSatelliteLayer())

        self._olLayerTypeRegistry.register(OlOpenStreetMapLayer())
        self._olLayerTypeRegistry.register(OlOpenCycleMapLayer())
        self._olLayerTypeRegistry.register(OlOCMLandscapeLayer())
        self._olLayerTypeRegistry.register(OlOCMPublicTransportLayer())
        self._olLayerTypeRegistry.register(OlOSMHumanitarianDataModelLayer())

        self._olLayerTypeRegistry.register(OlBingRoadLayer())
        self._olLayerTypeRegistry.register(OlBingAerialLayer())
        self._olLayerTypeRegistry.register(OlBingAerialLabelledLayer())

        self._olLayerTypeRegistry.register(OlOSMStamenTonerLayer())
        self._olLayerTypeRegistry.register(OlOSMStamenWatercolorLayer())
        self._olLayerTypeRegistry.register(OlOSMStamenTerrainLayer())

        self._olLayerTypeRegistry.register(OlMapQuestOSMLayer())
        self._olLayerTypeRegistry.register(OlMapQuestOpenAerialLayer())

        self._olLayerTypeRegistry.register(OlAppleiPhotoMapLayer())

        for group in self._olLayerTypeRegistry.groups():
            groupMenu = group.menu()
            for layer in self._olLayerTypeRegistry.groupLayerTypes(group):
                layer.addMenuEntry(groupMenu, self.iface.mainWindow())
            self.addMenu(groupMenu)

        # Register plugin layer type
        self.pluginLayerType = OpenlayersPluginLayerType(
            self.iface, self.setReferenceLayer, self._olLayerTypeRegistry)
        QgsPluginLayerRegistry.instance().addPluginLayerType(self.pluginLayerType)

    def addLayer(self, layerType):
        layer = OpenlayersLayer(self.iface, self._olLayerTypeRegistry)
        layer.setLayerName(layerType.displayName)
        layer.setLayerType(layerType)
        if layer.isValid():
            coordRefSys = layerType.coordRefSys(self.canvasCrs())
            self.setMapCrs(coordRefSys)
            QgsMapLayerRegistry.instance().addMapLayer(layer)

            # last added layer is new reference
            self.setReferenceLayer(layer)

    def setReferenceLayer(self, layer):
        self.layer = layer

    def removeLayer(self, layerId):
        if self.layer is not None:
            if QGis.QGIS_VERSION_INT >= 10900:
                if self.layer.id() == layerId:
                    self.layer = None
            else:
                if self.layer.getLayerID() == layerId:
                    self.layer = None
            # TODO: switch to next available OpenLayers layer?

    def canvasCrs(self):
        mapCanvas = self.iface.mapCanvas()
        if QGis.QGIS_VERSION_INT >= 20300:
            #crs = mapCanvas.mapRenderer().destinationCrs()
            crs = mapCanvas.mapSettings().destinationCrs()
        elif QGis.QGIS_VERSION_INT >= 10900:
            crs = mapCanvas.mapRenderer().destinationCrs()
        else:
            crs = mapCanvas.mapRenderer().destinationSrs()
        return crs

    def setMapCrs(self, coordRefSys):
        mapCanvas = self.iface.mapCanvas()
        # On the fly
        if QGis.QGIS_VERSION_INT >= 20300:
            mapCanvas.setCrsTransformEnabled(True)
        else:
            mapCanvas.mapRenderer().setProjectionsEnabled(True)
        canvasCrs = self.canvasCrs()
        if canvasCrs != coordRefSys:
            coordTrans = QgsCoordinateTransform(canvasCrs, coordRefSys)
            extMap = mapCanvas.extent()
            extMap = coordTrans.transform(extMap, QgsCoordinateTransform.ForwardTransform)
            if QGis.QGIS_VERSION_INT >= 20300:
                mapCanvas.setDestinationCrs(coordRefSys)
            elif QGis.QGIS_VERSION_INT >= 10900:
                mapCanvas.mapRenderer().setDestinationCrs(coordRefSys)
            else:
                mapCanvas.mapRenderer().setDestinationSrs(coordRefSys)
            mapCanvas.freeze(False)
            mapCanvas.setMapUnits(coordRefSys.mapUnits())
            mapCanvas.setExtent(extMap)