# -*- coding: utf-8 -*-
"""
Copyright (c) 2016 Biuro Urządzania Lasu i Geodezji Leśnej

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
OTHER DEALINGS IN THE SOFTWARE.
"""

"""operacje na bibliotece do wymiany danych"""
#Marcin Śliwa
from lib2to3.pytree import convert
from PyQt5 import QtGui, uic
from PyQt5.QtWidgets import * 
from PyQt5.QtCore import * 
#from Mapa_PU.model.biblioteka_dll import Biblioteka
from Mapa_PU.widok.eksportuj_swdl_dialog import EksportujSWDLDialog
from Mapa_PU.kontroler import metody_globalne
#from Mapa_PU.model.baza_danych import Baza
from Mapa_PU.model.baza_danych import Baza
from Mapa_PU.kontroler import komunikaty
from Mapa_PU.swdl.model.tags import Tags
import qgis
import os
import datetime
import io
import re
#import System
from Mapa_PU.swdl.kontroler import swdl_helper
#from Mapa_PU.swdl.containers.containers import Database, SwdlHelperInstance,Layers,SwdlCreatorInstance,SwdlObjectConverterInstance,DatabaseExportInstance
from Mapa_PU.swdl.containers.containers import Container
from Mapa_PU.swdl.model.scheme import TreeElementOptional
from PyQt5.QtWidgets import QMainWindow, QTextEdit,QAction, QFileDialog, QApplication 
from lxml.etree import XMLParser, XMLSchema, parse, XMLSyntaxError, XML
from Mapa_PU.swdl.model.tags import TagsLayer
from Mapa_PU.kontroler.kontrola_geometrii import KontrolaGeometrii
from Mapa_PU.kontroler.kontrole_danych import KontrolujDane
from Mapa_PU.kontroler.kontrola_opisu_taksacyjnego import KontroleOpisuTaksacyjnego
from Mapa_PU.widok.pasek_wiadomosci import PasekWiadomosc
from zipfile import ZipFile
from Mapa_PU.swdl.model.scheme import RootElement
from Mapa_PU.kontroler.excel import Excel, ExcelError
from Mapa_PU.model import date_helper
from Mapa_PU.swdl.kontroler.xml_converter import XmlConverter
from qgis.core import (
    QgsApplication, QgsTask, QgsMessageLog,
    )


