# -*- coding: utf-8 -*-
"""
/***************************************************************************
 CopernicusData
                                 A QGIS plugin
 This plugin downloads data using the cdsapi
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2020-01-21
        git sha              : $Format:%H$
        copyright            : (C) 2020 by Fredrik Lindberg
        email                : fredrikl@gvc.gu.se
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
"""
from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication, QObject, QThread
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction, QFileDialog, QMessageBox
from qgis.gui import QgsMapToolEmitPoint
from osgeo import osr, ogr, gdal
import os.path
import webbrowser
import datetime
from calendar import monthrange
from .WorkerDownload import Worker
import logging
import sys

# Initialize Qt resources from file resources.py
from .resources import *
# Import the code for the dialog
from .copernicus_data_dialog import CopernicusDataDialog


class CopernicusData:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'CopernicusData_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        self.dlg = CopernicusDataDialog()
        self.dlg.cmdSelectPoint.clicked.connect(self.select_point)
        self.dlg.cmdRunDownload.clicked.connect(self.download)
        self.dlg.pushButtonHelp.clicked.connect(self.help)
        self.fileDialog = QFileDialog()
        self.fileDialog.setFileMode(QFileDialog.Directory)
        self.fileDialog.setOption(QFileDialog.ShowDirsOnly, True)

        self.dlg.progressBar.setRange(0, 100)
        self.dlg.progressBar.setValue(0)

        # Parameters for downloader
        self.lat = None
        self.lon = None
        self.start_date = None
        self.end_date = None
        self.save_downloaded_file = None

        # Declare instance attributes
        self.actions = []
        # self.menu = self.tr(u'&Copernicus Data')

        # get reference to the canvas
        self.canvas = self.iface.mapCanvas()
        self.degree = 5.0
        self.point = None
        self.pointx = None
        self.pointy = None

        self.thread = None
        self.worker = None

        # #g pin tool
        self.pointTool = QgsMapToolEmitPoint(self.canvas)
        self.pointTool.canvasClicked.connect(self.create_point)

        self.folderPath = None
        self.save_downloaded_folder = None
        # Check if plugin was started the first time in current QGIS session
        # Must be set in initGui() to survive plugin reloads
        #self.first_start = None

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('CopernicusData', message)

    def add_action(
            self,
            icon_path,
            text,
            callback,
            enabled_flag=True,
            add_to_menu=True,
            add_to_toolbar=True,
            status_tip=None,
            whats_this=None,
            parent=None):

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            # Adds plugin icon to Plugins toolbar
            self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/copernicus_data/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u''),
            callback=self.run,
            parent=self.iface.mainWindow())

        # will be set False in run()
        # self.first_start = True

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(
                self.tr(u'&Copernicus Data'),
                action)
            self.iface.removeToolBarIcon(action)

    def help(self):
        url = "https://umep-docs.readthedocs.io/en/latest/pre-processor/Meteorological%20Data%20Download%20data%20(ERA5).html"
        webbrowser.open_new_tab(url)

    def select_point(self):  # Connected to "Seelct Point on Canves"
        # Calls a canvas click and create_point
        self.canvas.setMapTool(self.pointTool)
        self.dlg.setEnabled(False)

    def create_point(self, point):
        # report map coordinates from a canvas click
        self.dlg.setEnabled(True)
        self.dlg.activateWindow()

        # Strange things here since gdal 3.x Maybe to be fixed in other click parts...?
        canvas = self.iface.mapCanvas()
        srs = canvas.mapSettings().destinationCrs()
        # crs = str(srs.authid())
        # print(srs.authid())
        # old_cs = osr.SpatialReference()
        # old_cs.ImportFromEPSG(int(crs[5:]))
        
        old_cs = osr.SpatialReference()
        crs_ref = srs.toWkt()
        old_cs.ImportFromWkt(crs_ref)
        # print(old_cs)

        # new_cs = osr.SpatialReference()
        # new_cs.ImportFromEPSG(4326)

        wgs84_wkt = """
        GEOGCS["WGS 84",
            DATUM["WGS_1984",
                SPHEROID["WGS 84",6378137,298.257223563,
                    AUTHORITY["EPSG","7030"]],
                AUTHORITY["EPSG","6326"]],
            PRIMEM["Greenwich",0,
                AUTHORITY["EPSG","8901"]],
            UNIT["degree",0.01745329251994328,
                AUTHORITY["EPSG","9122"]],
            AUTHORITY["EPSG","4326"]]"""

        new_cs = osr.SpatialReference()
        new_cs.ImportFromWkt(wgs84_wkt)
    
        transform = osr.CoordinateTransformation(old_cs, new_cs)

        latlon = ogr.CreateGeometryFromWkt(
            'POINT (' + str(point.x()) + ' ' + str(point.y()) + ')')
        latlon.Transform(transform)

        gdalver = float(gdal.__version__[0])
        if gdalver == 3.:
            self.dlg.txtLon.setText(str(latlon.GetY()))  # changed to gdal 3
            self.dlg.txtLat.setText(str(latlon.GetX()))  # changed to gdal 3
        else:
            self.dlg.txtLon.setText(str(latlon.GetX()))  # changed to gdal 2
            self.dlg.txtLat.setText(str(latlon.GetY()))  # changed to gdal 2

        self.dlg.progressBar.setValue(0)

    def run(self):
        # Check the more unusual dependencies to prevent confusing errors later
        try:
            import supy as sp
        except Exception as e:
            QMessageBox.critical(None, 'Error', 'This plugin requires the supy package '
                                                'to be installed OR upgraded. Please consult the FAQ in the manual '
                                                'for further information on how to install missing python packages.')
            return

        if not (os.path.isfile(os.path.expanduser("~") + "/.cdsapirc")):
            QMessageBox.critical(None, 'CDS configuration missing', 'This plugin requires that you have configured your computer to use the CDS API. '
                                 'See the help section for the Copernicus plugin in the UMEP-manual for further information on how '
                                 'to make use of this plugin.')
            return

        self.dlg.show()
        self.dlg.exec_()

    def validate_downloader_input(self):
        """Validates user input for downloader section of form. Raises exception if a problem, commits
        parameters to object if OK"""

        # validate and record the latitude and longitude boxes (must be WGS84)
        try:
            lon = float(self.dlg.txtLon.text())
        except:
            raise ValueError('Invalid longitude co-ordinate entered')

        if not (-180 < lon < 180):
            raise ValueError(
                'Invalid longitude co-ordinate entered (must be -180 to 180)')

        try:
            lat = float(self.dlg.txtLat.text())
        except:
            raise ValueError('Invalid latitude co-ordinate entered')
        if not (-90 < lat < 90):
            raise ValueError(
                'Invalid latitude co-ordinate entered (must be -90 to 90)')

        self.lat = lat
        self.lon = lon

        # validate date range and add to object properties if OK
        start_date = self.dlg.txtStartDate.text()

        try:
            start_datetest = datetime.datetime.strptime(start_date, '%Y-%m-%d')
        except Exception:
            raise ValueError('Invalid start date (%s) entered' %
                             (start_datetest,))

        end_date = self.dlg.txtEndDate.text()
        try:
            end_datetest = datetime.datetime.strptime(end_date, '%Y-%m-%d')
        except Exception:
            raise ValueError('Invalid end date (%s) entered' % (end_datetest,))

        if start_date >= end_date:
            raise ValueError('Start date is greater or equal than end date')

        self.start_date = start_date
        self.end_date = end_date

    def folder_path(self):
        self.fileDialog.open()
        result = self.fileDialog.exec_()
        if result == 1:
            self.folderPath = self.fileDialog.selectedFiles()
            # self.dlg.textOutput.setText(self.folderPath[0])
            self.save_downloaded_folder = self.folderPath[0]

    def download(self):
        try:
            import supy as sp
        except:
            pass

        try:
            self.validate_downloader_input()  # Validate input co-ordinates and time range
            # Before starting, ask user where to save
            self.folder_path()
            if (self.folderPath is None) or (len(self.folderPath) == 0):
                return
        except Exception as e:
            QMessageBox.critical(None, "Error", str(e))
            return

        if QMessageBox.question(self.iface.mainWindow(), "Information", "Data will now be downloaded from the "
                                    "Copernicus project (https://cds.climate.copernicus.eu/)."
                                    "\r\n"
                                    "\r\n"
                                    "1 month of data takes about 6 minutes depending on traffic and your internet connection. " 
                                    "The traffic on the CDSAPI can be monitored from https://cds.climate.copernicus.eu/live/queue."
                                    "\r\n"
                                    "\r\n"
                                    "The QGIS session will be active while data is processed. If you like to monitor the download"
                                    "process from the CDS API, open your Python Console in QGIS."
                                    "\r\n"
                                    "\r\n"
                                    "Do you want to contiune?",
                                    QMessageBox.Ok | QMessageBox.Cancel) == QMessageBox.Ok:
                test = 4
        else:
            QMessageBox.critical(self.iface.mainWindow(),
                                 "Process aborted", "Download cancelled")
            return

        # print(self.lat)
        # print(self.lon)
        # print(self.start_date)
        # print(self.end_date)
        # print(self.folderPath[0])
        # self.dlg.progressBar.setValue(20)

        self.dlg.progressBar.setMinimum(0)
        self.dlg.progressBar.setMaximum(0)
        self.dlg.progressBar.setValue(0)

        # return

        # logger_sp = logging.getLogger('SuPy')
        # logger_sp.disabled = True

        # put in worker
        # sp.util.gen_forcing_era5(
            # self.lat, self.lon, self.start_date, self.end_date, dir_save=self.folderPath[0])
        # self.dlg.progressBar.setValue(100)
        # QMessageBox.information(
            # self.dlg, "Data Download (ERA5)", "Data downlaoded and processed succesfully")

        # return

        print(self.folderPath)

        self.startWorker(self.lat, self.lon, self.start_date, self.end_date, self.folderPath)

    def startWorker(self, lat, lon, start_date, end_date, folderPath):

        # Do download in separate thread and track progress
        worker = Worker(lat, lon, start_date, end_date, folderPath[0])
        
        self.dlg.cmdRunDownload.setText('Cancel')
        self.dlg.cmdRunDownload.clicked.disconnect()
        self.dlg.cmdRunDownload.clicked.connect(self.abort_download)
        self.dlg.cmdClose.setEnabled(False)
        
        thread = QThread(self.dlg)
        worker.moveToThread(thread)

        worker.finished.connect(self.workerFinished)
        worker.error.connect(self.download_error)
        thread.started.connect(worker.run)
        
        thread.start()
        self.thread = thread
        self.worker = worker

    def workerFinished(self, ret):
        try:
            self.worker.deleteLater()
        except RuntimeError:
             pass
        self.thread.quit()
        self.thread.wait()
        self.thread.deleteLater()


        if ret == 1:
            self.dlg.cmdRunDownload.setText('Run')
            self.dlg.cmdRunDownload.clicked.disconnect()
            self.dlg.cmdRunDownload.clicked.connect(self.download)
            self.setDownloaderButtonState(True)

            # Update the UI to reflect the saved file
            self.dlg.lblSavedDownloaded.setText(self.folderPath[0])
            # self.dlg.progressBar.setValue(100)
            self.dlg.progressBar.setRange(0, 100)
            self.dlg.progressBar.setValue(100)

            QMessageBox.information(self.dlg, "Data Download (ERA5)", "Data downlaoded and processed succesfully")

            self.iface.messageBar().pushMessage("Data Download (ERA5)",
                                    "Data downlaoded and processed succesfully", duration=5)
        else:
            self.dlg.cmdRunDownload.setText('Run')
            self.dlg.cmdRunDownload.clicked.disconnect()
            self.dlg.cmdRunDownload.clicked.connect(self.download)
            self.setDownloaderButtonState(True)
            self.dlg.progressBar.setValue(0)
            QMessageBox.information(None, "Data Download (ERA5)", "Operations cancelled, "
                    "process unsuccessful! See the General tab in Log Meassages Panel (speech bubble, lower right) for more information.")

    def download_error(self, errorstring):
        self.setDownloaderButtonState(True)
        QgsMessageLog.logMessage(errorstring, level=Qgis.Critical)
        
    def abort_download(self):
        self.dlg.progressBar.setValue(0)
        self.worker.kill()
        # Enable all buttons in downloader.
        self.setDownloaderButtonState(True)
        self.dlg.cmdRunDownload.clicked.disconnect()
        self.dlg.cmdRunDownload.setText('Run')
        self.dlg.cmdRunDownload.clicked.connect(self.download)

    def setDownloaderButtonState(self, state):
        ''' Enable or disable all dialog buttons in downloader section
        :param state: boolean: True or False. Reflects button state'''
        self.dlg.cmdSelectPoint.setEnabled(state)
        self.dlg.cmdRunDownload.setEnabled(state)
        self.dlg.cmdClose.setEnabled(state)
        self.dlg.txtLat.setEnabled(state)
        self.dlg.txtLon.setEnabled(state)
        self.dlg.txtStartDate.setEnabled(state)
        self.dlg.txtEndDate.setEnabled(state)
