# -*- coding: utf-8 -*-

__author__ = '(C) 2023 by Gerald Kogler'
__date__ = '1/10/2023'
__copyright__ = 'Copyright 2023, 300.000 Km/s'
__license__ = 'GPLv3 license'

from qgis.core import Qgis, QgsProject, QgsExpressionContextUtils, QgsGeometry, QgsAttributeEditorElement, QgsWkbTypes, QgsLayerTreeLayer, QgsProviderRegistry
from qgis.PyQt.QtCore import *

import os
import shutil
import json
import ast

from bridgestyle.qgis import layerStyleAsMapbox
from mapboxStyleScripts import getLayerStyle
from .utils import Utils

class mapStoreHandler():
    def __init__(self, parent, _printLog=None):
        """Constructor."""

        self.parent = parent
        self.pluginSettings = None
        self.mapStore = mapStore()
        self._printLog = _printLog

        # temporary fix to write directly to viewer
        self.viewerPath = QgsProject.instance().readPath("viewer/data")
        self.exportPath = QgsProject.instance().readPath("./")


    def makeExportFolder(self):
        """Make folder to output mbtiles and styles."""
        
        projectPath = QgsProject.instance().readPath("./")
        if self.parent.dlg1.dialog1_folder.filePath():
            self.exportPath = self.parent.dlg1.dialog1_folder.filePath()
        else:
            # make folder to output mbtiles and styles if export not defined?
            self.exportPath = os.path.join(projectPath, 'export')
            self.parent.dlg1.dialog1_folder.setFilePath(self.exportPath)

        # clean up if not dangerous
        # if os.path.exists(self.exportPath) and not self.exportPath == projectPath:
        #     shutil.rmtree(self.exportPath)
        # if not self.exportPath == projectPath:
        #     os.makedirs(self.exportPath, exist_ok = True)

        # make subfolder for every mapset
        mapsetName = Utils.toAsciiAlnum(self.parent.dlg1.dialog1_name.text())
        mapsetPath = os.path.join(self.exportPath, 'mapset_' + mapsetName)

        if not os.path.exists(mapsetPath):
            os.makedirs(mapsetPath)

        # make data subfolder
        dataPath = os.path.join(mapsetPath, 'data/attachments_' + mapsetName)
        if not os.path.exists(dataPath):
            os.makedirs(dataPath)

        # Copy file attachments to export/data folder
        for i in range(self.parent.dlg1.dialog1_attachments.rowCount()):
            file = self.parent.dlg1.dialog1_attachments.cellWidget(i, 0).filePath()
            if os.path.exists(file):
                shutil.copy2(file, dataPath)

        return mapsetPath


    def readSettingsFromProject(self, dlg=None):
        """Read all settings from QGIS project and show in dialogs."""

        if self._printLog:
            print("readSettingsFromProject", dlg, self.mapStore.dataLoaded)

        if dlg == None or not self.mapStore.dataLoaded:
            self.pluginSettings = QgsExpressionContextUtils.projectScope(QgsProject.instance()).variable("project_qwv")
            if self._printLog:
                print("self.pluginSettings", self.pluginSettings)

            if self.pluginSettings and (not hasattr(self.mapStore, "plugin_version") or self.mapStore.plugin_version != self.parent.pluginVersion):
                self.parent.iface.messageBar().pushMessage(Utils.tr("Warning"), Utils.tr("Settings from old plugin version detected, please delete 'project_qwv' from Project Settings -> Variables"), level=Qgis.Warning, duration=3)

            if self.pluginSettings:
                self.mapStore = mapStore(dotdict(ast.literal_eval(self.pluginSettings)))
            else:
                self.mapStore.plugin_version = self.parent.pluginVersion

            return False

        if dlg < 1 or dlg > 3:
            return False

        if not self.pluginSettings:
            return False

        if dlg == 1:
            self.parent.dlg1.dialog1_folder.setFilePath(self.mapStore.dlg1_folder)
            self.parent.dlg1.dialog1_name.setText(self.mapStore.dlg1_name)
            self.parent.dlg1.dialog1_description.clear()
            self.parent.dlg1.dialog1_description.appendPlainText(self.mapStore.dlg1_description)
            self.parent.dlg1.dialog1_extent.setText(self.mapStore.dlg1_extent)
            if self.mapStore.dlg1_multiple == 2:
                self.parent.dlg1.dialog1_multiple_mbtiles.setChecked(True)

            # fill background table
            # for bg in self.mapStore.dlg1_backgrounds:
            #     for index in range(self.parent.dlg1.dialog1_backgrounds.count()):
            #         item = self.parent.dlg1.dialog1_backgrounds.item(index)
            #         if bg['name'] == item.text():
            #             item.setSelected(True)

            # remove empty row and fill attachment table
            if len(self.mapStore.dlg1_attachments) > 0:
                self.parent.dlg1.dialog1_attachments.setCurrentCell(0,0)
                self.parent.dlg1.removeRowAttachment()
            for attachment in self.mapStore.dlg1_attachments:
                attachment = dotdict(attachment)
                self.parent.dlg1.addRowAttachment(attachment.src, attachment.name)

        elif dlg == 2:
            self.parent.dlg2.dialog2_folder.setFilePath(self.mapStore.dlg2_folder)
            self.parent.dlg2.dialog2_title.setText(self.mapStore.dlg2_title)
            self.parent.dlg2.dialog2_subtitle.setText(self.mapStore.dlg2_subtitle)
            self.parent.dlg2.dialog2_description.clear()
            self.parent.dlg2.dialog2_description.appendPlainText(self.mapStore.dlg2_description)
            self.parent.dlg2.dialog2_fulldescription.clear()
            if hasattr(self.mapStore, 'dlg2_fulldescription') and self.mapStore.dlg2_fulldescription != None:
                self.parent.dlg2.dialog2_fulldescription.appendPlainText(self.mapStore.dlg2_fulldescription)
            self.parent.dlg2.dialog2_attribution.clear()
            if hasattr(self.mapStore, 'dlg2_attribution') and self.mapStore.dlg2_attribution != None:
                self.parent.dlg2.dialog2_attribution.appendPlainText(self.mapStore.dlg2_attribution)
            if hasattr(self.mapStore, 'dlg2_output') and self.mapStore.dlg2_output != None:
                outputPath = self.mapStore.dlg2_output
            else:
                outputPath = QgsProject.instance().readPath("./")
            self.parent.dlg2.dialog2_output.setFilePath(outputPath)

            # remove empty row and fill logos table
            if len(self.mapStore.dlg2_logos) > 0:
                self.parent.dlg2.dialog2_logos.setCurrentCell(0,0)
                self.parent.dlg2.removeRowLogo()
            for logo in self.mapStore.dlg2_logos:
                logo = dotdict(logo)
                self.parent.dlg2.addRowLogo(logo.src, logo.name, logo.url)

            # remove empty row and fill attachments table
            if len(self.mapStore.dlg2_attachments) > 0:
                self.parent.dlg2.dialog2_attachments.setCurrentCell(0,0)
                self.parent.dlg2.removeRowAttachment()
            for attachment in self.mapStore.dlg2_attachments:
                attachment = dotdict(attachment)
                self.parent.dlg2.addRowAttachment(attachment.src, attachment.name)

        elif dlg == 3:
            self.parent.dlg3.dlg3Publish_project.setFilePath(self.mapStore.dlg3_project)
            self.parent.dlg3.dlg3Publish_protocol.setCurrentText(self.mapStore.dlg3_protocol)
            self.parent.dlg3.dlg3Publish_host.setText(self.mapStore.dlg3_host)
            self.parent.dlg3.dlg3Publish_path.setText(self.mapStore.dlg3_path)
            self.parent.dlg3.dlg3Publish_user.setText(self.mapStore.dlg3_user)


    def loadViews(self):
        """Load saved Map Themes as views."""

        # remove all
        if self.parent.dlg1.dialog1_views.rowCount() > 0:
            for index in range(self.parent.dlg1.dialog1_views.rowCount()):
                self.parent.dlg1.dialog1_views.setCurrentCell(0,0)
                self.parent.dlg1.removeRowView(self.parent.dlg1.getSelectedViewRow())

        # get all Map Theme names
        viewNames = []
        for name in QgsProject.instance().mapThemeCollection().mapThemes():
            viewNames.append(name)

        # add all saved views, but only if it still does exist as Map Theme
        index = 0
        for view in self.mapStore.dlg1_views:
            view = dotdict(view)
            if view.id in viewNames:
                self.parent.dlg1.addRowView(view.id, view.name, view.description, view.layers, view.zoommin, view.zoommax)
                # change iterator only if not deleting elements
                index+=1
            else:
                # view name doesn't exist any more, delete
                if len(self.parent.dlg1.dialog1_views.layers) == len(self.mapStore.dlg1_views):
                    del self.parent.dlg1.dialog1_views.layers[index]
                if len(self.parent.dlg1.dialog1_views.btns) == len(self.mapStore.dlg1_views):
                    del self.parent.dlg1.dialog1_views.btns[index]
                del self.mapStore.dlg1_views[index]

        # add empty row if no view is defined
        #if self.parent.dlg1.dialog1_views.rowCount() == 0:
        #    self.parent.dlg1.addRowView()


    def writeSettingsToProject(self, dlg, messageBar):
        """Write all settings to QGIS project."""

        if dlg < 1 or dlg > 3:
            return False

        self.mapStore.plugin_version = self.parent.pluginVersion

        if dlg == 1:
            # backgrounds = []
            # for item in self.parent.dlg1.dialog1_backgrounds.selectedItems():
            #     backgrounds.append({
            #         "name": item.text(),
            #         "source": item.data(Qt.UserRole)
            #     })

            attachments = []
            for i in range(self.parent.dlg1.dialog1_attachments.rowCount()):
                filePath = self.parent.dlg1.dialog1_attachments.cellWidget(i, 0).filePath()
                name = self.parent.dlg1.dialog1_attachments.cellWidget(i, 1).text()
                if (filePath and name):
                    attachments.append({
                        "src": filePath,
                        "name": name
                    })

            views = []
            for i in range(self.parent.dlg1.dialog1_views.rowCount()):
                views.append({
                    "id": self.parent.dlg1.dialog1_views.cellWidget(i, 0).currentText(),
                    "name": self.parent.dlg1.dialog1_views.cellWidget(i, 1).text(),
                    "description": self.parent.dlg1.dialog1_views.cellWidget(i, 2).toPlainText(),
                    "layers": self.parent.dlg1.dialog1_views.layers[i],
                    "zoommin": self.parent.dlg1.dialog1_views.cellWidget(i, 4).value(),
                    "zoommax": self.parent.dlg1.dialog1_views.cellWidget(i, 5).value(),
                })

            self.mapStore.dlg1_name = self.parent.dlg1.dialog1_name.text()
            self.mapStore.dlg1_folder = self.parent.dlg1.dialog1_folder.filePath()
            self.mapStore.dlg1_description = self.parent.dlg1.dialog1_description.toPlainText()
            # self.mapStore.dlg1_backgrounds = backgrounds
            self.mapStore.dlg1_attachments = attachments
            self.mapStore.dlg1_extent = self.parent.dlg1.dialog1_extent.text()
            self.mapStore.dlg1_views = views
            self.mapStore.dlg1_multiple = self.parent.dlg1.dialog1_multiple_mbtiles.checkState()


        elif dlg == 2:
            logos = []
            for i in range(self.parent.dlg2.dialog2_logos.rowCount()):
                logos.append({
                    "src": self.parent.dlg2.dialog2_logos.cellWidget(i, 0).filePath(),
                    "name": self.parent.dlg2.dialog2_logos.cellWidget(i, 1).text(),
                    "href": self.parent.dlg2.dialog2_logos.cellWidget(i, 2).text()
                })

            attachments = []
            for i in range(self.parent.dlg2.dialog2_attachments.rowCount()):
                attachments.append({
                    "src": self.parent.dlg2.dialog2_attachments.cellWidget(i, 0).filePath(),
                    "name": self.parent.dlg2.dialog2_attachments.cellWidget(i, 1).text()
                })

            self.mapStore.dlg2_folder = self.parent.dlg2.dialog2_folder.filePath()
            self.mapStore.dlg2_mapsets = []
            self.mapStore.dlg2_title = self.parent.dlg2.dialog2_title.text()
            self.mapStore.dlg2_subtitle = self.parent.dlg2.dialog2_subtitle.text()
            self.mapStore.dlg2_description = self.parent.dlg2.dialog2_description.toPlainText()
            self.mapStore.dlg2_fulldescription = self.parent.dlg2.dialog2_fulldescription.toPlainText()
            self.mapStore.dlg2_attribution = self.parent.dlg2.dialog2_attribution.toPlainText()
            self.mapStore.dlg2_output = self.parent.dlg2.dialog2_output.filePath()
            self.mapStore.dlg2_logos = logos
            self.mapStore.dlg2_attachments = attachments

        elif dlg == 3:
            self.mapStore.dlg3_project = self.parent.dlg3.dlg3Publish_project.filePath()
            self.mapStore.dlg3_protocol = self.parent.dlg3.dlg3Publish_protocol.currentText()
            self.mapStore.dlg3_host = self.parent.dlg3.dlg3Publish_host.text()
            self.mapStore.dlg3_path = self.parent.dlg3.dlg3Publish_path.text()
            self.mapStore.dlg3_user = self.parent.dlg3.dlg3Publish_user.text()

        QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), 'project_qwv', str(self.toDict()))

        messageBar.pushMessage(Utils.tr("Success"), Utils.tr("Settings written to QGIS project. In order to have them available in the future, you have to save your QGIS project."), level=Qgis.Success, duration=3)


    def toDict(self):
        """Return mapStore class as dict."""

        return {
            "plugin_version": self.mapStore.plugin_version,

            "dlg1_name": self.mapStore.dlg1_name,
            "dlg1_folder": self.mapStore.dlg1_folder,
            "dlg1_description": self.mapStore.dlg1_description,
            # "dlg1_backgrounds": self.mapStore.dlg1_backgrounds,
            "dlg1_attachments": self.mapStore.dlg1_attachments,
            "dlg1_extent": self.mapStore.dlg1_extent,
            "dlg1_views": self.mapStore.dlg1_views,
            "dlg1_multiple": self.mapStore.dlg1_multiple,

            "dlg2_folder": self.mapStore.dlg2_folder,
            "dlg2_mapsets": self.mapStore.dlg2_mapsets,
            "dlg2_title": self.mapStore.dlg2_title,
            "dlg2_subtitle": self.mapStore.dlg2_subtitle,
            "dlg2_description": self.mapStore.dlg2_description,
            "dlg2_fulldescription": self.mapStore.dlg2_fulldescription,
            "dlg2_attribution": self.mapStore.dlg2_attribution,
            "dlg2_output": self.mapStore.dlg2_output,
            "dlg2_logos": self.mapStore.dlg2_logos,
            "dlg2_attachments": self.mapStore.dlg2_attachments,

            "dlg3_project": self.mapStore.dlg3_project,
            "dlg3_protocol": self.mapStore.dlg3_protocol,
            "dlg3_host": self.mapStore.dlg3_host,
            "dlg3_path": self.mapStore.dlg3_path,
            "dlg3_user": self.mapStore.dlg3_user
        }


    def getBackgrounds(self):
        """Read backgrounds from layer tree."""

        backgrounds = []
        for node in QgsProject.instance().layerTreeRoot().children():
            if isinstance(node, QgsLayerTreeLayer):
                layer = node.layer()
                if str(layer.type()) == 'LayerType.Raster':
                    uri_components = QgsProviderRegistry.instance().decodeUri(layer.dataProvider().name(), layer.publicSource())

                    if "url" in uri_components:
                        attribution = ""
                        if layer.attribution():
                            attribution = "&copy; " + layer.attribution()

                        maxzoom = 19
                        if "zmax" in uri_components:
                            maxzoom = int(uri_components["zmax"])

                        backgrounds.append({
                            "name": node.name(),
                            "source": {
                                "attribution": attribution, 
                                "maxzoom": maxzoom, 
                                "tileSize": 256, 
                                "tiles": [uri_components["url"]], 
                                "type": "raster"
                            }
                        })
                elif str(layer.type()) == 'LayerType.VectorTile':
                    uri_components = QgsProviderRegistry.instance().decodeUri(layer.dataProvider().name(), layer.publicSource())

                    if "styleUrl" in uri_components:

                        backgrounds.append({
                            "name": node.name(),
                            "source": {
                                "style": uri_components["styleUrl"], 
                                "type": "vector"
                            }
                        })
        return backgrounds


    def writeMapsetJson(self, mapsetPath, mapsetName, _writeMaplibreStylesFile):
        """Write mapset as JSON file to be used by dialog 1."""

        attachments = []
        mapsetName = Utils.toAsciiAlnum(self.parent.dlg1.dialog1_name.text())
        for i in range(self.parent.dlg1.dialog1_attachments.rowCount()):
            filePath = os.path.join('attachments_' + mapsetName, os.path.basename(self.parent.dlg1.dialog1_attachments.cellWidget(i, 0).filePath()))
            name = self.parent.dlg1.dialog1_attachments.cellWidget(i, 1).text()
            
            if (filePath and name):
                attachments.append({
                    "src": filePath,
                    "name": name
                })

        # backgrounds = []
        # for item in self.parent.dlg1.dialog1_backgrounds.selectedItems():
        #     backgrounds.append({
        #         "name": item.text(),
        #         #"url": item.toolTip(),
        #         "source": item.data(Qt.UserRole)
        #     })
        backgrounds = self.getBackgrounds()

        multipleMbtiles = self.parent.dlg1.dialog1_multiple_mbtiles.checkState() == 2

        maps = []
        indexStyleId = 0
        for indexViews in range(self.parent.dlg1.dialog1_views.rowCount()):

            mapsetName4Url = mapsetName
            if multipleMbtiles:
                mapsetName4Url = mapsetName + "_" + Utils.toAsciiAlnum(self.parent.dlg1.dialog1_views.cellWidget(indexViews, 1).text())
            
            if self.parent.dlg1.dialog1_views.layers[indexViews]:

                layers = []
                layerStyles = []
                for layer in reversed(self.parent.dlg1.dialog1_views.layers[indexViews]):
                
                    # only first layer style
                    mapLayer = QgsProject.instance().mapLayer(layer["id"])
                    #styleJSONPath = layerStyleAsMapboxFolder(mapLayer, mapsetPath)[0]
                    styleJSONPath = layerStyleAsMapbox(mapLayer)[0]

                    # for now get only first layer as we have to adapt viewer
                    layer_id = "style_" + str(indexStyleId)

                    # all layer styles for adapted viewer
                    layersStyleJSON = json.loads(styleJSONPath)["layers"]
                    indexStyles = 0
                    for layerStyle in layersStyleJSON:

                        #print("layerStyle", layerStyle)

                        layersStyleJSON[indexStyles]["source"] = layer_id
                        layersStyleJSON[indexStyles]["source-layer"] = layer["id"]

                        # layers without rule end up having layerstyle with it=":0", which ends up without painting as they are duplicated -> adding source-layer
                        layersStyleJSON[indexStyles]["id"] = layer["id"] + ":" + layersStyleJSON[indexStyles]["id"]
                        
                        # temporary fix for "text-font" and "text-size"
                        if "layout" in layerStyle:
                            if "text-font" in layerStyle["layout"]:
                                layersStyleJSON[indexStyles]["layout"]["text-font"] = ["Open Sans Semibold"]
                            if "text-size" in layerStyle["layout"]:
                                layersStyleJSON[indexStyles]["layout"]["text-size"] = int(float(layersStyleJSON[indexStyles]["layout"]["text-size"]))
                            layersStyleJSON[indexStyles]["layout"]["visibility"] = "visible";
                        else:
                            layersStyleJSON[indexStyles]["layout"] = {"visibility": "visible"};

                        indexStyles += 1
                        
                    indexStyleId += 1

                    # get fields for popups from QGIS Attributes Form
                    fields = []
                    edit_form_config = mapLayer.editFormConfig()
                    root_container = edit_form_config.invisibleRootContainer()
                    for field_editor in root_container.findElements(QgsAttributeEditorElement.AeTypeField):
                        i = field_editor.idx()
                        if i >= 0 and mapLayer.editorWidgetSetup(i).type() != 'Hidden' and mapLayer.attributeAlias(i) != "":
                            #print(i, field_editor.name(), mapLayer.fields()[i].name(), mapLayer.attributeDisplayName(i), mapLayer.attributeAlias(i))

                            fields.append({
                                "name": mapLayer.attributeAlias(i),
                                "field": mapLayer.fields()[i].name()
                            })

                    minzoom = max(round(self.parent.dlg1.getLayerMinZoom(mapLayer, indexViews)), self.parent.dlg1.getViewZoomMin())
                    maxzoom = min(round(self.parent.dlg1.getLayerMaxZoom(mapLayer, indexViews)), self.parent.dlg1.getViewZoomMax())

                    layers.append({
                        "id": layer_id,
                        "layer_id": layer_id,
                        "name": layer["name"],
                        "route": None,
                        "desc": layer["description"],
                        "z_index": 4,
                        "type": "maplibre",
                        "maplibreLayers": layersStyleJSON,
                        "popup": {
                            "popup_status": len(fields)>0, #layer["switchable"]
                            "fields": fields
                        },
                        "legend": {
                            "legend_status": layer["legend"]
                        },
                        "opacity": 1,
                        "minzoom": minzoom,
                        "maxzoom": maxzoom,
                        "source": {
                            "type": "vector",
                            #"tiles": ["http://127.0.0.1:8080/" + Utils.toAsciiAlnum(self.parent.dlg1.dialog1_name.text()) + "/{z}/{x}/{y}.pbf"],
                            "tiles": ["{baseurl}data/tileserver/tileserver.php?/index.json?/" + mapsetName4Url + "/{z}/{x}/{y}.pbf"],
                            "minzoom": minzoom,
                            "maxzoom": maxzoom,
                        },
                        "wkbType": QgsWkbTypes.displayString(mapLayer.wkbType()),
                        "geometryType": QgsWkbTypes.geometryDisplayString(mapLayer.geometryType())
                    })
                    layerStyles += layersStyleJSON

                    #print("layer", QgsWkbTypes.displayString(mapLayer.wkbType()), mapLayer.wkbType(), QgsWkbTypes.geometryDisplayString(mapLayer.geometryType()), "provider", mapLayer.dataProvider().wkbType())

            maps.append({
                "name": self.parent.dlg1.dialog1_views.cellWidget(indexViews, 1).text(),
                "route": ["clusters"],
                "desc": self.parent.dlg1.dialog1_views.cellWidget(indexViews, 2).toPlainText(),
                "properties": {},
                "minzoom": self.parent.dlg1.dialog1_views.cellWidget(indexViews, 4).value(),
                "maxzoom": self.parent.dlg1.dialog1_views.cellWidget(indexViews, 5).value(),
                "layers": layers
            })

        # transform map center to lat long coordinates
        mapsetCenter = Utils.getExtentRectangleProj(self.parent.dlg1.dialog1_extent.text(), 4326).center()
        mapsetCenterCoords = QgsGeometry.fromPointXY(mapsetCenter).asPoint()

        mapset = {
            "chapter": self.parent.dlg1.dialog1_name.text(),
            "type": "atlas",
            "name": self.parent.dlg1.dialog1_name.text(),
            "desc": self.parent.dlg1.dialog1_description.toPlainText(),
            "content": "centralitats",
            "style_icon": "moon",
            "style_color": None,
            "attached_docs": attachments,
            "view.center": [mapsetCenterCoords.x(), mapsetCenterCoords.y()],
            "view.zoom": 15,
            "view.bbox": Utils.getExtentLngLatBoundsProj(self.parent.dlg1.dialog1_extent.text(), 4326),
            "template": "",
            "atlas_background": backgrounds, 
            "maps": maps
        }

        # use background color from QGIS if not white
        bgColor = Utils.toHexColor(self.parent.iface.mapCanvas().canvasColor())
        if bgColor != '#ffffff':
            mapset["bgcolor"] = bgColor

        # write mapset conf
        with open(os.path.join(mapsetPath, mapsetName + '.json'), 'w') as f:
            json.dump(mapset, f)

        # write layer styles to test directly in QGIS
        if _writeMaplibreStylesFile:
            layerStyles = {"layers": layerStyles}
            with open(os.path.join(mapsetPath, mapsetName + '_style.json'), 'w') as f:
                json.dump(layerStyles, f)


    def writeViewerJson(self, mapsets_list):
        """Write conf.json to be used by maplibre viewer."""

        logos = []
        for i in range(self.parent.dlg2.dialog2_logos.rowCount()):
            filePath = os.path.basename(self.parent.dlg2.dialog2_logos.cellWidget(i, 0).filePath())
            name = self.parent.dlg2.dialog2_logos.cellWidget(i, 1).text()

            if (filePath and name):
                logos.append({
                    "src": filePath,
                    "name": name,
                    "href": self.parent.dlg2.dialog2_logos.cellWidget(i, 2).text()
                })

        attachments = []
        for i in range(self.parent.dlg2.dialog2_attachments.rowCount()):
            filePath = os.path.basename(self.parent.dlg2.dialog2_attachments.cellWidget(i, 0).filePath())
            name = self.parent.dlg2.dialog2_attachments.cellWidget(i, 1).text()

            if (filePath and name):
                attachments.append({
                    "src": filePath,
                    "name": name
                })

        viewer = {
            "project": {
                "name": self.parent.dlg2.dialog2_title.text(),
                "long_name": "",
                "short_name": self.parent.dlg2.dialog2_subtitle.text(),
                "description": self.parent.dlg2.dialog2_description.toPlainText(),
                "fulldescription": self.parent.dlg2.dialog2_fulldescription.toPlainText(),
                "attribution": self.parent.dlg2.dialog2_attribution.toPlainText(),
                "logos": logos,
                "attached_docs": attachments
            },
            "chapters": []
        }

        # add chapter for every mapset selected
        for mapsetCheckbox in mapsets_list:
            if mapsetCheckbox.isChecked():
                with open(mapsetCheckbox.text(), 'r') as f:
                    mapsetJSON = json.load(f)
                    viewer["chapters"].append(mapsetJSON)

        # write json conf file
        with open(os.path.join(self.parent.dlg2.dialog2_folder.filePath(), 'conf.json'), 'w') as f:
            json.dump(viewer, f)