class Swdl():#Swdl(Biblioteka):
    """Klasa operująca na bibliotece do generowania SWDL"""     
    def __init__(self,root,swdl_version,sciezkaModulu,konfiguracja,mapa,iface,projekt,wersja,baza_kontroli:Baza):
        #katalogZasobow='%s//moduly//swdl//' % sciezkaModulu
        #Biblioteka.__init__(self,katalogZasobow)
        
        #self.zaladujZasob('BULiGL.SWDL.SWDLConverter')
        self.sciezkaModulu=sciezkaModulu
        self.__konfiguracja=konfiguracja
        self.__mapa=mapa
        self.__iface=iface
        self.__warstwy=(TagsLayer.SUBAREA_PARCEL.value,TagsLayer.LINES.value,TagsLayer.BASIC_OBJECTS.value,
                        TagsLayer.COUNTY.value,TagsLayer.COUNTY_LABELS.value,TagsLayer.SUBAREA_LABELS.value,
                        TagsLayer.POINT_SINGULARITIES.value,TagsLayer.SURFACE_SINGULARITIES.value,
                        TagsLayer.PNSW.value,TagsLayer.SURFACE.value,TagsLayer.PARCEL_LAND_USE.value)  #('POW.shp','O_PODST.shp','PNSW.shp','OS_PKT.shp','OS_POW.shp')
        self.__root=root
        self.__wersja=wersja
        self.__projekt=projekt
        self.__baza_kontroli=baza_kontroli
        self.__swdl_version=swdl_version
    
    def importuj(self,root):
        """Importuje dane z swdl do bazy i warstw projektu"""
        try:
            (jest,blad,brak)=self.__kontrolaWarstw()
            if jest==False:
                komunikaty.blad(u'Import z SWDL', u"W katalogu peojektu Warstwy brak warstw: %s " % ',',join(brak))
                return
            if len(blad)>0 or self.__kontrolaLiczbyRekordow("F_ARODES")==True:
                komunikaty.blad(u'Import z SWDL', u"W aktywnym projekcie zapisane są już dane (w bazie lub w warstwach). Proszę skonfigurować połączenie do nowego projektu z podpiętą pustą bazą programu Taksator PU.")
                return
            plik_swdl=self.plik_swdl()
            if plik_swdl==None:
                return
            #pasek_wiadomosci.setText("Import danych z pliku SWDL")
            #pasek_wiadomosci.setProgressValue(10)
            #Walidacja pliku swdl
            message=self.validujPlik(root, plik_swdl)
            if len(message)>0:
                komunikaty.blad('Plik SWDL zawiera błędy',''.join(message)) #komunikaty.wiadomoscBlad(self.__iface,'Plik SWDL zawiera błędy',''.join(message),60)
                return
            #Dodstę do bazy
            baza=os.path.normpath(self.__konfiguracja.plikBazaTaksator)
            katalog_warstw=os.path.normpath(self.__konfiguracja.warstwy)
            Container.Database=Baza(baza)
            Container.Layers=katalog_warstw
            #Database.database.override(Baza(baza))
            #Layers.path.override(katalog_warstw)
            #obiekt swdl
            swdl_object=Container.get().SwdlCreatorInstance 
            #swdl_object=SwdlCreatorInstance.swdl_creator()
            with ZipFile(plik_swdl) as zip:
                list=[swdl for swdl in zip.namelist() if '.SWDL'  in swdl.upper()]
                if len(list)==0:
                    komunikaty.wiadomoscBlad(self.__iface,'Brak pliku .swdl',60)
                    return
                with zip.open(list[0]) as xml:
                    swdl=swdl_object.create_swdl_from_xml(xml.read())
                    
                    #przygotowanie paska postępu
                    #max=len(swdl.subarea_data_list)
                    #progress=komunikaty.pasek_postepu(u"Trwa import danych z pliku SWDL.",max)
            database_export=Container.get().DatabaseExportInstance 
            database_export.insert_into_database(swdl)
        #   task1 = QgsTask.fromFunction('Waste cpu 1',database_export.insert_into_database ,
        #                              on_finished=self.completed, swdl=swdl)
        #             
        #   QgsApplication.taskManager().addTask(task1)
            self.__mapa.otworzProjekt()        
            komunikaty.informacja(u"Import z SWDL",u"Import danych z pliku SWDL zakończył się sukcesem.")
        except Exception as e:
            raise e
            komunikaty.blad_systemowy(e, u'Import danych z pliku SWDL zakończył się błędem')
#             komunikaty.blad("Błąd swdl.importuj",traceback.format_exc())
        finally:
            komunikaty.usun()
    
    def test1(self):
        
        
        task1 = QgsTask.fromFunction('Waste cpu 1', self.test,
                             on_finished=self.completed,wait_time=20)
        QgsApplication.taskManager().addTask(task1)
    
    def completed(self,exception, result=None):
           
        komunikaty.wiadomoscBlad(self.iface,u'AAAA',Exception())
    
    def test(self,task,wait_time):
        import random
        from time import sleep
        wait_time = wait_time / 100
        total = 0
        iterations = 0
        for i in range(100):
            sleep(wait_time)
            # use task.setProgress to report progress
            task.setProgress(i)
            arandominteger = random.randint(0, 500)
            total += arandominteger
            iterations += 1
            # check task.isCanceled() to handle cancellation
            if task.isCanceled():
                stopped(task)
                return None
            # raise an exception to abort the task
            if arandominteger == 42:
                raise Exception('bad value!')
        return {'total': total, 'iterations': iterations,
                'task': task.description()}
         
                
    def plik_swdl(self):
        """Zwraca plik swdl
        :return plik
        """
        try:
             
            plikSWDL,typ=QFileDialog.getOpenFileName(self.__iface.mainWindow(),u'Proszę wskazać plik SWDL',self.__konfiguracja.sciezka,"Plik SWDL (*.zip *.ZIP)")
            
            if plikSWDL=="":
                return None
            return plikSWDL
        except Exception as e:
            komunikaty.blad_systemowy(e)
        
    def eksportuj(self):
        """Eksport danych z bazy do swdl"""
        try:
