#coding=utf-8
#Copyright (C) 2015 IDAEA-CSIC
#
#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. This program is distributed in the hope that it will be useful, but
#ITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
#PURPOSE. See the GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License along with this program; if not, write to
#the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
'''
:copyright: Copyright (C) 2015 IDAEA-CSIC
:authors: V. Velasco Mansilla, L.M. de Vries, A. Nardi, R. Criollo, E. Vázquez Suñé
:contact: enric.vazquez@idaea.csic.es
:license: GPL General Public License, version 2 or any later version (http://opensource.org/licenses/GPL-2.0)
'''
from PyQt4.Qt import QSqlQueryModel, QAbstractItemView, Qt, QSqlQuery, \
                     QSqlTableModel, QItemSelectionModel, QSortFilterProxyModel
from PyQt4.QtGui import QIcon, QMessageBox
from PyQt4.QtCore import QDateTime

from AKFeature import AKFeature
from form.AKFormQuerySamples import AKFormQuerySamples
from view.CheckBoxDelegate import CheckBoxDelegate
from model.QueryManagerSamplesModel import QueryManagerSamplesModel
from AKSettings import TABLES

class AKFeatureSamples(AKFeature):
    '''
    This feature takes care of storing a 'query': a selection of points for a specific date-range.
    Within this selection, specific samples can be deactivated to not form part of the query.
    Filter functionality is added so it is easier to activate or deactivate a specific set of samples.
    '''
    def __init__(self, settings, iface, parent=None):
        super(AKFeatureSamples, self).__init__(iface, parent)

        self.m_settings = settings
        self.m_icon = QIcon(settings.getIconPath('edit-select-chem.png'))
        self.m_text = u'Query Samples'
        self.m_form = AKFormQuerySamples(self.iface, self.parent())

        self.defineSQLQueries()

        self.m_form.queryButton.clicked.connect(self.onQuery)
        self.m_form.addQueryButton.clicked.connect(self.onAddQuery)
        self.m_form.removeQueryButton.clicked.connect(self.onRemoveQuery)
        self.m_form.removeAllQueryButton.clicked.connect(self.onRemoveAllQuery)
        self.m_form.refreshSpatialPointsButton.clicked.connect(self.onRefreshSpatialPoints)
        self.m_form.btnClose.clicked.connect(self.onClose)
        self.m_form.chkByPoints.stateChanged.connect(self.onPointsEnabled)
        self.m_form.btnSelectAllPoints.clicked.connect(self.onSelectAllPoints)
        self.m_form.btnApplyFilter.clicked.connect(self.updateTbSamples)
        self.m_form.btnActivateSamples.clicked.connect(self.activateResults)
        self.m_form.btnDeactivateSamples.clicked.connect(self.deActivateResults)
        self.m_form.btnSelectAllResults.clicked.connect(self.onSelectAllResults)
        self.m_form.btnDeselectAllResults.clicked.connect(self.onDeselectAllResults)
        self.m_form.chkByDate.stateChanged.connect(self.onDateRangeEnabled)
        self.m_form.chkByCampaign.stateChanged.connect(self.onCampaignEnabled)
        self.m_form.cmbFilterCampaign.currentIndexChanged.connect(self.onCampaignChanged)
        self.m_form.dtFilterStartDate.dateTimeChanged.connect(self.onDateRangeChanged)
        self.m_form.dtFilterEndDate.dateTimeChanged.connect(self.onDateRangeChanged)
        self.m_form.finished.connect(self.onClose)

    def initialize(self):
        self.m_form.setWindowTitle(self.m_text)
        self.clearAllAttributes()
        self.currentDB = self.m_settings.getCurrentDB()
        if(self.currentDB != None):
            self.onLoadQueryView()
            self.samplesModel.setDB(self.currentDB)
            self.m_form.show()
            self.m_form.exec_()

    def clearAllAttributes(self):
        self.currentDB = None
        self.spatialPointListModel = QSqlQueryModel(self)
        self.spatialPointQueriedListModel = QSqlQueryModel(self)
        self.samplesModel = QueryManagerSamplesModel(self.sqlUpdateSavedSamples, "id", self)

        self.currentQueryId = None
        self.filterCampaign = ""
        self.filterStartDate = None
        self.filterEndDate = None
        self.filterPointIdList = []
        self.m_form.tvResults.setModel(QSqlQueryModel(self))
        if self.m_form.cmbFilterCampaign != None:
            self.m_form.cmbFilterCampaign.setModel(QSqlQueryModel(self))
        self.m_form.lvFilterPoint.setModel(QSqlQueryModel(self))
        self.m_form.lvSpatialSelection.setModel(QSqlQueryModel(self))

    def onClose(self):
        self.m_form.grpLowerPanel.setEnabled(False)
        self.m_form.close()

    def onLoadQueryView(self):
        self.queryListModel = QSqlTableModel(self, self.currentDB)
        self.queryListModel.setTable(self.queryListTable)
        self.queryListModel.setEditStrategy(QSqlTableModel.OnFieldChange)
        self.queryListModel.select()
        curView = self.m_form.lvQueries
        curView.setModel(self.queryListModel)
        curView.setSelectionBehavior(QAbstractItemView.SelectRows)
        curView.setModelColumn(1)
        curView.selectionModel().selectionChanged.connect(self.onLvQueriesSelectionChanged)
        self.selectFirstInView(curView)

    def onRefreshSpatialPoints(self):
        pointsLayer = self.m_settings.m_availableTables[TABLES.POINTS]["layerObject"]
        selectedFeatures = pointsLayer.selectedFeatures()
        selectedPointIDs = [x.id() for x in selectedFeatures if x.geometry().type() == 0 ]
        selectedPointIDsAsStrings = [str(x) for x in selectedPointIDs]
        queryStr = self.sqlSelPoints_1 + ",".join(selectedPointIDsAsStrings) + self.sqlSelPoints_2

        self.spatialPointListModel.setQuery(queryStr, self.currentDB)
        while self.spatialPointListModel.canFetchMore():
            self.spatialPointListModel.fetchMore()

        if self.spatialPointListModel.lastError().isValid():
            print(self.spatialPointListModel.lastError())
            print("error 1")
        self.m_form.lvSpatialSelection.setModel(self.spatialPointListModel)
        self.m_form.lvSpatialSelection.setCurrentIndex(self.spatialPointListModel.index(0, 0))
        self.m_form.lvSpatialSelection.selectionModel().selectionChanged.connect(self.onSpatialSelectionChanged)
        self.m_form.lvSpatialSelection.setModelColumn(1)
        self.selectAllInView(self.m_form.lvSpatialSelection, QItemSelectionModel.Select)

    def onAddQuery(self):
        defaultName = "newQuery"
        defaultStartDate = "1900-01-27 00:00:00.000"
        defaultEndDate = "2014-01-27 00:00:00.000"
        rec = self.queryListModel.record()
        rec.setValue(1, defaultName)
        rec.setValue(2, defaultStartDate)
        rec.setValue(3, defaultEndDate)
        self.queryListModel.insertRecord(-1, rec)

    def onRemoveAllQuery(self):
        quit_msg = "Are you sure you want to remove all the queries?"
        reply = QMessageBox.question(self.m_form, 'Confirmation',
                         quit_msg, QMessageBox.Yes, QMessageBox.No)
        numOfRows = self.queryListModel.rowCount()
        if reply == QMessageBox.Yes:
            for _rowNum in range(numOfRows):
                self.queryListModel.removeRow(0)
            self.clearAllAttributes()

    def onRemoveQuery(self):
        sel = self.m_form.lvQueries.selectionModel().selection()
        if len(sel.indexes()) > 0:
            index = sel.indexes()[0]
            rowNum = index.row()
            self.queryListModel.removeRow(rowNum)
            self.clearAllAttributes()

    def deleteStoredQueryDetails(self):
        queryStr = "PRAGMA foreign_keys = ON;"
        QSqlQuery(queryStr, self.currentDB)
        # remove old query data
        query = QSqlQuery(self.currentDB)
        query.prepare(self.sqlDeleteQueryPoints)
        query.addBindValue(self.currentQueryId)
        query.exec_()

    def onQuery(self):
        if(self.currentQueryId == None):
            return
        startDate = self.m_form.startDateEdit.dateTime().toString("yyyy-MM-dd hh:mm:ss.zzz")
        endDate = self.m_form.endDateEdit.dateTime().toString("yyyy-MM-dd hh:mm:ss.zzz")

        query = QSqlQuery(self.currentDB)
        query.prepare(self.sqlUpdateSavedQueries)
        query.addBindValue(startDate)
        query.addBindValue(endDate)
        query.addBindValue(self.currentQueryId)
        query.exec_()
        # Deleta the query and it's related samples from the db.
        self.deleteStoredQueryDetails()
        # add new query data:
        realPointList = []
        for i in range(self.m_form.lvSpatialSelection.model().rowCount()):
            realPointList.append(self.m_form.lvSpatialSelection.model().record(i).value(0))

        for pointId in realPointList:
            query = QSqlQuery(self.currentDB)
            query.prepare(self.insertSavedQueryPoints)
            query.addBindValue(self.currentQueryId)
            query.addBindValue(pointId)
            query.exec_()

        # Get the points that have samples within the required dates
        pointList = ["\"" +  str(x) + "\"" for x in realPointList]
        listString = ",".join(pointList)
        queryStr = self.selPointsWithSamples_1
        queryStr += "(" + listString + ")"
        queryStr += self.selPointsWithSamples_2
        query = QSqlQuery(self.currentDB)
        query.prepare(queryStr)
        query.addBindValue(startDate)
        query.addBindValue(endDate)
        query.exec_()

        savedQueryPointId = None
        # insert the found points into table
        while query.next():
            pointId = query.value(0)
            # get new id
            query2 = QSqlQuery(self.currentDB)
            query2.prepare(self.sqlGetNewId)
            query2.addBindValue(self.currentQueryId)
            query2.addBindValue(pointId)
            query2.exec_()
            while query2.next():
                savedQueryPointId = query2.value(0)

            # get sample campaign details
            query3 = QSqlQuery(self.currentDB)
            query3.prepare(self.sqlSelSampleDetails)
            query3.addBindValue(pointId)
            query3.addBindValue(startDate)
            query3.addBindValue(endDate)
            query3.exec_()
            while query3.next():
                sampleId = query3.value(0)
                # Save the sample ids for this query
                query4 = QSqlQuery(self.currentDB)
                query4.prepare(self.sqlInsertSavedQuerySamples)
                query4.addBindValue(savedQueryPointId)
                query4.addBindValue(sampleId)
                query4.exec_()

        # Reload everything with new data
        self.updateFilters()
        self.updateTbSamples()

    def onLvQueriesSelectionChanged(self):
        self.m_form.grpLowerPanel.setEnabled(True)
        #Update current Query ID
        sel = self.m_form.lvQueries.selectionModel().selection()
        if len(sel.indexes()) < 1:
            return
        index = sel.indexes()[0]
        self.currentQueryId = self.queryListModel.record(index.row()).value("Id")
        self.currentStartDate = QDateTime.fromString(self.queryListModel.record(index.row()).value("startDate"), "yyyy-MM-dd hh:mm:ss.zzz")
        self.currentEndDate = QDateTime.fromString(self.queryListModel.record(index.row()).value("endDate"), "yyyy-MM-dd hh:mm:ss.zzz")
        self.updateFilters()
        self.updateTbSamples()
        self.updateEditQueryPanel()

    def updateFilters(self):
        # Disable all filters
        if self.m_form.chkByCampaign != None:
            self.m_form.chkByCampaign.setChecked(False)
        self.m_form.chkByDate.setChecked(False)
        self.m_form.chkByPoints.setChecked(False)
        # Update date selections widgets
        self.m_form.dtFilterStartDate.setDateTimeRange(self.currentStartDate,self.currentEndDate)
        self.m_form.dtFilterStartDate.setDateTime(self.currentStartDate)
        self.m_form.dtFilterEndDate.setDateTimeRange(self.currentStartDate,self.currentEndDate)
        self.m_form.dtFilterEndDate.setDateTime(self.currentEndDate)
        # Update Campaign Filter
        query = QSqlQuery(self.currentDB)
        query.prepare(self.sqlSelCampaigns)
        query.addBindValue(self.currentQueryId)
        query.exec_()
        if self.m_form.cmbFilterCampaign != None:
            campaignModel = QSqlQueryModel(self.m_form.cmbFilterCampaign)

            campaignModel.setQuery(query)
            while campaignModel.canFetchMore():
                campaignModel.fetchMore()

            self.m_form.cmbFilterCampaign.setModel(campaignModel)
            self.m_form.cmbFilterCampaign.setModelColumn(1)
        self.filterCampaign = ""
        self.loadFilterPointList()
        self.m_form.lvFilterPoint.selectionModel().selectionChanged.connect(self.onFilterPointsChanged)

    def onPointsEnabled(self, checkState):
        self.m_form.lvFilterPoint.setEnabled(checkState)
        disabledPointFilter = checkState == Qt.Unchecked
        if(disabledPointFilter):
            self.filterPointIdList = []
        else:
            self.onFilterPointsChanged()

    def onSelectAllPoints(self):
        self.selectAllInView(self.m_form.lvFilterPoint, QItemSelectionModel.Select)

    def onDeselectAllPoints(self):
        self.selectAllInView(self.m_form.lvFilterPoint, QItemSelectionModel.Deselect)

    def onFilterPointsChanged(self):
        if(self.m_form.chkByPoints.checkState() == Qt.Checked):
            sel = self.m_form.lvFilterPoint.selectionModel().selection()
            self.filterPointIdList = []
            for index in sel.indexes():
                if(index.column() == 0):
                    pointId = "\"" + str(self.m_form.lvFilterPoint.model().data(index)) + "\""
                    self.filterPointIdList.append(pointId)

    def onDateRangeEnabled(self, checkState):
        self.m_form.dtFilterStartDate.setEnabled(checkState)
        self.m_form.dtFilterEndDate.setEnabled(checkState)
        self.m_form.lblFilterStartDate.setEnabled(checkState)
        self.m_form.lblFilterEndDate.setEnabled(checkState)
        disabledDateRangeFilter = checkState == Qt.Unchecked
        if(disabledDateRangeFilter):
            self.filterStartDate = None
            self.filterEndDate = None
        else:
            self.onDateRangeChanged()

    def onDateRangeChanged(self):
        if(self.m_form.chkByDate.checkState() == Qt.Checked):
            self.filterStartDate = self.m_form.dtFilterStartDate.dateTime().toString("yyyy-MM-dd hh:mm:ss.zzz")
            self.filterEndDate = self.m_form.dtFilterEndDate.dateTime().toString("yyyy-MM-dd hh:mm:ss.zzz")

    def onCampaignEnabled(self, checkState):
        if self.m_form.cmbFilterCampaign != None:
            self.m_form.cmbFilterCampaign.setEnabled(checkState)
            disabledCampaignFilter = checkState == Qt.Unchecked
            if(disabledCampaignFilter):
                self.filterCampaign = ""
            else:
                self.onCampaignChanged()

    def onCampaignChanged(self):
        if(self.m_form.chkByCampaign.checkState() == Qt.Checked):
            campCombo = self.m_form.cmbFilterCampaign
            curIndex = campCombo.currentIndex()
            newIndex = campCombo.model().index(curIndex,0)
            self.filterCampaign = campCombo.model().data(newIndex)

    def updateEditQueryPanel(self):
        # Update date time widgets
        query = QSqlQuery(self.currentDB)
        query.prepare(self.sqlSelSavedQuery)
        query.addBindValue(self.currentQueryId)
        query.exec_()

        startDate = None
        endDate = None
        while query.next():
            startDate = query.value(1)
            endDate = query.value(2)

        dateTime = QDateTime.fromString(startDate, "yyyy-MM-dd hh:mm:ss.zzz")
        self.m_form.startDateEdit.setDateTime(dateTime)
        dateTime = QDateTime.fromString(endDate, "yyyy-MM-dd hh:mm:ss.zzz")
        self.m_form.endDateEdit.setDateTime(dateTime)
        # Fill list view with points saved in query
        query = QSqlQuery(self.currentDB)
        query.prepare(self.sqlSelSavedQueryPoint)
        query.addBindValue(self.currentQueryId)
        query.exec_()
        # Set model for spatial selection listview:
        self.spatialPointListModel = QSqlQueryModel(self)

        self.spatialPointListModel.setQuery(query)
        while self.spatialPointListModel.canFetchMore():
            self.spatialPointListModel.fetchMore()

        self.m_form.lvSpatialSelection.setModel(self.spatialPointListModel)
        self.m_form.lvSpatialSelection.setCurrentIndex(self.spatialPointListModel.index(0, 0))
        self.m_form.lvSpatialSelection.setModelColumn(1)
        self.m_form.lvSpatialSelection.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.m_form.lvSpatialSelection.selectionModel().selectionChanged.connect(self.onSpatialSelectionChanged)
        self.m_form.lvSpatialSelection.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.m_form.lvSpatialSelection.setSelectionBehavior(QAbstractItemView.SelectRows)

    def loadFilterPointList(self):
        query = QSqlQuery(self.currentDB)
        query.prepare(self.sqlSelectSavedPointsWithSamples)
        query.addBindValue(self.currentQueryId)
        query.exec_()

        self.spatialPointQueriedListModel.setQuery(query)
        while self.spatialPointQueriedListModel.canFetchMore():
            self.spatialPointQueriedListModel.fetchMore()

        self.m_form.lvFilterPoint.setModel(self.spatialPointQueriedListModel)
        self.m_form.lvFilterPoint.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.m_form.lvFilterPoint.setModelColumn(1)
        self.m_form.lvFilterPoint.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.m_form.lvFilterPoint.setSelectionMode(QAbstractItemView.ExtendedSelection)

    def updateTbSamples(self):
        queryStr = self.sqlSelSamples
        if(self.filterCampaign != ""):
            queryStr += self.sqlSelSamples_campaign
        if(self.filterStartDate != None):
            queryStr += self.sqlSelSamples_dateRange
        if(self.filterPointIdList != []):
            queryStr += self.sqlSelSamples_pointList + "(" + ",".join(self.filterPointIdList) + ")"
        queryStr += self.sqlEndQuery
        query = QSqlQuery(self.currentDB)
        query.prepare(queryStr)
        query.addBindValue(self.currentQueryId)
        if(self.filterCampaign != ""):
            query.addBindValue(self.filterCampaign)
        if(self.filterStartDate != None):
            query.addBindValue(self.filterStartDate)
            query.addBindValue(self.filterEndDate)
        query.exec_()

        self.samplesModel.setQuery(query)
        while self.samplesModel.canFetchMore():
            self.samplesModel.fetchMore()

        proxyModel = QSortFilterProxyModel()
        proxyModel.setSourceModel(self.samplesModel);
        self.m_form.tvResults.setModel(proxyModel)
        self.m_form.tvResults.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.m_form.tvResults.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.m_form.tvResults.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.m_form.tvResults.setItemDelegateForColumn(0, CheckBoxDelegate(self.m_form.tvResults))
        self.m_form.tvResults.setColumnHidden(1, True)
        self.m_form.tvResults.resizeColumnsToContents()
        self.m_form.tvResults.setSortingEnabled(True)

    def activateResults(self):
        sel = self.m_form.tvResults.selectionModel()
        curModel = self.m_form.tvResults.model()
        for index in sel.selectedRows():
            curModel.setData(index, 1)

    def deActivateResults(self):
        sel = self.m_form.tvResults.selectionModel()
        curModel = self.m_form.tvResults.model()
        for index in sel.selectedRows():
            curModel.setData(index, 0)

    def onSelectAllResults(self):
        self.selectAllInView(self.m_form.tvResults, QItemSelectionModel.Select)

    def onDeselectAllResults(self):
        self.selectAllInView(self.m_form.tvResults, QItemSelectionModel.Deselect)

    def onSpatialSelectionChanged(self):
        sel = self.m_form.lvSpatialSelection.selectionModel().selection()
        selPoints = []
        for index in sel.indexes():
            if(index.column() == 0):
                pointId = self.m_form.lvSpatialSelection.model().data(index)
                selPoints.append(pointId)
        self.m_settings.m_availableTables[TABLES.POINTS]["layerObject"].setSelectedFeatures(selPoints)

    def getIdsOfSelectedPointsInLayer(self, cLayer):
        selectedFeatures = cLayer.selectedFeatures()
        # TODO: Need to find out how to access QGis (for QGis.Point). Forcing the import QGis, makes Point available, but still gives an error
        fullArray = [x.id() for x in selectedFeatures if x.geometry().type() == 0 ]
        return fullArray