class mapStore():
    def __init__(self, data=None):
        """Constructor."""

        self.dataLoaded = False

        if data:
            self.dataLoaded = True

            self.plugin_version = data.plugin_version

            self.dlg1_name = data.dlg1_name
            self.dlg1_folder = data.dlg1_folder
            self.dlg1_description = data.dlg1_description
            #self.dlg1_backgrounds = data.dlg1_backgrounds
            self.dlg1_attachments = data.dlg1_attachments
            self.dlg1_extent = data.dlg1_extent 
            self.dlg1_views = data.dlg1_views
            self.dlg1_multiple = data.dlg1_multiple

            self.dlg2_folder = data.dlg2_folder
            self.dlg2_mapsets = data.dlg2_mapsets
            self.dlg2_title = data.dlg2_title
            self.dlg2_subtitle = data.dlg2_subtitle
            self.dlg2_description = data.dlg2_description
            self.dlg2_fulldescription = data.dlg2_fulldescription
            self.dlg2_attribution = data.dlg2_attribution
            self.dlg2_output = data.dlg2_output
            self.dlg2_logos = data.dlg2_logos
            self.dlg2_attachments = data.dlg2_attachments
            
            self.dlg3_project = data.dlg3_project
            self.dlg3_url = data.dlg3_url
            self.dlg3_protocol = data.dlg3_protocol
            self.dlg3_host = data.dlg3_host
            self.dlg3_path = data.dlg3_path
            self.dlg3_user = data.dlg3_user

        else:
            #self.plugin_version = self.parent.pluginVersion
            self.plugin_version = "0.4"

            self.dlg1_name = None
            self.dlg1_folder = None
            self.dlg1_description = None
            #self.dlg1_backgrounds = []
            self.dlg1_attachments = []
            self.dlg1_extent = None
            self.dlg1_views = []
            self.dlg1_multiple = None

            self.dlg2_folder = None
            self.dlg2_mapsets = []
            self.dlg2_title = None
            self.dlg2_subtitle = None
            self.dlg2_description = None
            self.dlg2_fulldescription = None
            self.dlg2_attribution = None
            self.dlg2_output = None
            self.dlg2_logos = []
            self.dlg2_attachments = []
            
            self.dlg3_project = None
            self.dlg3_protocol = None
            self.dlg3_host = None
            self.dlg3_path = None
            self.dlg3_user = None


class dotdict(dict):
    """dot.notation access to dictionary attributes"""
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__