#             if self.__kontrolaLiczbyRekordow("F_PARAMETER")==False:
#                 komunikaty.blad(u"Brak rekordów", u"Nie można wykonać eksportu z bieżącej bazy, ponieważ brakuje informacji o obiekcie. Uzupełnij je za pomocą programu Taksator -Plany uproszczone.")
#                 return None
            
            self.okno=EksportujSWDLDialog(self.__konfiguracja,self.__iface.mainWindow())
            
            ret=self.okno.exec_()
            if ret==0:
                return None
            
            
            #progress=komunikaty.pasek_postepu('Test',10)
            
            #komunikaty.usun()
                        
            #export danych
            #komunikaty.czekanie(u"Eksport danych do pliku SWDL.")
            #komunikat,postep=komunikaty.pasek_postepu(u"Eksport danych do pliku SWDL.")
            pasek_wiadomosci=PasekWiadomosc(self.__iface,u"Eksport danych do pliku SWDL.")
            pasek_wiadomosci.pasekPostepu(minimum=0, maximum=100)
            
            baza=os.path.normpath(self.__konfiguracja.plikBazaTaksator)
            katalogWarstw=os.path.normpath(self.__konfiguracja.warstwy)
            sciezka=os.path.normpath('%s\\moduly\\swdl' % self.sciezkaModulu)
            #konfiguracja kontenera o bazę danych i katalog warstw
            Container.Database=Baza(baza)
            Container.Layers=os.path.normpath(self.__konfiguracja.warstwy)
            
            #Kontrola danych przed eksportem
            #kontrola geometryczna i danych w bazie
            controler=Container.get().DatabaseControlerInstance
            database_helper=Container.get().DatabaseHelperInstance
            database_helper.open()
            kontrola_geometrii=KontrolaGeometrii(self.__iface,self.__root,self.__konfiguracja,self.__mapa,self.__projekt,self.__wersja,self.__baza_kontroli)
            kontrola_danych=KontrolujDane(self.__root,self.__swdl_version,self.__mapa,self.__konfiguracja,self.__iface,self.__wersja)
            kontrola_opisu=KontroleOpisuTaksacyjnego(self.__root,self.__konfiguracja,self.__wersja,self.__iface)
            directory=os.path.split(self.okno.plik)
            report=controler.control(self.__root,self.__swdl_version,pasek_wiadomosci,kontrola_geometrii,kontrola_danych,kontrola_opisu,Container.Database,'{}\Kontrole'.format(directory[0]))
            database_helper.close()
            #Zapis wyników
                        
            if report.is_error==True:
                heading=('Kontrola exportu danych do pliku SWDL','Wersja programu Mapa PU {}, {}'.format(self.__wersja,date_helper.curentDate()),'Raport zawiera wyniki kontroli exportu do pliku SWDL.')
                metody_globalne.zapiszText( report.file_path, report.report )
                
                #Zapis do pliku Excel
                errors=list()
                excel=Excel()
                for result in report.results:
                    for error in result.errors:
                        errors.append(ExcelError(result.name,'{} {}'.format(result.message,error),''))
                
                filename_xls='%s\Kontrola_exportu_SWDL_%s.xlsx' % (self.__konfiguracja.kontrole,  datetime.date.today())
                excel.save_to_excell(errors,self.__root,filename_xls,heading)
                
                ret = komunikaty.pytanie_zamknij_pokaz_raport_txt_xls(u'Zakończenie kontroli', (u"Nie można wykonać eksportu do pliku SWDL z bieżącej bazy ponieważ dane zawierają błędy.  Szczegóły zostały zapisane do pliku raportu \n '%s'" % '{}\n{}'.format(report.file_path,filename_xls)))
                if ret == 1 :
                    os.startfile(report.file_path)
                if ret==2:
                    os.startfile(filename_xls)
                komunikaty.usun()
                return
            #
           
            
            if self.okno.dataDomyslna==True:
                data=None
            else:
                dataUstawiona=self.okno.data
                data=date(dataUstawiona.year(),dataUstawiona.month(),dataUstawiona.day())
            
            pasek_wiadomosci.setText('Generowanie pliku swdl')
            pasek_wiadomosci.setProgressValue(10)
            #Export danych
            swdl_object=Container.get().SwdlCreatorInstance #SwdlCreatorInstance.swdl_creator()
            swdl=swdl_object.create_swdl_object(self.okno.pow,self.okno.adres,self.okno.stanowisko,self.okno.nazwaOsoby, self.okno.telefon, self.okno.email,self.okno.adresLesny,data)
            pasek_wiadomosci.setProgressValue(60)
            pasek_wiadomosci.setText('Zapis pliku swdl')
            #przygotowanie paska postępu
            #max=len(swdl.subarea_data_list)
            #progress=komunikaty.pasek_postepu(u"Eksport danych do pliku SWDL.",max)
            
            xml=Container.get().SwdlObjectConverterInstance.create_swdl(swdl) #SwdlObjectConverterInstance.swdl_object_converter().create_swdl(swdl)
            pasek_wiadomosci.setProgressValue(100)
            ##Zapis drzewa do pliku
            xml.write(self.okno.plik)
            #konwerter.ExportToSWDL(baza.encode("windows-1250"),katalogWarstw.encode("windows-1250"),plik.encode("windows-1250"),naglowek,konfiguracja,sciezka.encode("windows-1250"))
            komunikaty.usun()
            komunikaty.informacja('Eksport', u'Eksport danych do pliku SWDL zakończył się sukcesem.')
           
        except Exception as e:
            
            raise e

    def validuj(self,root):
        u"""Waliduje plik SWDL szablonem XSD"""
        try:
            
            plik_swdl=self.plik_swdl()
            if plik_swdl==None:
                return
            komunikaty.czekanie(u"Sprawdzanie poprawności pliku SWDL.")
            message=self.validujPlik(root, plik_swdl)
            if len(message)>0:
                komunikaty.wiadomoscBlad(self.__iface,'Plik SWDL zawiera błędy',''.join(message),60)
            else:
                komunikaty.wiadomoscInfo(self.__iface,u'Plik SWDL jest poprawny')

        except Exception as e:
            raise e
    
    def validujPlik(self,root:str,plik_swdl:str)->[]:
        u"""Waliduje plik swdl"""
        try:
           
            #file=open(plik_swdl,mode='rb')
            #xml=XML(file.read())
            with ZipFile(plik_swdl) as myzip:
                list=[swdl for swdl in myzip.namelist() if '.SWDL'  in swdl.upper()]
                if len(list)==0:
                    return 'Brak pliku .swdl'
                with myzip.open(list[0]) as file:
                    xml=XML(file.read())
                    #Pobranie wersji swdl Tags.SWDL_VERSION.value
                    converter=XmlConverter()
                    e=converter.get_element(xml,'swdlVersion')
                    version=e.text
                    #
                    xsd=root+u'/XSD/{}/XSD/swdl/swdl.xsd'.format(version)
                    message=[]
                    if os.path.exists(xsd)==False:
                        message.append("Wersja pliku swdl {} nie jest obsługiwana przez aplikację.".format(version))
                        return message
                    xmlschema=XMLSchema(file=xsd)
                    #parser=XMLParser(schema=xmlschema)
                    
                    if not xmlschema.validate(xml):
                        for error in xmlschema.error_log:
                            message.append("{} Linia: {}\n".format(error.message,error.line))
                    return message
        except Exception as e:
            raise e
            
    def __kontrolaWarstw(self):
        """sprawdza czy istnieją pliki warstw
        :return wynik kontroli (jest warstwa,warstwy pełne,brak warstw)
        """
        try:
            jest=False
            blad=[]
            brak=[]
            katalog=self.__konfiguracja.warstwy
            for nazwa in self.__warstwy:
                sciezka='%s\\%s.shp' % (katalog,nazwa)
                
                warstwa=self.__mapa.otworzWarstwe(nazwa, sciezka)
                if warstwa==None:
                    brak.append(nazwa)
                    continue
                jest=True
                if warstwa.liczbaRekordow()>0:
                     blad.append(nazwa)
                
            return (jest,blad,brak)
        except Exception as e:
            komunikaty.blad_systemowy(e)
    
    def __kontrolaLiczbyRekordow(self,nazwaTabeli):
        """Kontroluje czy w tabeli są dane
        :param nazwaTabeli: nazwa tabeli
        :return False - brak danych
        """
        try:
            sql="SELECT Count(*) FROM %s" % nazwaTabeli
            baza=Baza(self.__konfiguracja.plikBazaTaksator)
            baza.polaczOdbc()
            liczba=baza.wykonajZapytanie(sql)[0][0]
            baza.zamknijPolaczenie()
            if liczba==0:
                return False
            return True
        except Exception as e:
            komunikaty.blad_systemowy(e)

    
        