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

#******************************************************************************
#
# Freewat
# ---------------------------------------------------------
#
#
# Copyright (C) 2014 - 2015 Iacopo Borsi (iacopo.borsi@tea-group.com)
#
# This source 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.
#
# This code is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# A copy of the GNU General Public License is available on the World Wide Web
# at <http://www.gnu.org/licenses/>. You can also obtain it by writing
# to the Free Software Foundation, 51 Franklin Street, Suite 500 Boston,
# MA 02110-1335 USA.
#
#******************************************************************************

import os
from PyQt4 import QtGui, uic
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
from freewat.freewat_utils  import getVectorLayerByName, getVectorLayerNames, getFieldNames
from pyspatialite import dbapi2 as sqlite3


FORM_CLASS, _ = uic.loadUiType(os.path.join(os.path.dirname(__file__), 'ui/ui_copyFromVector.ui'))


class CopyFromVector(QDialog, FORM_CLASS):
    def __init__(self, iface):
        QDialog.__init__(self)
        self.iface = iface
        self.setupUi(self)

        self.buttonBox.rejected.connect(self.reject)
        self.buttonBox.button(QDialogButtonBox.Ok).clicked.connect(self.copyFunction)

        self.cmbLayerFrom.currentIndexChanged.connect(self.reloadFieldsFrom)
        self.cmbLayerTo.currentIndexChanged.connect(self.reloadFieldsTo)

        self.manageGui()

        self.checkSelected()
        self.cmbLayerFrom.currentIndexChanged.connect(self.checkSelected)

    def checkSelected(self):
        fromLay = getVectorLayerByName(self.cmbLayerFrom.currentText())
        if not fromLay:
            return

        if fromLay.selectedFeatures():
            self.check_selected.setEnabled(True)
        else:
            self.check_selected.setEnabled(False)
            self.check_selected.setChecked(False)

    def manageGui(self):
        self.cmbLayerFrom.clear()
        self.cmbLayerTo.clear()

        layerNameList = getVectorLayerNames()

        layerNameList.sort()

        self.cmbLayerFrom.addItems(layerNameList)
        self.cmbLayerTo.addItems(layerNameList)
        fromLay = getVectorLayerByName(self.cmbLayerFrom.currentText())
        toLay = getVectorLayerByName(self.cmbLayerTo.currentText())

    def reloadFieldsFrom(self):
        fromLay = getVectorLayerByName(self.cmbLayerFrom.currentText())
        self.cmbFieldFrom_1.clear()
        self.cmbFieldFrom_1.addItems(getFieldNames(fromLay))
        self.cmbFieldFrom_2.clear()
        self.cmbFieldFrom_2.addItem(' ')
        self.cmbFieldFrom_2.addItems(getFieldNames(fromLay))
        self.cmbFieldFrom_3.clear()
        self.cmbFieldFrom_3.addItem(' ')
        self.cmbFieldFrom_3.addItems(getFieldNames(fromLay))
        self.cmbFieldFrom_4.clear()
        self.cmbFieldFrom_4.addItem(' ')
        self.cmbFieldFrom_4.addItems(getFieldNames(fromLay))
        self.cmbFieldFrom_5.clear()
        self.cmbFieldFrom_5.addItem(' ')
        self.cmbFieldFrom_5.addItems(getFieldNames(fromLay))
        self.cmbFieldFrom_6.clear()
        self.cmbFieldFrom_6.addItem(' ')
        self.cmbFieldFrom_6.addItems(getFieldNames(fromLay))
        self.cmbFieldFrom_7.clear()
        self.cmbFieldFrom_7.addItem(' ')
        self.cmbFieldFrom_7.addItems(getFieldNames(fromLay))
        self.cmbFieldFrom_8.clear()
        self.cmbFieldFrom_8.addItem(' ')
        self.cmbFieldFrom_8.addItems(getFieldNames(fromLay))
        self.cmbFieldFrom_9.clear()
        self.cmbFieldFrom_9.addItem(' ')
        self.cmbFieldFrom_9.addItems(getFieldNames(fromLay))
        self.cmbFieldFrom_10.clear()
        self.cmbFieldFrom_10.addItem(' ')
        self.cmbFieldFrom_10.addItems(getFieldNames(fromLay))

    def reloadFieldsTo(self):
        toLay = getVectorLayerByName(self.cmbLayerTo.currentText())
        self.cmbFieldTo_1.clear()
        self.cmbFieldTo_1.addItems(getFieldNames(toLay))
        self.cmbFieldTo_2.clear()
        self.cmbFieldTo_2.addItem(' ')
        self.cmbFieldTo_2.addItems(getFieldNames(toLay))
        self.cmbFieldTo_3.clear()
        self.cmbFieldTo_3.addItem(' ')
        self.cmbFieldTo_3.addItems(getFieldNames(toLay))
        self.cmbFieldTo_4.clear()
        self.cmbFieldTo_4.addItem(' ')
        self.cmbFieldTo_4.addItems(getFieldNames(toLay))
        self.cmbFieldTo_5.clear()
        self.cmbFieldTo_5.addItem(' ')
        self.cmbFieldTo_5.addItems(getFieldNames(toLay))
        self.cmbFieldTo_6.clear()
        self.cmbFieldTo_6.addItem(' ')
        self.cmbFieldTo_6.addItems(getFieldNames(toLay))
        self.cmbFieldTo_7.clear()
        self.cmbFieldTo_7.addItem(' ')
        self.cmbFieldTo_7.addItems(getFieldNames(toLay))
        self.cmbFieldTo_8.clear()
        self.cmbFieldTo_8.addItem(' ')
        self.cmbFieldTo_8.addItems(getFieldNames(toLay))
        self.cmbFieldTo_9.clear()
        self.cmbFieldTo_9.addItem(' ')
        self.cmbFieldTo_9.addItems(getFieldNames(toLay))
        self.cmbFieldTo_10.clear()
        self.cmbFieldTo_10.addItem(' ')
        self.cmbFieldTo_10.addItems(getFieldNames(toLay))

    def copyFunction(self):
        # ------------ Load input data  ------------
        self.progressBar.setMinimum(0)
        self.progressBar.setMaximum(0)
        self.progressBar.setValue(0)

        # Layer names

        fromLay = getVectorLayerByName(self.cmbLayerFrom.currentText())
        toLay = getVectorLayerByName(self.cmbLayerTo.currentText())

        # use this method if both the layer provider is SpatiLite (faster method)
        if fromLay.providerType() == 'spatialite' and toLay.providerType() == 'spatialite':

            field_dict = {}
            field_dict[self.cmbFieldFrom_1.currentText()] = self.cmbFieldTo_1.currentText()

            # Check if other fields have to be copied, and add them to the list, just in case.
            if self.cmbFieldFrom_2.currentText() != ' ':
                field_dict[self.cmbFieldFrom_2.currentText()] = self.cmbFieldTo_2.currentText()
            if self.cmbFieldFrom_3.currentText() != ' ':
                field_dict[self.cmbFieldFrom_3.currentText()] = self.cmbFieldTo_3.currentText()
            if self.cmbFieldFrom_4.currentText() != ' ':
                field_dict[self.cmbFieldFrom_4.currentText()] = self.cmbFieldTo_4.currentText()
            if self.cmbFieldFrom_5.currentText() != ' ':
                field_dict[self.cmbFieldFrom_5.currentText()] = self.cmbFieldTo_5.currentText()
            if self.cmbFieldFrom_6.currentText() != ' ':
                field_dict[self.cmbFieldFrom_6.currentText()] = self.cmbFieldTo_6.currentText()
            if self.cmbFieldFrom_7.currentText() != ' ':
                field_dict[self.cmbFieldFrom_7.currentText()] = self.cmbFieldTo_7.currentText()
            if self.cmbFieldFrom_8.currentText() != ' ':
                field_dict[self.cmbFieldFrom_8.currentText()] = self.cmbFieldTo_8.currentText()
            if self.cmbFieldFrom_9.currentText() != ' ':
                field_dict[self.cmbFieldFrom_9.currentText()] = self.cmbFieldTo_9.currentText()
            if self.cmbFieldFrom_10.currentText() != ' ':
                field_dict[self.cmbFieldFrom_10.currentText()] = self.cmbFieldTo_10.currentText()

            # connection to the database
            # get the dbName from the FROM vector layer
            uri = QgsDataSourceURI(fromLay.dataProvider().dataSourceUri())
            dbName = uri.database()
            primaryKey, = [fromLay.fields()[i].name() for i in fromLay.pkAttributeList()]
            con = sqlite3.connect(dbName)
            # get the cursor
            cur = con.cursor()

            # get the geometry column names of both fromLay and toLay layers
            # fromLay
            fromLay_sql = """SELECT f_geometry_column FROM geom_cols_ref_sys WHERE f_table_name IS '{}' """.format(fromLay.name().lower())
            cur.execute(fromLay_sql)
            fromLay_geom = cur.fetchall()[0][0]
            # toLay
            toLay_sql = """SELECT f_geometry_column FROM geom_cols_ref_sys WHERE f_table_name IS '{}' """.format(toLay.name().lower())
            cur.execute(toLay_sql)
            toLay_geom = cur.fetchall()[0][0]

            # initialize SQL string
            sql_string = 'UPDATE "{}" SET {}'.format(
                    toLay.name(),
                    ','.join([
                        ('"{v}" = COALESCE((SELECT "{k}" FROM "{src}" WHERE ST_Intersects("{src}"."{sgeo}", "{dst}"."{dgeo}") /*FILTER*/), "{v}")'.format(
                            v=v, k=k, src=fromLay.name(), dst=toLay.name(), sgeo=fromLay_geom, dgeo=toLay_geom)
                        if fromLay.wkbType() not in [QGis.WKBPolygon, QGis.WKBMultiPolygon] else
                        '"{v}" = COALESCE((SELECT "{k}" FROM "{src}" WHERE ST_Intersects("{src}"."{sgeo}", ST_Centroid("{dst}"."{dgeo}")) /*FILTER*/), "{v}")'.format(
                            v=v, k=k, src=fromLay.name(), dst=toLay.name(), sgeo=fromLay_geom, dgeo=toLay_geom)
                        )
                        for k, v in field_dict.items()])
                    )

            if self.check_selected.isChecked():
                sql_string = sql_string.replace('/*FILTER*/', "AND \"{}\" IN ({})".format(
                    primaryKey,
                    ','.join([str(f[primaryKey]) for f in fromLay.selectedFeatures()])))

            # execute the sql statement
            cur.execute(sql_string)
            # close the cursor, commit the changes and close the db connection
            cur.close()
            con.commit()
            con.close()

        # use to old method if both the layer providers are ogr (layers = shapefile)
        elif fromLay.providerType() == 'ogr' and toLay.providerType() == 'ogr' \
                or fromLay.providerType() == 'ogr' and toLay.providerType() == 'spatialite' \
                or fromLay.providerType() == 'spatialite' and toLay.providerType() == 'ogr':

            fieldList = [(self.cmbFieldFrom_1.currentText(), self.cmbFieldTo_1.currentText())]

            # Check if other fields have to be copied, and add them to the list, just in case.
            if self.cmbFieldFrom_2.currentText() != ' ':
                fieldList.append((self.cmbFieldFrom_2.currentText(), self.cmbFieldTo_2.currentText()))
            if self.cmbFieldFrom_3.currentText() != ' ':
                fieldList.append((self.cmbFieldFrom_3.currentText(), self.cmbFieldTo_3.currentText()))
            if self.cmbFieldFrom_4.currentText() != ' ':
                fieldList.append((self.cmbFieldFrom_4.currentText(), self.cmbFieldTo_4.currentText()))
            if self.cmbFieldFrom_5.currentText() != ' ':
                fieldList.append((self.cmbFieldFrom_5.currentText(), self.cmbFieldTo_5.currentText()))
            if self.cmbFieldFrom_6.currentText() != ' ':
                fieldList.append((self.cmbFieldFrom_6.currentText(), self.cmbFieldTo_6.currentText()))
            if self.cmbFieldFrom_7.currentText() != ' ':
                fieldList.append((self.cmbFieldFrom_7.currentText(), self.cmbFieldTo_7.currentText()))
            if self.cmbFieldFrom_8.currentText() != ' ':
                fieldList.append((self.cmbFieldFrom_8.currentText(), self.cmbFieldTo_8.currentText()))
            if self.cmbFieldFrom_9.currentText() != ' ':
                fieldList.append((self.cmbFieldFrom_9.currentText(), self.cmbFieldTo_9.currentText()))
            if self.cmbFieldFrom_10.currentText() != ' ':
                fieldList.append((self.cmbFieldFrom_10.currentText(), self.cmbFieldTo_10.currentText()))

            layerFrom = QgsMapLayerRegistry.instance().mapLayersByName(
                    self.cmbLayerFrom.currentText())[0]
            layerTo = QgsMapLayerRegistry.instance().mapLayersByName(
                    self.cmbLayerTo.currentText())[0]
            assert(layerFrom and layerTo)
            layerFrom.dataProvider().createSpatialIndex()
            layerTo.dataProvider().createSpatialIndex()

            changedAttributesDict = {}

            featIds = set(layerFrom.selectedFeaturesIds()) \
                    if self.check_selected.isChecked() else \
                    set([i.id() for i in layerFrom.getFeatures()])

            transfo = QgsCoordinateTransform(layerTo.crs(), layerFrom.crs()) \
                    if layerFrom.crs() != layerTo.crs() else None

            for toFeat in layerTo.getFeatures():
                toGeom = QgsGeometry(toFeat.geometry()) \
                        if fromLay.wkbType() not in [QGis.WKBPolygon, QGis.WKBMultiPolygon] else \
                        QgsGeometry(toFeat.geometry().centroid())

                if transfo:
                    toGeom.transform(transfo)

                fromRequest = QgsFeatureRequest(toGeom.boundingBox())
                for fromFeat in layerFrom.getFeatures(fromRequest) :
                    fromGeom =QgsGeometry(fromFeat.geometry())
                    if fromFeat.id() in featIds and toGeom.intersects(fromGeom):
                        changedAttributes = {}
                        for fieldFrom, fieldTo in fieldList:
                            destIdx = layerTo.dataProvider().fieldNameIndex(fieldTo)
                            valueToCopy = fromFeat[fieldFrom]
                            changedAttributes[destIdx] = valueToCopy
                            QApplication.processEvents()
                        changedAttributesDict[toFeat.id()] = changedAttributes

            layerTo.dataProvider().changeAttributeValues(changedAttributesDict)
            layerTo.updateFields()

        self.progressBar.setMaximum(100)

        self.reject()
