#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 import Qt, QtCore
from PyQt4.Qt import QAbstractItemView
from qgis.core import QgsFeatureRequest
from view.CheckBoxDelegate import CheckBoxDelegate

from feature.AKFeature import AKFeature
from form.AKFormSamplesResults import AKFormSamplesResults

class AKFeatureResults(AKFeature):
    '''
    This is the base class for the results sub-feature. This feature adds the selected measurements to a new model grouped by sample.
    Depending on the ResultsFeature-type post-processing is applied to the new model.
    Some ResultsFeatures require the sample results grouped by parameter, where the values are stored as timeseries.
    The function storeDataInTimeSeries() takes care of that.
    '''
    def __init__(self, settings, iface, windowTitle="", parent=None):
        super(AKFeatureResults, self).__init__(iface, parent)
        self.m_settings = settings
        self.columnsAlreadyPresent = []
        
        self.m_text = windowTitle
        self.m_form = AKFormSamplesResults(iface, self.parent())
        self.customizeForm()

    def customizeForm(self):
        self.m_form.setWindowTitle(self.m_text)
        self.m_form.btnClose.clicked.connect(self.onClose)
        self.m_form.finished.connect(self.onClose)
        self.m_form.btnSaveTable.clicked.connect(self.onSaveTable)
        self.m_form.plotButton.clicked.connect(self.onActionClicked)        
        self.m_form.setWindowTitle(self.m_text)
        self.m_form.customResultsGroup.hide()

    def initialize(self):
        self.currentDB = self.m_settings.getCurrentDB()
        if(self.currentDB != None): 
            self.m_form.setWindowModality(QtCore.Qt.WindowModal)
            self.m_form.show()
            self.m_form.exec_()
    
    def onActionClicked(self):
        pass
    
    def setModel(self, newModel = None):
        self.sampleMeasurementsModel = newModel

    def showData(self, hideMeasurements=False):
        (_numOfColumns, 
         numOfFixedColumns, 
         numOfMeasurementCols, 
         _numOfCustomCols) = self.sampleMeasurementsModel.getNumberOfColumns()
        self.m_form.tvSampleMeasurements.setModel(self.sampleMeasurementsModel)

        self.m_form.tvSampleMeasurements.setColumnHidden(self.sampleMeasurementsModel.COLUMNS["PointId"], hideMeasurements)
        self.m_form.tvSampleMeasurements.setColumnHidden(self.sampleMeasurementsModel.COLUMNS["Coordinate X"], hideMeasurements)
        self.m_form.tvSampleMeasurements.setColumnHidden(self.sampleMeasurementsModel.COLUMNS["Coordinate Y"], hideMeasurements)
        
        if "valid" in self.sampleMeasurementsModel.COLUMNS:
            self.m_form.tvSampleMeasurements.setItemDelegateForColumn(self.sampleMeasurementsModel.COLUMNS["valid"], CheckBoxDelegate(self.m_form.tvSampleMeasurements))

        for column in range(numOfFixedColumns, numOfFixedColumns+numOfMeasurementCols):
            if column not in self.columnsAlreadyPresent:
                self.m_form.tvSampleMeasurements.setColumnHidden(column, hideMeasurements)

        self.m_form.tvSampleMeasurements.setSelectionBehavior(QAbstractItemView.SelectRows)

    def onClose(self):
        self.m_form.close()
        
    def onSaveTable(self):
        model = self.sampleMeasurementsModel
        numRows = model.rowCount()
        numColumns = model.columnCount()
        
        headers = []
        for col in range(numColumns):
            headers.append(model.headerData(col, QtCore.Qt.Horizontal))            
       
        contents = []
        for row in range(numRows):
            rowLine = []
            for col in range(numColumns):
                curItem = model.data(model.index(row, col))
                rowLine.append(curItem)
            contents.append(rowLine)

        self.saveTable(self.m_form, headers, contents)

    def postProcessModelData(self, paramInfo, parameterOrder):
        model = self.sampleMeasurementsModel
        self.paramInfo = paramInfo
        self.parameterOrder = parameterOrder

        self.m_form.grpLowerPanel.setEnabled(True)

        # add columns
        for paramName in parameterOrder:
            curParamInfo = paramInfo[paramName]
            parameterId = curParamInfo["id"]
            name = curParamInfo["name"]
            baseParam = curParamInfo["baseParam"]
            unit = curParamInfo["unit"]
            unitCode = curParamInfo["unitCode"]
            unitId = curParamInfo["unitId"]
            label = ""
            if "label" in curParamInfo:
                label = curParamInfo["label"]
            if parameterId not in model.COLUMNS:
                model.addColumn(parameterId, paramName, baseParam, unit, unitCode, unitId, name, label)
            else:
                self.columnsAlreadyPresent.append(model.COLUMNS[parameterId])

        # get columns of interest
        self.columnsOfInterest = {}
        for paramName in parameterOrder:
            curParamInfo = paramInfo[paramName]
            parameterId = curParamInfo["id"]
            self.columnsOfInterest[paramName] = {"relatedParamIds": []}
            columnsOfInterest = self.columnsOfInterest[paramName]
            if "parameterIds" in curParamInfo:
                parameterIds = curParamInfo["parameterIds"]
                for paramId in parameterIds:
                    if paramId in model.COLUMNS:
                        columnsOfInterest["relatedParamIds"].append(paramId)

        # store the values for the related parameters in the model
        # if a value is found, calculate the new column by applying a factor 
        numOfSamples = model.rowCount()
        for row in range(numOfSamples):
            newColumn = None
            for paramName in parameterOrder:
                value = None
                for relatedParamId in self.columnsOfInterest[paramName]["relatedParamIds"]:
                    if(model.data(model.index(row, model.COLUMNS[relatedParamId])) != None):
                        value = model.data(model.index(row, model.COLUMNS[relatedParamId]))
                        break # If a value is found, stop searching.
                parameterId = paramInfo[paramName]["id"]
                newColumn = model.COLUMNS[parameterId]
                if(value != None):
                    value = value / paramInfo[paramName]["factor"]
                model.setData(model.index(row, newColumn), value)

    def addValidColumn(self, paramInfo, parameterOrder):
        model = self.sampleMeasurementsModel
        model.addSimpleColumn("valid")
        numOfSamples = model.rowCount()
        for row in range(numOfSamples):
            valid = True
            for paramName in parameterOrder:
                paramId = paramInfo[paramName]["id"]
                columnIndex = model.COLUMNS[paramId]
                newValue = model.data(model.index(row, columnIndex))
                if(newValue == None):
                    valid = False
            self.sampleMeasurementsModel.setData(self.sampleMeasurementsModel.index(row, model.COLUMNS["valid"]), valid)
            
    def createUniqueSeriesId(self, pointId, parameterId):
        return str(pointId) + "_" + str(parameterId)
    
    def storeDataInTimeSeries(self):
        model = self.sampleMeasurementsModel
