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

"""
/***************************************************************************
 Visualist
                                 A QGIS plugin
 Plugin for Crime Analysts
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2019-04-15
        copyright            : (C) 2019 by Quentin Rossy
        email                : quentin.rossy@unil.ch
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
"""

__author__ = 'Quentin Rossy'
__date__ = '2019-04-15'
__copyright__ = '(C) 2019 by Quentin Rossy'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

import os, math

from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtCore import QVariant

from qgis.core import (QgsField,
                       QgsFeatureSink,
                       QgsFeature,
                       QgsGeometry,
                       QgsPoint,
                       QgsPointXY,
                       QgsRectangle,
                       QgsWkbTypes,
                       QgsProcessing,
                       QgsProcessingException,
                       QgsProcessingParameterExtent,
                       QgsProcessingParameterDistance,
                       QgsProcessingParameterFeatureSink,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterString,
                       QgsFields,
                       QgsProcessingUtils,
                       QgsSpatialIndex,
                       QgsSettings)

from .visualist_alg import VisualistAlgorithm
from .utils import renderers

class PointsInGrid(VisualistAlgorithm):
    dest_id = None  # Save a reference to the output layer id

    POINTS = 'POINTS'
    EXTENT = 'EXTENT'
    GRID = 'GRID'
    FIELD = 'FIELD'
    OUTPUT = 'OUTPUT'

    def __init__(self):
        super().__init__()


    def name(self):
        return 'gridmap'

    def randDist(self, source):
        ext = source.sourceExtent()
        A = ext.height()*ext.width()
        n = source.featureCount()
        return int(round(0.5*math.sqrt(A/n)))

    def initAlgorithm(self, config=None):
        point_source = QgsProcessingParameterFeatureSource(self.POINTS,self.tr('Points'), [QgsProcessing.TypeVectorPoint])
        self.addParameter(point_source)

        self.addParameter(QgsProcessingParameterExtent(self.EXTENT, self.tr('Grid extent')))

        grid_size = QgsProcessingParameterDistance(self.GRID,self.tr('Size of the grid'),parentParameterName=self.POINTS,defaultValue=100)
        self.addParameter(grid_size)
        # grid_size.setText(randDist(point_source))

        self.addParameter(QgsProcessingParameterString(self.FIELD,self.tr('Count field name'), defaultValue='NUMPOINTS'))
        self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,self.tr('Grid Map'), QgsProcessing.TypeVectorPolygon))

    def postProcessAlgorithm(self, context, feedback):
        """
        PostProcessing Tasks to define the Symbology
        """
        output = QgsProcessingUtils.mapLayerFromString(self.dest_id, context)
        r = renderers.MapRender(output)
        r.choropleth(self.field_name)

        return {self.OUTPUT: self.dest_id}

    def processAlgorithm(self, parameters, context, feedback):
        point_source = self.parameterAsSource(parameters, self.POINTS, context)
        if point_source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.POINTS))

        bbox = self.parameterAsExtent(parameters, self.EXTENT, context, point_source.sourceCrs())
        grid_size = self.parameterAsDouble(parameters, self.GRID, context)
        if bbox.width() < grid_size or bbox.height() < grid_size:
            raise QgsProcessingException(
                self.tr('Grid size is too large for the covered area'))

        rand_dist = self.randDist(point_source)
        feedback.pushWarning(self.tr('Expected mean distance between points if the distribution was random: {}'.format(rand_dist)))
        if grid_size < rand_dist:
            feedback.pushWarning(self.tr('You should consider to increase the size of the cell'))

        field_name = self.parameterAsString(parameters, self.FIELD, context)
        self.field_name = field_name

        fields = QgsFields()
        fields.append(QgsField("fid", QVariant.Int, "int", 9, 0))
        fields.append(QgsField(field_name, QVariant.LongLong))
        field_index = fields.lookupField(field_name)

        (sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, QgsWkbTypes.Polygon, point_source.sourceCrs(), QgsFeatureSink.RegeneratePrimaryKey)
        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        index = QgsSpatialIndex()
        for feat in point_source.getFeatures():
            index.addFeature(feat)

        current = 0
        total = 100.0/((bbox.width()/grid_size)*(bbox.height()/grid_size))
        y = bbox.yMinimum()
        while y < bbox.yMaximum():
            if feedback.isCanceled():
                break
            x = bbox.xMinimum()
            while x < bbox.xMaximum():
                if feedback.isCanceled():
                    break
                output_feature = QgsFeature()

                #Create Grid Symbol
                pt1 = QgsPointXY(x, y)
                pt2 = QgsPointXY(x + grid_size, y)
                pt3 = QgsPointXY(x + grid_size, y - grid_size)
                pt4 = QgsPointXY(x, y - grid_size)
                pt5 = QgsPointXY(x, y)
                polygon = ([[pt1, pt2, pt3, pt4, pt5]])
                output_feature.setGeometry(QgsGeometry.fromPolygonXY(polygon))

                #Count Point in Grid
                count = 0
                rect = QgsRectangle(x,y-grid_size,x+grid_size,y)
                idList = index.intersects(rect)
                count = len(idList)

                #Set attributes and add feature to sink
                attrs = [current,count]
                output_feature.setAttributes(attrs)
                sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
                # Update progressbar
                feedback.setProgress(int(current * total))
                x += grid_size
                current += 1
            y += grid_size

        return {self.OUTPUT: self.dest_id}
