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

"""
/***************************************************************************
 IMSGS
                                 A QGIS plugin
 This plugin generates grid based on IMSGS standart.
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2023-10-25
        copyright            : (C) 2023 by Irwanto, Rania Altairatri Evelina Brawijaya, Mutia Hendriani Putri, Muhammad Usman Alshaadiq
        email                : 15120068@mahasiswa.itb.ac.id
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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__ = 'Irwanto, Rania Altairatri Evelina Brawijaya, Mutia Hendriani Putri, Muhammad Usman Alshaadiq'
__date__ = '2023-10-25'
__copyright__ = '(C) 2023 by Irwanto, Rania Altairatri Evelina Brawijaya, Mutia Hendriani Putri, Muhammad Usman Alshaadiq'

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

__revision__ = '$Format:%H$'

from qgis.core import (QgsProcessing,
                       QgsFeatureSink,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterFeatureSink,
                       QgsProcessingParameterString,
                       QgsProcessingParameterEnum,
                       QgsCoordinateTransform,
                       QgsCoordinateReferenceSystem,
                       QgsProject,
                       QgsRectangle,
                       QgsProcessingParameterBoolean,
                       QgsFields,
                       QgsField,
                       QgsWkbTypes,
                       QgsProcessingMultiStepFeedback
                       )
from qgis.PyQt.QtCore import *
import os
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtCore import QUrl
import processing
import math


class IMSGSAlgorithm(QgsProcessingAlgorithm):

    OUTPUT = 'OUTPUT'
    INPUT = 'INPUT'
    boundingbox = 'STRING_PARAM'
    selected_values = 'RADIO_PARAM'
    extract = 'Extract Grid by Vector Layer'

    def name(self):
        return 'Generate Grid'

    def displayName(self):
        return self.tr(self.name())

    def group(self):
        return self.tr(self.groupId())

    def groupId(self):
        return 'a. Grid'

    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        return IMSGSAlgorithm()
    
    def icon(self):
        return QIcon(os.path.join(os.path.dirname(__file__), 'icons/generategrid.png'))
    
    def helpUrl(self):
        file = os.path.dirname(__file__) + '/en.html'
        if not os.path.exists(file):
            return ''
        return QUrl.fromLocalFile(file).toString(QUrl.FullyEncoded)    

    def initAlgorithm(self, config):
# ====================  Parameter =====================================  

        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.INPUT,
                self.tr('Input Vector Layer'),
                [QgsProcessing.TypeVectorPolygon],
                optional=True,
            )
        )

        # bounding box parameters
        self.addParameter(
            QgsProcessingParameterString(
                self.boundingbox,  # The parameter name
                self.tr('Geometry Extent [WGS84]'),  # The parameter label
                defaultValue='λmin, λmax, φmin, φmax',  # Optional: Set a default value
                optional = True
            )
        )

        # Add a radio button parameter
        radio_options = ["30' x 30'", "15' x 15'", "7'30\" x 7'30\"", "2'30\" x 2'30\"",'30" x 30"', '5" x 5"' ]
        
        self.addParameter(
            QgsProcessingParameterEnum(
                self.selected_values,  # The parameter name
                self.tr('Grid Resolution'),  # The parameter label
                options=radio_options,
                defaultValue="30' x 30'"  # Optional: Set a default value
            )
        )

        self.addParameter(
            QgsProcessingParameterBoolean(
                'Extract Grid by Vector Layer',
                'Extract Grid by Vector Layer',
                False,
                optional=True
            )
        )

        # Output parameters
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.OUTPUT,
                self.tr('Output layer'),
            )
        )


    def processAlgorithm(self, parameters, context, model_feedback):

# ==================== Define Parameter =====================================   
        source = self.parameterAsSource(parameters, self.INPUT, context) # input AOI parameter
        extent_param = self.parameterAsString(parameters, self.boundingbox,context) # bounding box parameter
        extract  = self.parameterAsBoolean(parameters, self.extract,context) # extract parameter

        #initialize progress bar
        feedback = QgsProcessingMultiStepFeedback(2, model_feedback)


        grid_repp = processing.run("native:reprojectlayer",
                                        {'INPUT':parameters['INPUT'],
                                        'TARGET_CRS':QgsCoordinateReferenceSystem('EPSG:4326'),
                                        'OUTPUT':QgsProcessing.TEMPORARY_OUTPUT})
        
        grid_rep = processing.run("native:fixgeometries", 
                                {'INPUT':grid_repp['OUTPUT'],
                                 'METHOD':0,
                                 'OUTPUT':'TEMPORARY_OUTPUT'})
        
        if source is not None :

            # Get the source extent
            source_extent = source.sourceExtent()

            # convert bounding box from dict to QgsRectangle
            bbox_rect = QgsRectangle(source_extent.xMinimum(), source_extent.yMinimum(), source_extent.xMaximum(), source_extent.yMaximum())

            # Transform the source extent to EPSG 4326 (WGS 84)
            crs_transform = QgsCoordinateTransform(source.sourceCrs(), QgsCoordinateReferenceSystem(4326), QgsProject.instance())
            bbox_4326 = crs_transform.transformBoundingBox(bbox_rect)
            bbox = bbox_4326

            # Extract minimum and maximum coordinates from QgsRectangle
            extent_min_x = bbox.xMinimum()
            extent_max_x = bbox.xMaximum()
            extent_min_y = bbox.yMinimum()
            extent_max_y = bbox.yMaximum()
            extent = [extent_min_x, extent_max_x, extent_min_y, extent_max_y]

        else : 
            # Convert extent_param to a list of float values
            extent_param = extent_param.split(',')
            extent_param = [float(x) for x in extent_param]
            extent = extent_param  

        # selected values parameter
        selected_value = self.parameterAsString(parameters, self.selected_values, context)

        radio = []
        if selected_value == f'0' :
            radio = 1800/3600
        elif selected_value == f'1' :
            radio = 900/3600 
        elif selected_value == f'2' :
            radio = 450/3600
        elif selected_value == f'3' :
            radio = 150/3600
        elif selected_value == f'4' :
            radio = 30/3600
        elif selected_value == f'5' :
            radio = 5/3600
    

 # ==================== algoritm =====================================  
        feedback.setProgressText('Create Grid ...')
        # initialize

        #originSGSRI
        ori_latmin= float(-15)
        ori_lonmin= float(90) 

        # Calculate col_start, col_end, row_start, and row_end
        col_start = math.floor((extent[0] - ori_lonmin) / radio) - 1
        col_end = math.floor((extent[1] - ori_lonmin) / radio) + 2
        row_start = math.floor((extent[2] - ori_latmin) / radio) - 1
        row_end = math.floor((extent[3] - ori_latmin) / radio) + 2

        # define the origin point 
        xmin = ori_lonmin+(float(col_start)*float(radio))
        xmax = ori_lonmin+(float(col_end)*float(radio))
        ymin = ori_latmin+(float(row_start)*float(radio))
        ymax = ori_latmin+(float(row_end)*float(radio))

        # create grid
        grid_options = {'TYPE':2,
                        'EXTENT':f'{xmin},{xmax},{ymin},{ymax}[EPSG:4326]',
                        'HSPACING':radio,
                        'VSPACING':radio,
                        'HOVERLAY':0,
                        'VOVERLAY':0,
                        'CRS':QgsCoordinateReferenceSystem('EPSG:4326'),
                        'OUTPUT':QgsProcessing.TEMPORARY_OUTPUT
                        }

        result_grid = processing.run("native:creategrid", grid_options)

        # progress bar step 1
        feedback.setCurrentStep(1)
        if feedback.isCanceled():
            return {}
        
        #chect extract feedback
        if extract :
            # extract by location
            feature_1 = result_grid['OUTPUT']
            extract_options = {
                'INPUT': feature_1,  # path grid shape file
                'PREDICATE': [0],
                'INTERSECT': grid_rep['OUTPUT'],  # aoi path shape file
                'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT,
            }
            # processing extract
            result_12 = processing.run("native:extractbylocation", extract_options)
            features_12 = result_12['OUTPUT']
        else :
            features_12 = result_grid['OUTPUT']


# ==================== output parameter =====================================  

        feedback.setProgressText('Calculate ID For GRID ...')

        # Add all fields from the grid to the output layer
        fields = QgsFields()
        fields.append(QgsField('IMGSID', QVariant.String, '', 50))

        # Output parameter
        output_crs = QgsCoordinateReferenceSystem('EPSG:4326')
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, output_crs)

        # sink feature
        for feat_1 in features_12 .getFeatures():
            x = feat_1.geometry().centroid().asPoint().x()
            y = feat_1.geometry().centroid().asPoint().y()
            Grid_id = calculate_ID(x,y,radio)
            feat_1['ID'] = Grid_id     
            sink.addFeature(feat_1, QgsFeatureSink.FastInsert)
        
        feedback.setCurrentStep(2)
        if feedback.isCanceled():
            return {}

        return {self.OUTPUT: dest_id}

#algorithm to calculate IMGS
def calculate_ID (x_cent,y_cent,selectedvalues):
    #create Indonesian multiselected_values[0]ale grid system's ID for 5"x5"
    #30'x30' (KKBBC)
    K=int(((x_cent-90)/1.5)+1)
    B=int(((y_cent-(-15))/1)+1)
    m=(x_cent-((K-1)*1.5+90))*60
    M=int((m/30)+1)
    n=(y_cent-((B-1)*1-15))*60                
    N=int((n/30)+1)
    C=M+((N-1)*3)

    #15'x15' (KKBBCD)
    o=(m-((M-1)*30))
    p=(n-((N-1)*30))
    O=int((o/15)+1)
    P=int((p/15)+1)
    D=O+((P-1)*2)

    #7'30"x7'30" (KKBBCDE)
    q=(o-((O-1)*15))
    r=(p-((P-1)*15))
    Q=int((q/7.5)+1)
    R=int((r/7.5)+1)
    E=Q+((R-1)*2)

    #2'30"x2'30" (KKBBCDEF)
    s=(q-((Q-1)*7.5))
    t=(r-((R-1)*7.5))
    S=int((s/2.5)+1)
    T=int((t/2.5)+1)
    F=S+((T-1)*3)

    #30"x30" (KKBBCDEFGG)
    i30=(s-((S-1)*2.5))
    n30=(t-((T-1)*2.5))
    I30=int((i30/0.5)+1)
    N30=int((n30/0.5)+1)
    G=I30+((N30-1)*5)

    #5"x5" (KKBBCDEFGGHH)
    u=(i30-((I30-1)*0.5))*60
    v=(n30-((N30-1)*0.5))*60
    U=int((u/5)+1)
    V=int((v/5)+1)
    H=U+((V-1)*6)

    #convert to string
    #KK
    if K>=10:
        k=str(K)
    else:
        k='0'+str(K)
        
    #BB
    if B>=10:
        b=str(B)
    else:
        b='0'+str(B)

    c=str(C) #C
    d=str(D) #D
    e=str(E) #E
    f=str(F) #F

    #GG
    if G>=10:
        g=str(G)
    else:
        g='0'+str(G)

    #HH
    if H>=10:
        h=str(H)
    else:
        h='0'+str(H)
    #combining ID
    if selectedvalues==5/3600 :
        id=k+b+c+d+e+f+g+h
    elif selectedvalues==30/3600:
        id=k+b+c+d+e+f+g
    elif selectedvalues==150/3600:
        id=k+b+c+d+e+f
    elif selectedvalues==450/3600:
        id=k+b+c+d+e
    elif selectedvalues==900/3600:
        id=k+b+c+d
    elif selectedvalues==1800/3600:
        id=k+b+c
    
    return id