"""
/***************************************************************************
 layout.py

 Layout class allows to manage layouts with QGIS


        begin                : 2023-08-20
        git sha              : $Format:%H$
        copyright            : (C) 2023 by Jean-Marie Arsac
        email                : jmarsac@azimut.fr
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

import os
import shutil

from qgis.core import (
    QgsApplication,
    QgsExpressionContextUtils,
    QgsLayoutExporter,
    QgsPointXY,
    QgsProject,
)
from qgis.PyQt.QtCore import QDir, QFile, QFileInfo
from qgis.utils import iface

from folios_wizard.gui.folio_dialog_composer import FolioDialogComposer
from folios_wizard.toolbelt.preferences import PlgOptionsManager
from folios_wizard.utils import Utils


class Layout:
    LAYOUT_PREFIX = "FOLIO"
    TA_LAYOUT_PREFIX = "TA"

    def __init__(self):
        """Constructor"""
        self._layout = None
        self._refMap = None
        self._settings = PlgOptionsManager.get_plg_settings()

    @classmethod
    def isLayoutName(cls, layout_name, check_extension: bool = False):
        if check_extension:
            filename, extension = os.path.splitext(layout_name)
            if not extension.lower() == ".qpt":
                return False

        if layout_name.upper().startswith(cls.LAYOUT_PREFIX):
            return True
        else:
            return False

    @classmethod
    def isTaLayoutName(cls, layout_name, check_extension: bool = False):
        if check_extension:
            filename, extension = os.path.splitext(layout_name)
            if not extension.lower() == ".qpt":
                return False

        if layout_name.upper().startswith(cls.TA_LAYOUT_PREFIX):
            return True
        else:
            return False

    @classmethod
    def layoutExists(cls, layout_name):
        manager = QgsProject.instance().layoutManager()
        layouts_list = manager.printLayouts()

        for layout in layouts_list:
            # to be used by Folios Wizard plugin, layout name must start with 'dict' (case insensitive)
            if layout.name().lower() == layout_name.lower():
                return True

        return False

    @classmethod
    def init_templates(cls, reset_all: bool = False):
        # print("init_templates")
        settings = PlgOptionsManager.get_plg_settings()
        templates_dir = settings.maps_templates_folder
        os.makedirs(templates_dir, exist_ok=True)

        templates = [
            f.name
            for f in os.scandir(templates_dir)
            if f.is_file() and (cls.isLayoutName(f.name) or cls.isTaLayoutName(f.name))
        ]
        if len(templates) == 0 or reset_all == True:
            to_copy = True
        else:
            to_copy = False

        if to_copy:
            templates = [
                f.name
                for f in os.scandir(templates_dir)
                if f.is_file()
                and (cls.isLayoutName(f.name) or cls.isTaLayoutName(f.name))
            ]
            for t in templates:
                os.remove(os.path.join(templates_dir, t))

            source_path = Utils.resolve("layouts")
            templates = [
                f.name
                for f in os.scandir(source_path)
                if f.is_file()
                and (cls.isLayoutName(f.name) or cls.isTaLayoutName(f.name))
            ]
            for t in templates:
                shutil.copy2(os.path.join(source_path, t), templates_dir)

    def loadTemplates(self, cbox=None):
        manager = QgsProject.instance().layoutManager()
        layouts_list = manager.printLayouts()
        if cbox is not None:
            cbox.clear()
            set_index = False

        templates_dir = self._settings.maps_templates_folder
        if templates_dir == "":
            profile_dir = QgsApplication.qgisSettingsDirPath()
            templates_dir = os.path.join(profile_dir, "composer_templates")
        if QDir(templates_dir).exists() is False:
            profile_dir = QgsApplication.qgisSettingsDirPath()
            templates_dir = os.path.join(profile_dir, "composer_templates")
            os.makedirs(templates_dir, exist_ok=True)

        self._settings.maps_templates_folder = templates_dir
        # Search the templates folder and add files to templates list and sort it
        templates = [f.name for f in os.scandir(templates_dir) if f.is_file()]
        templates.reverse()

        # Get the project file name and if it exist the project title. Use for Title suggestion
        project_file_name = QFileInfo(QgsProject.instance().fileName()).baseName()
        project_title = QgsProject.instance().title()
        if project_title == "":
            project_title = project_file_name

        # Add all the templates from the list to the listWidget (only add files with *.qpt extension and prefixed
        # with 'dict' (case unsensitive))
        for template in templates:
            if self.isLayoutName(template, True):
                if cbox is not None:
                    filename, extension = os.path.splitext(template)
                    cbox.addItem(filename)
                    set_index = True

    # Python function that do the main work of setting up the print layout
    # The code in the function can work stand alone if you use the commented variables and edit their values
    def loadLayout(self, template_source, layout_name, title_text):
        """Generate the layout"""
        from qgis.core import QgsPrintLayout, QgsProject, QgsReadWriteContext
        from qgis.PyQt.QtXml import QDomDocument

        # template_source = '/home/user/Document/Template.qpt'
        # layout_name = 'NewLayout'
        # title_text = 'New Title'
        # Create objects lm = layout manager, l = print layout
        lm = QgsProject.instance().layoutManager()
        l = QgsPrintLayout(QgsProject.instance())
        l.initializeDefaults()

        # Load template file and load it into the layout (l)
        template_file = open(template_source, "r+", encoding="utf-8")
        template_content = template_file.read()
        template_file.close()
        document = QDomDocument()
        document.setContent(template_content)
        context = QgsReadWriteContext()
        l.loadFromTemplate(document, context)

        # Give the layout a name (must be unique)
        l.setName(layout_name)

        """
        # Get current canvas extent and apply that to all maps (items) in layout
        # Replace any text "{{title}}" in any layout label with the dialog Title text
        canvas = iface.mapCanvas()
        for item in l.items():
            if item.type() == 65639:  # Map
                item.zoomToExtent(canvas.extent())
            if item.type() == 65641:  # Label
                item.setText(item.text().replace('{{title}}', title_text))
        """
        # Add layout to layout manager
        l.refresh()
        lm.addLayout(l)

    def loadLayouts(self, cbox=None):
        manager = QgsProject.instance().layoutManager()
        layouts_list = manager.printLayouts()
        if cbox is not None:
            cbox.clear()
            set_index = False

        for layout in layouts_list:
            if self.isLayoutName(layout.name()):
                if cbox is not None:
                    cbox.addItem(layout.name())
                    set_index = True
                page = layout.pageCollection().pages()[0]
                """
                #map_item = layout.referenceMap()
                #print("=========================")
                #print(layout.name())
                #print("displayName",map_item.displayName())
                #print("map_item.sizeWithUnits()", map_item.sizeWithUnits().width(), map_item.sizeWithUnits().height())
                #print("map_item.fixedSize()", map_item.fixedSize())
                #print("displayName",map_item.displayName())
                #print("atlasDriven",map_item.atlasDriven())
                #print("atlasMargin",map_item.atlasMargin())
                #print("atlasScalingMode",map_item.atlasScalingMode())
                #print("extent",map_item.extent())
                #print("scale",map_item.scale())
                #print("mapRotation",map_item.mapRotation())
                #print("presetCrs",map_item.presetCrs())
                #print(".........................")
                #print(map_item)
                #print("displayName",map_item.displayName())
                #print("atlasDriven",map_item.atlasDriven())
                #print("atlasMargin",map_item.atlasMargin())
                #print("atlasScalingMode",map_item.atlasScalingMode())
                #print("extent",map_item.extent())
                #print("scale",map_item.scale())
                #print("mapRotation",map_item.mapRotation())
                #print("presetCrs",map_item.presetCrs())
                #print("delta",dx,dy)
                #print("delta",dx / ref_map.scale(), dy / ref_map.scale())
                #print("ref_map.rotation", ref_map.rotation())
                #print("ref_map.scale", ref_map.scale())
                """
                if self._layout is None:
                    self._layout = layout
                    self._refMap = layout.referenceMap()

        if cbox is not None and set_index == True:
            cbox.setCurrentIndex(0)

    def removeLayoutByName(self, layout_name):
        manager = QgsProject.instance().layoutManager()
        for layout in manager.printLayouts():
            if layout.name() == layout_name:
                if self._layout is not None and self._layout.name == layout_name:
                    self._layout = None
                manager.removeLayout(layout)
                break

        if not self._layout:
            for layout in manager.printLayouts():
                self._layout = layout
                break

    def removeProjectLayouts(self, remove_ta: bool = False):
        manager = QgsProject.instance().layoutManager()
        self._layout = None
        for layout in manager.printLayouts():
            if self.isLayoutName(layout.name()):
                manager.removeLayout(layout)
            elif remove_ta and self.isTaLayoutName(layout.name()):
                manager.removeLayout(layout)

    def setCurrentLayoutByName(self, layout_name):
        # print("setCurrentLayoutByName()", layout_name)
        manager = QgsProject.instance().layoutManager()
        for layout in manager.printLayouts():
            if layout.name() == layout_name:
                self._layout = layout
                self._refMap = layout.referenceMap()
                break

    def setPrintScale(self, print_scale):
        if self._layout:
            QgsExpressionContextUtils.setLayoutVariable(
                self._layout, "folio_print_scale", print_scale
            )
            self._refMap.setScale(print_scale)

    def currentLayout(self):
        # print("currentLayout()", self._layout.name() if self._layout else 'None')
        return self._layout

    def referenceMap(self):
        return self._refMap

    def folioPrintSize(self):
        h = self._refMap.sizeWithUnits().height()
        w = self._refMap.sizeWithUnits().width()
        k = 0.001
        return QgsPointXY(w * k, h * k)

    def geometriePDF(self, titre, taillePlan):
        # Display layout list
        # TODO
        dlg_config_composers = FolioDialogComposer(taillePlan)
        dlg_config_composers.show()
        result = dlg_config_composers.exec_()

        idx_plan = []

        if result:
            idx_plan = dlg_config_composers.listComposers.selectedItems()
            # Sortie du plan en PDF
            manager = QgsProject.instance().layoutManager()

        out = []
        if len(idx_plan) > 0:
            for i, idx in enumerate(idx_plan):
                id_plan = dlg_config_composers.listComposers.row(idx_plan[i])
                layout_name = dlg_config_composers.layout_listArray[id_plan]
                layout = manager.layoutByName(layout_name)

                # Retrieve the layout's map Item
                mapItem = layout.referenceMap()
                mapItem.zoomToExtent(iface.mapCanvas().extent())
                # Only mean to edit an existing item found so far is getting said item's ID
                # there's the layoutItems() method to get the list of items from a layout
                # but as of now is exclusive to C++ plugins

                # Output
                out_dir = self._settings.maps_output_folder
                if QDir(out_dir).exists() is False or out_dir is None:
                    out_dir = str(QDir.homePath())

                pdf = os.path.join(
                    out_dir,
                    self._settings.maps_prefix
                    + titre
                    + self._settings.maps_suffix
                    + "_"
                    + str(i)
                    + ".pdf",
                )

                if QFile.exists(pdf):
                    pdf = os.path.join(
                        out_dir,
                        self._settings.maps_prefix
                        + titre
                        + self._settings.maps_suffix
                        + "_"
                        + str(i)
                        + ".pdf",
                    )

                exported_layout = QgsLayoutExporter(layout)
                exported_layout.exportToPdf(pdf, QgsLayoutExporter.PdfExportSettings())
                out.append(pdf)

        return out