#         timePlotData = {unitId: {"valuesLabel": "mg/L",
#                                  "unitCode": "MGL",
#                                  "datesLabel": "time",
#                                  "title": "MyTitle_1",
#                                  "parameterIds": [parId1,parId2],
#                                  "series" : {"pointId_parId" : {"dates": ["23011977",  "25011977"],
#                                                                  "values": [1.5, 0.5],
#                                                                   "samples": ["asdfs", "asdfsdfff"],
#                                                                   "parameterName": "Magnesium",
#                                                                   "parameterId": 23,
#                                                                   "point" : "P12",
#                                                                   "pointId" : 123
#                                                                   },
#                                                "pointId_parId" : {"dates": ["23011977",  "25011977"],
#                                                                    "values": [1.5, 0.5],
#                                                                    "samples": ["asdfs", "asdfsdfff"],
#                                                                    "parameterName": "Chloride",
#                                                                    "parameterId": 23,
#                                                                    "point" : "P12",
#                                                                    "pointId" : 123
#                                                                    } 
#                                                }
#                                        },....  
#                             }
        timeSeriesData = {}
        columnInfo = self.sampleMeasurementsModel.getMeasurementColumnInfo()
        columnInfo.update(self.sampleMeasurementsModel.getCustomColumns())
        
        onlyValidRows = False
        if "valid" in self.sampleMeasurementsModel.COLUMNS:
            onlyValidRows = True
        # prepare timeSeriesData structure for series of measurements
        for paramId in columnInfo:
            curColumn = columnInfo[paramId]
            if "unitId" in curColumn: 
                unitId = curColumn["unitId"]
                unitCode = curColumn["unitCode"]
                unit = curColumn["unit"]
                if not unitId in timeSeriesData:
                    timeSeriesData[unitId] = {}
                    timeSeriesData[unitId]["valuesLabel"] = unit
                    timeSeriesData[unitId]["unitCode"] = unitCode
                    timeSeriesData[unitId]["datesLabel"] = "time"
                    timeSeriesData[unitId]["title"] = "Measurements (" + str(unit) + ")"
                    timeSeriesData[unitId]["series"] = {}
                    timeSeriesData[unitId]["parameterIds"] = []
                    timeSeriesData[unitId]["parameterNames"] = []
                timeSeriesData[unitId]["parameterIds"].append(paramId)
                timeSeriesData[unitId]["parameterNames"].append(columnInfo[paramId]["paramName"])
        
        # add all data to timeSeriesData series
        lastPoint = None
        numOfSamples = model.rowCount()
        for row in range(numOfSamples):
            pointId = model.data(model.index(row, model.COLUMNS["PointId"]))
            isValid = True
            if onlyValidRows:
                isValid = model.data(model.index(row, model.COLUMNS["valid"]))
            if isValid:
                if(lastPoint != pointId):
                    point = model.data(model.index(row, model.COLUMNS["Point"]))
                    for unitId in timeSeriesData:
                        for paramId in timeSeriesData[unitId]["parameterIds"]:
                            curColumn = columnInfo[paramId]
                            newId = self.createUniqueSeriesId(pointId,paramId)
                            timeSeriesData[unitId]["series"][newId] = {}
                            timeSeriesData[unitId]["series"][newId]["parameterName"] = curColumn["paramName"]
                            timeSeriesData[unitId]["series"][newId]["parameterId"] = paramId
                            timeSeriesData[unitId]["series"][newId]["point"] = point
                            timeSeriesData[unitId]["series"][newId]["pointId"] = pointId
                            timeSeriesData[unitId]["series"][newId]["samples"] = []
                            timeSeriesData[unitId]["series"][newId]["dates"] = []
                            timeSeriesData[unitId]["series"][newId]["values"] = []
                    lastPoint = pointId
                sample = model.data(model.index(row, model.COLUMNS["Sample"]))
                msrDate = model.data(model.index(row, model.COLUMNS["Date"]))
                for unitId in timeSeriesData:
                    for paramId in timeSeriesData[unitId]["parameterIds"]:
                        curColumn = columnInfo[paramId]
                        seriesId = self.createUniqueSeriesId(pointId,paramId)
                        value = model.data(model.index(row, curColumn["column"]))
                        if(value != None):
                            timeSeriesData[unitId]["series"][seriesId]["samples"].append(sample)
                            timeSeriesData[unitId]["series"][seriesId]["dates"].append(msrDate)
                            timeSeriesData[unitId]["series"][seriesId]["values"].append(value)
        # get rid of empty series
        for unitId in timeSeriesData:
            seriesToDelete = []
            for seriesId in timeSeriesData[unitId]["series"]:
                curSeries = timeSeriesData[unitId]["series"][seriesId]
                if not curSeries["values"]:
                    seriesToDelete.append(seriesId)
            for deleteId in seriesToDelete:
                del timeSeriesData[unitId]["series"][deleteId]
        return timeSeriesData
    
    def getStatistics(self, values):
        targetFields = {"earliest":values[0],
                        "latest" : values[-1],
                        "mean": sum(values)/float(len(values)),
                        "minimum" : min(values),
                        "maximum" : max(values)}
        return targetFields
    
    
    def retrieveStiffValues(self, 
                             valuesByPoint
                             ):

        values = []
            
        for pointId in valuesByPoint:
            dict = {}
            pointValues = valuesByPoint[pointId]
            Ca = self.paramInfo["N209MQL"]["id"]
            dict["Ca"] = pointValues[Ca]
            HCO = self.paramInfo["N168MQL"]["id"]
            dict["HCO3"] = pointValues[HCO]
            SO4 = self.paramInfo["N582MQL"]["id"]
            dict["SO4"] = pointValues[SO4]
            Cl = self.paramInfo["N247MQL"]["id"]
            dict["Cl"] = pointValues[Cl]
            Na = self.paramInfo["N580MQL"]["id"]
            dict["Na"] = pointValues[Na]
            Mg = self.paramInfo["N436MQL"]["id"]
            dict["Mg"] = pointValues[Mg]
            values.append(dict)
            
        return values
    
    def retrieveLocalStiffCoordinates(self, 
                             valuesByPoint,
                             distY):
        uniqueIds = []
        coordinates = []
        values = []
        for pointId in valuesByPoint:
            pointValues = valuesByPoint[pointId]
            Ca = self.paramInfo["N209MQL"]["id"]
            valueCa = pointValues[Ca]
            HCO = self.paramInfo["N168MQL"]["id"]
            valueHCO = pointValues[HCO]
            SO4 = self.paramInfo["N582MQL"]["id"]
            valueSO4 = pointValues[SO4]
            Cl = self.paramInfo["N247MQL"]["id"]
            valueCl = pointValues[Cl]
            Na = self.paramInfo["N580MQL"]["id"]
            valueNa = pointValues[Na]
            Mg = self.paramInfo["N436MQL"]["id"]
            valueMg = pointValues[Mg]
            coordCalcium = (-valueCa, -distY)
            coordBicarbonate = (valueHCO, -distY)
            coordSulfate = (valueSO4, 0)
            coordChloride = (valueCl, distY)
            coordSodium = (-valueNa, distY)
            coordMagnesium = (-valueMg, 0) 
            coordinates.append([coordCalcium,
                                coordBicarbonate, 
                                coordSulfate, 
                                coordChloride, 
                                coordSodium, 
                                coordMagnesium])
            values.append([valueCa, 
                           valueHCO, 
                           valueSO4, 
                           valueCl, 
                           valueNa, 
                           valueMg])
            uniqueIds.append(pointId)
        return uniqueIds,coordinates,values

    def calcScaledCoordinates(self, coordinates, scaleX, scaleY):
        newCoordinates = []
        for coordSet in coordinates:
            newCoordinates.append([(coord[0]/scaleX,coord[1]/scaleY) for coord in coordSet])
        return newCoordinates
    
    def calcTranslatedCoordinatesToPoints(self, pointLayer, pointIds, coordinates):
        newCoordinates = []
        for index in range(len(pointIds)):
            pointId = pointIds[index]
            pointFeature = pointLayer.getFeatures(QgsFeatureRequest().setFilterFid( pointId ) ).next()
            transX = pointFeature.geometry().asPoint()[0]
            transY = pointFeature.geometry().asPoint()[1]
            coordSet = coordinates[index]
            newCoordinates.append([(coord[0] + transX,coord[1] + transY) for coord in coordSet])
        return newCoordinates