# -*- coding: utf-8 -*-
"""
/***************************************************************************
 PhotovoltaicsLPDialog
                                 A QGIS plugin
 Wyznaczanie lokalizacji pod farmy fotowoltaiczne LP
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                             -------------------
        begin                : 2023-09-04
        git sha              : $Format:%H$
        copyright            : (C) 2025 by EnviroSolutions Sp. z o. o.
        email                : office@envirosolutions.pl
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 tempfile
import zipfile
from pathlib import Path
from qgis.PyQt import uic
from qgis.PyQt import QtWidgets
from qgis.PyQt.QtWidgets import QFileDialog, QApplication
from qgis.PyQt.QtGui import QColor, QFont
from qgis.utils import iface
from qgis.core import (
    Qgis, QgsRasterLayer, QgsProject, QgsVectorLayer,
    QgsMapSettings, QgsRectangle,
    QgsApplication, QgsWkbTypes, QgsCoordinateReferenceSystem,
    QgsVectorFileWriter, QgsPrintLayout, QgsLayoutItemMap, QgsLayoutPoint,
    QgsUnitTypes, QgsLayoutSize, QgsLayoutItemPicture, QgsLayoutItemLabel,
    QgsLayoutItemLegend, QgsLayerTree, QgsLayoutItemScaleBar,
    QgsScaleBarSettings, QgsLayoutExporter
) 

from .modules.dane_bdot_task import PobierzBdotTask
from .modules.analiza_task import AnalizaTask
from .modules.generuj_wydruk import WydrukGenerator
from .modules.zapisz_xlsx import ZapiszXLSX
from .modules.generuj_raport import GenerujRaport
from .utils import FileUtils, MessageUtils, LayerUtils

from .constants import (
    WMTS_URL_TEMPLATE, MAPA_BAZOWA_LAYERS, CRS_EPSG, MAPA_BAZOWA_URL,
    MAPA_BAZOWA_LAYER_NAME, INPUT_LAYERS, TEMP_DIR_PREFIX,
    LAYOUT_CONFIG, NAME_LAYER_OBSZARY,
    NAME_LAYER_LINIE, NAME_LAYER_DROGI,
    ENCODINGS, PROVIDERS,
    DRIVER_SHAPEFILE, FILE_FILTERS,
    FILENAME_DEFAULT_LAYERS_DIR, FILENAME_LAYER_LINIE, FILENAME_LAYER_DROGI
)


# To wczytuje plik .ui, dzięki czemu PyQt uzupełni wtyczkę elementami z Qt Designera.
FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'photovoltaics_LP_dialog_base.ui'))


class PhotovoltaicsLPDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, parent=None):
        """Konstruktor."""
        super(PhotovoltaicsLPDialog, self).__init__(parent)
        self.project = QgsProject.instance()
        # Set up the user interface from Designer through FORM_CLASS.
        # After self.setupUi() you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)

        self.iface = iface

        self.wydzielenia = None
        self.wydzielenia_opisy = None
        self.drogi_lesne = None
        self.powiaty = None
        self.obszary = None
        self.drogi = None
        self.linie= None
        self.linie_bdot10k = None
        self.oddzialy = None
        self.nadlesnictwo = None
        self.mapa_bazowa = None
        self.drogi_bdot10k = None
        self.drogi_list = []
        self.linie_list = []
        self.save_layer_path = ""

        # Tworzenie katalogu tymczasowego
        self.work_dir = tempfile.TemporaryDirectory(prefix=TEMP_DIR_PREFIX)
        self.temp_path = self.work_dir.name

        self.pobierzWarstwyPochodneBtn.clicked.connect(self.pobierzWarstwyPochodne)
        self.wczytajBdot10kBtn.clicked.connect(self.wczytajDaneBdot10k)
        self.analizaBtn.clicked.connect(self.analizaData)
        self.raportBtn.clicked.connect(self.generujRaport)
        self.wydrukBtn.clicked.connect(self.generujWydruk)
        self.resetujBtn.clicked.connect(self.resetuj)
        self.zamknijBtn.clicked.connect(self.zamknij)
        self.zapisBtn.clicked.connect(self.zapiszWarstwy)
        self.helpBtn.clicked.connect(self.help)


    def dodajMapeBazowa(self):
        """
        dodaje mapę bazową
        """
   
        wmts_url = WMTS_URL_TEMPLATE.format(
            layers=MAPA_BAZOWA_LAYERS,
            crs=f"EPSG:{CRS_EPSG}",
            url=MAPA_BAZOWA_URL
        )
        
        self.mapa_bazowa = QgsRasterLayer(wmts_url, MAPA_BAZOWA_LAYER_NAME, PROVIDERS['WMS'])
        if self.mapa_bazowa.isValid():
            root = self.project.layerTreeRoot()
            self.project.addMapLayer(self.mapa_bazowa, False)
            root.insertLayer(len(root.children()), self.mapa_bazowa)
            self.mapa_bazowa.renderer().setOpacity(0.2)
            self.mapa_bazowa.triggerRepaint()

        else:
            MessageUtils.pushMessageBoxCritical(self, "Nie udało się wczytać mapy bazowej", "Sprawdź połączenie z internetem!")

    def pobierzWarstwyPochodne(self):
        """Ładuje warstwy pochodne z folderu .zip do projektu).
        """
        self.selected_data = QFileDialog.getOpenFileName(
            None, "Wybierz archiwum", "", FILE_FILTERS['ZIP'])

        if self.selected_data[0]:
            self.pobierzWarstwyPochodneBtn.setEnabled(False)
            zf = zipfile.ZipFile(self.selected_data[0])
            zf.extractall(self.temp_path)
            powiaty_path = os.path.join(self.temp_path, INPUT_LAYERS['powiaty']['filename'])
            wydzielenia_path = os.path.join(self.temp_path, INPUT_LAYERS['wydzielenia']['filename'])
            drogi_lesne_path = os.path.join(self.temp_path, INPUT_LAYERS['drogi_lesne']['filename'])
            oddzialy_path = os.path.join(self.temp_path, INPUT_LAYERS['oddzialy']['filename'])
            wydzielenia_opisy_path = os.path.join(self.temp_path, INPUT_LAYERS['wydzielenia_opisy']['filename'])
            nadlesnictwo_path = os.path.join(self.temp_path, INPUT_LAYERS['nadlesnictwo']['filename'])

            self.oddzialy= QgsVectorLayer(oddzialy_path, INPUT_LAYERS['oddzialy']['layer_name'], PROVIDERS['OGR'])
            self.powiaty = QgsVectorLayer(powiaty_path, INPUT_LAYERS['powiaty']['layer_name'], PROVIDERS['OGR'])
            self.wydzielenia = QgsVectorLayer(wydzielenia_path, INPUT_LAYERS['wydzielenia']['layer_name'], PROVIDERS['OGR'])
            self.drogi_lesne = QgsVectorLayer(drogi_lesne_path, INPUT_LAYERS['drogi_lesne']['layer_name'], PROVIDERS['OGR'])
            self.wydzielenia_opisy = QgsVectorLayer( wydzielenia_opisy_path, INPUT_LAYERS['wydzielenia_opisy']['layer_name'], PROVIDERS['OGR'])
            self.nadlesnictwo = QgsVectorLayer( nadlesnictwo_path, INPUT_LAYERS['nadlesnictwo']['layer_name'], PROVIDERS['OGR'])
            self.nadlesnictwo.dataProvider().setEncoding(ENCODINGS['WIN1250'])
           
            
            if self.drogi_lesne.isValid() and self.wydzielenia.isValid():
                    # dodanie stylu do warstwy z drogami leśnymi
                    LayerUtils.applyLayerStyle(self.drogi_lesne, INPUT_LAYERS['drogi_lesne']['layer_name'])

                    # dodanie stylu do warstwy z wydzieleniami leśnymi
                    LayerUtils.applyLayerStyle(self.wydzielenia, INPUT_LAYERS['wydzielenia']['layer_name'])


                    self.project.addMapLayers( [self.drogi_lesne, self.wydzielenia])
                    self.dodajMapeBazowa() #dodawanie warstwy bazowej
                    ms = QgsMapSettings()
                    ms.setLayers([self.drogi_lesne, self.wydzielenia])  
                    rect = QgsRectangle(ms.fullExtent()) 
                    iface.mapCanvas().setExtent(rect) # ustawaianie do zakresu warstwy wydzielenni warstwy dróg leśnych
                    iface.mapCanvas().refresh()
                    MessageUtils.pushMessage(self.iface, 'Załadowano warstwy pochodne')
                    self.pobierzWarstwyPochodneBtn.setEnabled(False)
                    self.wczytajBdot10kBtn.setEnabled(True)
                    self.resetujBtn.setEnabled(True)

                   
            else:
                MessageUtils.pushMessageBoxCritical(self, "Nie udało się wczytać warstw pochodnych", "Sprawdź poprawność danych!")
                self.resetuj()    
                    
        else:
               
            MessageUtils.pushMessageBoxCritical(self, "Nie wybrano danych", "Wybierz dane!")
            
            
    def wczytajDaneBdot10k(self):
            """
            Ściąga dane_bdot10k z internetu do projektu.
            """
            features = [object for object in self.powiaty.getFeatures()]
            
            if not features:
                MessageUtils.pushWarning(self.iface, "Brak wybranych powiatów!")
                return

            MessageUtils.pushMessage(self.iface, 'Pobieranie danych BDOT10k')
            
            self.wczytajBdot10kBtn.setEnabled(False)
            self.resetujBtn.setEnabled(False)
            
            self.bdot_task = PobierzBdotTask(
                description='Pobieranie danych BDOT',
                features=features, 
                iface=self.iface,
                temp_path=self.temp_path,
                wczytajBdot10kBtn=self.wczytajBdot10kBtn,
                analizaBtn=self.analizaBtn,
                resetujBtn=self.resetujBtn,
                parent=self
            )
            
            QgsApplication.taskManager().addTask(self.bdot_task)
            MessageUtils.pushLogInfo('Uruchomiono zadanie (Task przypisany do self.bdot_task)')
        
    def analizaData(self):
        """
        Wykonuje analizę na potrzeby farm fotowoltalicznych.
        """
        self.analizaBtn.setEnabled(False)
        self.resetujBtn.setEnabled(False)
        
        # Natychmiastowe wylaczenie przyciskow
        QApplication.processEvents()
        
        MessageUtils.pushMessage(self.iface, 'Analiza danych')
        self.analiza_task = AnalizaTask( 
                description='Analiza danych',
                wydzielenia_opisy = self.wydzielenia_opisy,
                wydzielenia = self.wydzielenia,
                oddzialy = self.oddzialy, 
                drogi_lesne = self.drogi_lesne,
                mapa_bazowa = self.mapa_bazowa,
                iface = self.iface,
                analizaBtn = self.analizaBtn,
                zapisBtn = self.zapisBtn,
                raportBtn=self.raportBtn,
                wydrukBtn=self.wydrukBtn,
                resetujBtn = self.resetujBtn,
                parent=self
            )
                
        
        QgsApplication.taskManager().addTask(self.analiza_task)

    def zapiszWarstwy(self):
        """
        Zapisuje warstwy wyznaczonych obszarów, najbliższych dróg i 
        najbiższych linii energetycznych do wybranej lokalizacji
        """
    
        self.save_layer_path = QFileDialog.getSaveFileName(
                None, "Wybierz lokalizację", FILENAME_DEFAULT_LAYERS_DIR, FILE_FILTERS['SHP'])
        if len(self.save_layer_path[0]) > 0:
               
            try:
                layers = LayerUtils.getResultLayers(self.project)
                wyznaczone_obszary = layers['obszary']
                linie = layers['linie']
                drogi = layers['drogi']

                if not wyznaczone_obszary or not linie or not drogi:
                    MessageUtils.pushMessageBoxCritical(self, "Brak warstw", "Najpierw przeprowadź analizę!")
                    return

                features_obszary = [feature for feature in wyznaczone_obszary.getFeatures()]
                fields_obszary = wyznaczone_obszary.fields()
                path=self.save_layer_path[0]
                self.tworzWarstwy(path, QgsWkbTypes.Polygon, fields_obszary, NAME_LAYER_OBSZARY, features_obszary)

                features_linie = [feature for feature in linie.getFeatures()]
                fields_linie = linie.fields()
                path_linie = os.path.join(os.path.dirname(path), FILENAME_LAYER_LINIE)
                self.tworzWarstwy(path_linie, QgsWkbTypes.LineString, fields_linie, NAME_LAYER_LINIE, features_linie)

                features_drogi = [feature for feature in drogi.getFeatures()]
                fields_drogi = drogi.fields()
                path_drogi = os.path.join(os.path.dirname(path), FILENAME_LAYER_DROGI)
                self.tworzWarstwy(path_drogi, QgsWkbTypes.LineString, fields_drogi, NAME_LAYER_DROGI, features_drogi)      

                MessageUtils.pushMessage(self.iface, "Zapisywanie warstw zakonczone sukcesem!")
                folder_name = os.path.dirname(path)
                FileUtils.openFile(folder_name)
            except:
                MessageUtils.pushMessageBoxCritical(self, "Spróbuj jeszcze raz", "Problem z zapisem warstw!")

    def tworzWarstwy(self,path, typ_geom, fields, name, features):
        """
        Tworzy warstwy wyznaczonych obszarów, najbliższych dróg i 
        najbiższych linii energetycznych.
        """

        crs = QgsCoordinateReferenceSystem(f"EPSG:{CRS_EPSG}")
        transform_context = self.project.transformContext()
        save_options = QgsVectorFileWriter.SaveVectorOptions()
        save_options.driverName = DRIVER_SHAPEFILE
        save_options.fileEncoding = ENCODINGS['UTF8']
        if Qgis.QGIS_VERSION_INT >= 31030:
            writer = QgsVectorFileWriter.create(
                path, fields, typ_geom, crs, transform_context, save_options)
        else:
            writer = QgsVectorFileWriter(
                self.save_layer_path[0], ENCODINGS['UTF8'], fields,  typ_geom, crs, DRIVER_SHAPEFILE)
        if writer.hasError() != QgsVectorFileWriter.NoError:
            MessageUtils.pushMessageBoxCritical(self, "Spróbuj jeszcze raz", "Problem z zapisem warstwy!")
        else:
            del writer
            layer = QgsVectorLayer(
                path, name, PROVIDERS['OGR'])
            prov = layer.dataProvider()
            prov.addFeatures(features)
        
    def generujRaport(self):
        """
        Generuje raport i zapisuje go jako plik excela.
        """

        layers = LayerUtils.getResultLayers(self.project)
        obszary = layers.get('obszary')
        linie = layers.get('linie')
        drogi = layers.get('drogi')

        if not obszary or not linie or not drogi:
            MessageUtils.pushMessageBoxCritical(self, "Brak warstw", "Najpierw przeprowadź analizę, aby wygenerować warstwy wynikowe!")
            return

        attr_nadl = LAYOUT_CONFIG['TITLE']['ATTR_NAME']
        nazwa_nadl = LayerUtils.getNameFromLayer(self.nadlesnictwo, attr_nadl, self.iface)

        try:
            nazwa_pliku = ZapiszXLSX().zapiszExcel()
        except Exception as e:
            MessageUtils.pushLogInfo(f"Błąd podczas wybierania ścieżki zapisu: {e}")
            MessageUtils.pushMessageBoxCritical(self, "Błąd", "Nie udało się określić ścieżki zapisu pliku.")
            return

        if not nazwa_pliku:
            return

        try:
            generuj_raport = GenerujRaport()
            generuj_raport.tworzTabele(nazwa_pliku, obszary, drogi, linie, nazwa_nadl)
            
            MessageUtils.pushMessage(self.iface, 'Generowanie raportu zakończone sukcesem!')
            
            folder_name = os.path.dirname(nazwa_pliku)
            if os.path.exists(folder_name):
                FileUtils.openFile(folder_name)
                
        except PermissionError:
            MessageUtils.pushMessageBoxCritical(self, "Błąd uprawnień", "Nie można zapisać pliku. Zamknij raport, jeśli jest otwarty w innym programie i spróbuj ponownie.")
        except Exception as e:
            MessageUtils.pushLogInfo(f"Błąd podczas generowania raportu: {e}")
            MessageUtils.pushMessageBoxCritical(self, "Błąd generowania", f"Wystąpił nieoczekiwany problem: {str(e)}")

    def generujWydruk(self):
        """
        Generuje wydruk i zapisuje go jako plik w wybranej lokalizacji.
        """
        generator = WydrukGenerator(self)
        sciezka_pliku = generator.generuj()

        if sciezka_pliku:
            folder = os.path.dirname(sciezka_pliku)
            MessageUtils.pushMessage(self.iface, f"Pomyślnie zapisano: {os.path.basename(sciezka_pliku)}")
            FileUtils.openFile(folder)    

    def resetuj(self):
        """
        Resetuje dane
        """
        self.wydzielenia = None
        self.wydzielenia_opisy = None
        self.drogi_lesne = None
        self.powiaty = None
        self.obszary = None
        self.drogi = None
        self.linie= None
        self.linie_bdot10k = None
        self.oddzialy = None
        self.nadlesnictwo = None
        self.mapa_bazowa = None

        self.pobierzWarstwyPochodneBtn.setEnabled(True)
        self.wczytajBdot10kBtn.setEnabled(False)
        self.analizaBtn.setEnabled(False)
        self.zapisBtn.setEnabled(False)
        self.raportBtn.setEnabled(False)
        self.wydrukBtn.setEnabled(False)
        MessageUtils.pushMessage(self.iface, "Dane zostały zresetowane")
        
    def zamknij(self):
        """
        Zamyka okno wtyczki
        """
        self.close()
    
    def help(self):
        """
        Otwiera instrukcję obsługi wtyczki w pdf
        """
        try:
            filepath = Path(__file__).parent / "docs" / "Instrukcja.pdf"
            os.startfile(filepath)
        except Exception:
            MessageUtils.pushMessageBoxCritical(self, "Błąd", "Nie można otworzyć instrukcji obsługi (plik docs/Instrukcja.pdf).")



