"""
 Tool Name:  DownloadWBD
 Description: Downloads WBD data by HUC2 using the National Map API.
 Author: Patrick Longley (plongley@usgs.gov)
 Created: 08/11/2020
 Language: Written in python3 (arcpro). Modified to also work in python2 (arcmap).
 History:
"""

# IMPORTS
import os
import arcpy
import sys
import wbd_f
import wbd_c
import wbd_params

#PYTHON 2/3 issues
PYTHON_VERSION = sys.version_info.major
if PYTHON_VERSION == 3:
    MEMORY_FPATH = 'memory'
elif PYTHON_VERSION == 2:
    MEMORY_FPATH = 'in_memory'

class EdgeMatchPolygons(object):
    """
    Edge matches polygons in a job after linework has been completed
    """
    def __init__(self):
        """
        Initialize variables
        """
        self.label       = "Edge match polygons"
        self.description = "Edge matches polygons in a job after linework has been completed"
        self.callfrom_pyt = True
        self.category = 'Job checkout'

    def getParameterInfo(self):
        """
        Define the parameters for use in arcmap/pro.
        """
        params = [
            wbd_params.single_huc,
            wbd_params.polygonfc_updated,
            wbd_params.polygonfc_job,
            wbd_params.linefc_job,
        ]
        return params

    def updateMessages(self, params):
        """
        Modify the messages created by internal validation for each tool
        parameter.This method is called after internal validation. 
        """
        if params[0].altered:
            if not wbd_f.check_hucre([params[0].valueAsText]):
                params[0].setErrorMessage('Invalid HUC code')
        if params[1].altered and params[2].altered:
            updated_fc = params[1].valueAsText
            job_fc = params[2].valueAsText
            if not wbd_f.check_hucfieldsmatch([updated_fc, job_fc]):
                message = 'Feature classes must have matching huc fields.'
                params[1].setErrorMessage(message)
                params[2].setErrorMessage(message)

    def reproject_tomatchjob(self, updated_fc, job_fc):
        job_sr = arcpy.Describe(job_fc).spatialReference
        updated_sr = arcpy.Describe(updated_fc).spatialReference
        if job_sr != updated_sr:
            workspace = os.path.dirname(wbd_f.get_fpath(job_fc))
            if '.gdb' in workspace and not workspace.endswith('.gdb'):
                workspace = os.path.dirname(workspace)
            fpath = os.path.join(workspace, 'updatedfc_rpj')
            reprojected_fc = arcpy.Project_management(updated_fc, fpath, job_sr)    #can't overwrite in python2?
            return reprojected_fc, True
        else:
            return updated_fc, False

    def edgematch_updatedpolygons(self, polygons, lines, todelete):
        lp_fpath = os.path.join(MEMORY_FPATH, 'labelpnt_updated')
        labelpoints = arcpy.FeatureToPoint_management(polygons, lp_fpath, "INSIDE")
        if todelete:
            arcpy.Delete_management(polygons)
        # create buffer polygons (correct geometry wrong attribution)
        edgematched = arcpy.FeatureToPolygon_management(
            lines,
            os.path.join(MEMORY_FPATH, 'edgematched_polygons'),
            label_features=labelpoints
        )
        edgematched_fl = arcpy.MakeFeatureLayer_management(edgematched, 'edgematched_fl')
        arcpy.SelectLayerByLocation_management(edgematched_fl, 'CONTAINS', labelpoints) # can't select by attribute >>> select by location
        return edgematched_fl

    def select_todelete(self, fc, hucfield, huccode):
        where = """{} LIKE '{}%'""".format(arcpy.AddFieldDelimiters(fc, hucfield), huccode)
        jobpolygons_todelete = arcpy.MakeFeatureLayer_management(
            fc,
            'jobpolygons_todelete',
            where_clause=where
        )
        return jobpolygons_todelete

    def create_bufferpolygons(self, todelete_fl, polygonfc_job, line_fl):
        # create buffer label points
        jobpolygons_buffer = arcpy.MakeFeatureLayer_management(
            polygonfc_job,
            'jobpolygons_buffer',
        )
        arcpy.SelectLayerByLocation_management(
            jobpolygons_buffer,
            overlap_type='SHARE_A_LINE_SEGMENT_WITH',
            select_features=todelete_fl
        )
        arcpy.SelectLayerByLocation_management(
            jobpolygons_buffer,
            overlap_type='WITHIN',
            select_features=todelete_fl,
            selection_type='REMOVE_FROM_SELECTION'
        )
        lp_fpath = os.path.join(MEMORY_FPATH, 'labelpnt_buffer')
        labelpoints = arcpy.FeatureToPoint_management(jobpolygons_buffer, lp_fpath, "INSIDE")
        # create buffer polygons (correct geometry wrong attribution)
        buffer_polygons = arcpy.FeatureToPolygon_management(
            line_fl,
            os.path.join(MEMORY_FPATH, 'buffer_polygons'),
            label_features=labelpoints
        )
        buffer_fl = arcpy.MakeFeatureLayer_management(buffer_polygons, 'buffer_fl')
        arcpy.SelectLayerByLocation_management(buffer_fl, 'CONTAINS', labelpoints) # can't select by attribute >>> select by location
        return buffer_fl

    def create_geometrydict(self, fl):
        shape_dict = {}
        with arcpy.da.SearchCursor(fl, [wbd_c.F_TNMID, 'SHAPE@']) as cursor:
            for row in cursor:
                shape_dict[row[0]] = row[1]
        return shape_dict

    def execute(self, params, messages):
        """
        Loops through HUCS and downloads WBD data.
        """
        if self.callfrom_pyt:
            huccode = params[0].valueAsText
            polygonfc_updated = params[1].valueAsText
            polygonfc_job = params[2].valueAsText
            linefc_job = params[3].valueAsText
        else:
            huccode = params[0]
            polygonfc_updated = params[1]
            polygonfc_job = params[2]
            linefc_job = params[3]

        hucfield = wbd_f.get_hucfield(polygonfc_job)
        hudigit = int(hucfield[3:])
        where = """ {} <= {} """.format(arcpy.AddFieldDelimiters(linefc_job, wbd_c.F_HUDIGIT), hudigit)
        line_fl = arcpy.MakeFeatureLayer_management(linefc_job, 'line_fl', where_clause=where)
        polygonfc_updated, perform_delete = self.reproject_tomatchjob(polygonfc_updated, polygonfc_job)
        polygonfl_updated = self.edgematch_updatedpolygons(polygonfc_updated, line_fl, perform_delete)
        todelete_fl = self.select_todelete(polygonfc_job, hucfield, huccode)
        bufferpolygons_fl = self.create_bufferpolygons(todelete_fl, polygonfc_job, line_fl)  # correct geometry wrong attribution
        newgeometry_dict = self.create_geometrydict(bufferpolygons_fl)
        # update geometry using dictionary for buffer
        workspace = os.path.dirname(wbd_f.get_fpath(polygonfc_job))
        if '.gdb' in workspace and not workspace.endswith('.gdb'):
            workspace = os.path.dirname(workspace)
        with arcpy.da.Editor(workspace) as edit:
            with arcpy.da.UpdateCursor(polygonfc_job, [wbd_c.F_TNMID, 'SHAPE@']) as cursor:
                for row in cursor:
                    try:
                        row[1] = newgeometry_dict[row[0]]
                        cursor.updateRow(row)
                    except KeyError:
                        pass
        # replace old polygons with updated polygons
        arcpy.DeleteFeatures_management(todelete_fl)
        arcpy.Append_management(polygonfl_updated, polygonfc_job, 'NO_TEST')
        message = """
        This tool breaks the spatial index of the outputs in arcmap.
        Follow These instructions (https://support.esri.com/en/technical-article/000011225) to fix the problem in ARCCatalog.
        """
        arcpy.AddWarning(message)


if __name__ == '__main__':
    """
    Execute as standalone script.
    """
    arcpy.env.workspace = r'C:\Users\plongley\Desktop\test_edgematching\WBD704284\GDB\WBD704284.gdb'
    huccode = '19080305'
    updated_polygons = r'C:\GIS_Project\WBD\AK\Work\hu19080305\19080305_proj.gdb\Layers\HU10_albers'
    job_polygons = r'C:\Users\plongley\Desktop\test_edgematching\WBD704284\GDB\WBD704284.gdb\WBD\WBDHU10'
    job_lines = r'C:\Users\plongley\Desktop\test_edgematching\WBD704284\GDB\WBD704284.gdb\WBD\WBDLine'
    params = (
        huccode,
        updated_polygons,
        job_polygons,
        job_lines
    )
    edgematch = EdgeMatchPolygons()
    edgematch.callfrom_pyt = False
    edgematch.execute(params, None)

