from PyQt4 import QtCore
from PyQt4 import QtGui
from qgis.core import *
import pandas as pd
import tempfile
import os
from numpy import linspace
import re
from PyQt4.QtCore import QSettings


class VectorLayer(object):

    def __init__(self, model_files_path):
        self.modelFilesPath = model_files_path
        self.tmpPath = tempfile.gettempdir() + os.path.sep
        self.tmpFileR = self.tmpPath + 'data0.csv'
        self.tmpFileW = self.tmpPath + 'data1.csv'
        self.tmpFileSO = self.tmpPath + 'data2.csv'
        self.crs = ''

    def run(self, lam):
        gp = self.merge_csv(self.modelFilesPath + '.hob', self.modelFilesPath + '._r', self.modelFilesPath + '._w',
                            self.modelFilesPath + '._so', lam)
        layers = self.build_layer_from_files([self.tmpFileR, self.tmpFileW, self.tmpFileSO])
        # self.decorate_layers(layers, gp)
        self.register_layers(layers)

    def get_coordinates_from_grid(self):
        layers = QgsMapLayerRegistry.instance().mapLayers()
        grid_layer = None

        for name, layer in layers.iteritems():
            if '_grid' in name:
                grid_layer = layer

        if grid_layer is None:
            raise Exception("no base grid layer present")

        self.crs = grid_layer.crs().authid()

        df = pd.DataFrame(columns=['ROW', 'COL', 'x', 'y'])

        for feat in grid_layer.getFeatures():
            geom = feat.geometry()
            p = geom.centroid()
            if not p:
                continue
            point = p.asPoint()
            x = point.x()
            y = point.y()
            row = feat.attribute('ROW')
            col = feat.attribute('COL')

            # tmp = pd.DataFrame([[row, col, x, y]], columns=['ROW', 'COL', 'X', 'Y'])
            # pd.concat([tmp, df])
            df.loc[len(df)] = [row, col, x, y]
        return df

    def merge_csv(self, hob_path, r_path, w_path, so_path, lam):

        """

        Parameters
        ----------
        hob_path
        r_path
        w_path
        so_path
        lam = 'mean, max, median'

        Returns
        -------
        groups = {'layer0': [min-max in 5 steps]}

        """
        # inde ='OBSERVATION or PRIOR NAME',
        location_data = pd.read_csv(hob_path, delim_whitespace=True, skiprows=3, header=None,
                                    names=['OBSERVATION or PRIOR NAME', 'layer', 'row', 'column', 'p1', 'p2', 'p3',
                                           'p4', 'p5'])
        location_data = location_data[['OBSERVATION or PRIOR NAME', 'row', 'column']]
        location_data.rename(columns={'row': 'ROW', 'column': 'COL'}, inplace=True)

        grid_cords = self.get_coordinates_from_grid()
        location_data = pd.merge(location_data, grid_cords, how='left', on=['ROW', 'COL'])

        def apply_lam(what, df):
            if what == 'mean':
                df.groupby(['OBSERVATION or PRIOR NAME']).mean()
            elif what == 'max':
                df.groupby(['OBSERVATION or PRIOR NAME']).max()
            elif what == 'median':
                df.groupby(['OBSERVATION or PRIOR NAME']).median()

        groups = {}

        r_data = pd.read_csv(r_path, delim_whitespace=True)
        data = pd.merge(location_data, r_data, on='OBSERVATION or PRIOR NAME')
        groups[0] = linspace(data['RESIDUAL'].min(), data['RESIDUAL'].max(), 5)
        apply_lam(lam, data)
        data.to_csv(self.tmpFileR, index=False)

        w_data = pd.read_csv(w_path, delim_whitespace=True)
        data = pd.merge(location_data, w_data, on='OBSERVATION or PRIOR NAME')
        groups[1] = linspace(data['WEIGHTED RESIDUAL'].min(), data['WEIGHTED RESIDUAL'].max(), 5)
        apply_lam(lam, data)
        data.to_csv(self.tmpFileW, index=False)

        so_data = pd.read_csv(so_path, delim_whitespace=True)
        data = pd.merge(location_data, so_data, on='OBSERVATION or PRIOR NAME')
        groups[2] = linspace(data['LEVERAGE'].min(), data['LEVERAGE'].max(), 5)
        apply_lam(lam, data)
        data.to_csv(self.tmpFileSO, index=False)

        return groups

    def build_layer_from_files(self, path):
        layers = []
        names = ['Residuals', 'Weighted Residuals', 'Leverage']

        settings = QSettings()
        # Take the "CRS for new layers" config, overwrite it while loading layers and...
        old_proj_value = settings.value("/Projections/defaultBehaviour", "prompt", type=str)
        settings.setValue("/Projections/defaultBehaviour", "useProject")

        for i, f in enumerate(path):
            url = QtCore.QUrl.fromLocalFile(f)
            url.addQueryItem('type', 'csv')
            url.addQueryItem('xField', 'x')
            url.addQueryItem('yField', 'y')

            layer = QgsVectorLayer(url.toString(), names[i], 'delimitedtext')
            crs_id = re.findall('\d+', self.crs)
            layer.setCrs(QgsCoordinateReferenceSystem(int(crs_id[0]), QgsCoordinateReferenceSystem.EpsgCrsId))
            layers.append(layer)

        settings.setValue("/Projections/defaultBehaviour", old_proj_value)
        return layers

    def decorate_layers(self, layers, groups):
        opacity = 1
        target_fields = ['RESIDUAL', 'WEIGHTED RESIDUAL', 'LEVERAGE']
        range_list = []
        for i, layer in enumerate(layers):
            target_field = target_fields[i]

            min_last = groups[i][0]
            for y, g in enumerate(groups[i]):
                # iterate over range of possible values for this layer
                if y == 0:
                    continue
                label = str(g)
                min = min_last
                max = g
                min_last = max + 0.0001
                colour = QtGui.QColor('#ffee00')
                symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
                symbol.setColor(colour)
                symbol.setAlpha(opacity)
                symbol.setSize(g)
                range = QgsRendererRangeV2(min, max, symbol, label)
                range_list.append(range)

            renderer = QgsGraduatedSymbolRendererV2('', range_list)
            renderer.setMode(QgsGraduatedSymbolRendererV2.EqualInterval)
            renderer.setClassAttribute(target_field)
            layer.setRendererV2(renderer)

    def register_layers(self, layers):
        for layer in layers:
            if layer.isValid():
                # layer.setCrs(QgsCoordinateReferenceSystem(4326, QgsCoordinateReferenceSystem.EpsgCrsId))
                QgsMapLayerRegistry.instance().addMapLayer(layer)
