import sys
import os
import arcpy
from arcpy import env
import re
import wbd_c
import wbd_f
import wbd_params
# from wbd_automation.library import wbd_c, wbd_f, wbd_params

# Constants at top of file
#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'
wbdhu_check = "WBDHU_check"
buffer_wbd = "WBDbuffer"
intermed_lne = "WBDLinepoly"
HUDIGIT_FLAG = wbd_c.F_HUDIGIT + wbd_c.FLAG_SUFFIX
HUDIGIT_CALC= wbd_c.F_HUDIGIT + wbd_c.CALC_SUFFIX
WHITESPACE = r'^\s*$'


class HUDigit(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "HUDigit check"
        self.description = ""
        self.callfrom_pyt = True
        self.category = 'Attribution'
        self.canRunInBackground = False  # optional (I haven't been using this)

    def getParameterInfo(self):
        """Define parameter definitions"""
        # parameter data types: https://pro.arcgis.com/en/pro-app/arcpy/geoprocessing_and_python/defining-parameter-data-types-in-a-python-toolbox.htm
        # parameter info: https://pro.arcgis.com/en/pro-app/arcpy/classes/parameter.htm
        polygons_updated = arcpy.Parameter(
            displayName="Updated WBD polygon feature class",
            name="polygons_updated",
            datatype="GPFeatureLayer",
            parameterType="Required",
            direction="Input",
            multiValue=True)
        polygons_updated.filter.list = ["POLYGON"]
        polygons_reference = arcpy.Parameter(
            displayName="Reference WBD polygon feature class",
            name="polygons_reference",
            datatype="GPFeatureLayer",
            parameterType="Required",
            direction="Input",
            multiValue=True)
        polygons_reference.filter.list = ["POLYGON"]
        wbdline_updated = arcpy.Parameter(
            displayName="Updated WBDLine feature class",
            name="wbdline_updated",
            datatype="GPFeatureLayer",
            parameterType="Required",
            direction="Input",
            multiValue=True)
        wbdline_updated.filter.list = ["LINE"]
        huc_qc = arcpy.Parameter(
            displayName="HUC(s) to be QC'd",
            name="huc_qc",
            datatype="GPString",
            parameterType="Required",
            direction="Input",
            multiValue=True)
        parameters = [polygons_updated, polygons_reference, wbdline_updated, huc_qc]
        return parameters

    def get_hudigit(self, huca, hucb):     # TODO be careful with Nones (if interior lines >>> 2, problem)
        '''
        Takes two huc numbers and returns the earliest mismatched digit, rounded up to an even number
        :param huca: string or int
        :param hucb: string or int
        :return: int
        '''
        if not huca or not hucb:
            return 2
        else:
            for idx, dig in enumerate(str(huca)):
                if str(hucb)[idx] != dig:
                    # Returned digits must be even rounded up
                    if (idx + 1) % 2 == 0:
                        return idx + 1
                    else:
                        return idx + 2
                else:
                    return len(huca)

    def extract_digits(self, input_string):
        '''
        Function that takes a string (feature class name) and extracts the numbers surrounded by other letters
        :param input_string: feature class name, must be a string
        :return: int
        '''
        numbers = re.findall('\d+', input_string)
        return (numbers[0])

    def prepare_data_for_qc(self, huc_type):
        '''
        Prepares data for QC, takes the number(s) extracted from the feature class name and adds them to an expression,
        add a huc field, and calculates huc(12, 14, 16) to that field.
        :param huc_type: feature class name, must be a string
        :return: NoneType. Modifies global scope env.workspace
        '''
        huc_digit = self.extract_digits(huc_type)
        field_type = "TEXT"
        field_name = "huc"
        field_precision = 16
        arcpy.AddField_management(huc_type, field_name, field_type, field_precision, "", "", "", "NULLABLE")
        expression = '!huc' + huc_digit + '!'
        arcpy.CalculateField_management(huc_type, field_name, expression, "PYTHON_9.3")

    def create_hucheck_fc(self, input_fc):
        '''
        Creates a feature class of smallest level of HUs in the dataset
        :param input_string: feature class name, must be a string
        :return: feature class
         '''
        # Set local variables
        outLocation = env.workspace
        # combined_HUs = "WBDHU_check" delete?
        # Execute FeatureClassToFeatureClass
        arcpy.FeatureClassToFeatureClass_conversion(input_fc, outLocation, wbdhu_check)

    def select_copy_hus_to_check(self, in_feature, selection):
        '''
        select and copy input (HU14s or HU12s) in dataset to WBDHU_check, only copies features that do not overlap HU16s
        select by location feature in input that have their centroid in WBDHU_check, switch selection, and copy
        selected features from input to WBDHU_check
        :param input_string: feature class name, must be a string and selection name, must be a string
        :return: NoneType. Modifies global scope env.workspace
        '''
        # #Make a new temporary layer
        fl = arcpy.MakeFeatureLayer_management(in_feature, selection)
        # #select by location
        arcpy.SelectLayerByLocation_management(selection, 'have_their_center_in', wbdhu_check)
        arcpy.SelectLayerByLocation_management(selection, "", "", "", "SWITCH_SELECTION", "")

        # If features matched criteria write them to a new feature class
        matchcount = int(arcpy.GetCount_management(fl)[0])
        if matchcount == 0:
            print('no features matched spatial and attribute criteria')
        else:
            arcpy.Append_management(selection, wbdhu_check, "NO_TEST")
        arcpy.Delete_management(selection)

        #

    def add_buffer_to_check(self, in_feature, selection):
        '''
        Add a buffer of non-overlapping 16, 14, and 12-digits around the data to be QC'd, so the outerboundary HUDigits
        can be calculated and checked correctly.
        copies features from input (HU16s, HU14s, HU12s) that do not overlap features in WBDHU_check into a buffer fc
        :param input_string: feature class name, must be a string and selection name, must be a string
        :return: NoneType. Modifies global scope env.workspace
        '''
        # select by location feature in HU16(14, 2)Ref that intersect WBDHU_check
        arcpy.MakeFeatureLayer_management(in_feature, selection)
        arcpy.SelectLayerByLocation_management(selection, 'intersect', wbdhu_check)
        huc_digit = self.extract_digits(in_feature)
        huc_where = "huc" + huc_digit
        print(arcpy.GetCount_management(in_feature))

        # If one item in list don't join the list, let the below format handle the quotations
        if len(self.huc_qc) > 1:
            hucs = f"%' OR {huc_where} LIKE '".join(self.huc_qc)
        # Cast to list
        elif type(self.huc_qc) == str:
            hucs = self.huc_qc
        elif type(self.huc_qc) == list:
            hucs = self.huc_qc[0]
        whereClause = """{} LIKE '{}%'""".format(arcpy.AddFieldDelimiters(in_feature, huc_where), hucs)
        print(whereClause)

        # within previous selection, remove any HUC16(14, 12) that contains the HUC8 in their huc field
        arcpy.SelectLayerByAttribute_management(selection, "REMOVE_FROM_SELECTION", whereClause)
        print(arcpy.GetCount_management(selection))
        # If features matched criteria write them to a new feature class
        matchcount = int(arcpy.GetCount_management(selection)[0])
        if matchcount == 0:
            print('no features matched spatial and attribute criteria')
            print('failed to add buffer')
        else:
            arcpy.Append_management(selection, buffer_wbd, "NO_TEST")
        arcpy.Delete_management(selection)

    def add_field(self, in_line, field_name1, field_name2):
        '''
        adds two new fields to wbdlinepoly fc
        :param input_string: feature class name, must be a string and two field names, must be strings
        :return: NoneType. Modifies global scope env.workspace
        '''
        field_type = "TEXT"
        field_precision = 16
        arcpy.AddField_management(in_line, field_name1, field_type, field_precision, "", "",
                                  "", "NULLABLE")
        arcpy.AddField_management(in_line, field_name2, field_type, field_precision, "", "",
                                  "", "NULLABLE")

    def join_and_calc_field(self, in_field, join_field, field_name):
        '''
        joins WBDbuffer feature class to new line feature class (created from polygons) based on FID.
        Calculates huc from WBDHU_check feature class to new line feature class (populates HUCA and HUCB).
        :param input_string: object_id field (left or right) from new line, must be a string, objectid field from
        WBDHU_check, must be a string, field to calculate, must be a string
        :return: NoneType. Modifies global scope env.workspace
        '''
        arcpy.JoinField_management(intermed_lne, in_field, buffer_wbd, join_field)
        expression = "!huc!"
        arcpy.CalculateField_management(intermed_lne, field_name, expression, "PYTHON_9.3")

    def execute(self, parameters, messages):
        """The source code of the tool."""
        if self.callfrom_pyt:
            self.polygonfc_updated = parameters[0].valueAsText
            self.polygonfc_reference = parameters[1].valueAsText
            self.wbdlinefc = parameters[2].valueAsText
            self.huc_qc = parameters[3].valueAsText
        else:
            self.polygonfc_updated = parameters[0]
            self.polygonfc_reference = parameters[1]
            self.wbdlinefc = parameters[2]
            self.huc_qc = parameters[3]

        fields = [f.name for f in arcpy.ListFields(self.wbdlinefc)]
        if HUDIGIT_CALC not in fields:
            arcpy.AddField_management(self.wbdlinefc, HUDIGIT_CALC, 'TEXT', field_length=16)   # TODO change data type
        if HUDIGIT_FLAG not in fields:
            arcpy.AddField_management(self.wbdlinefc, HUDIGIT_FLAG, 'TEXT', field_length=wbd_c.FLAG_LENGTH)
            arcpy.CalculateField_management(self.wbdlinefc, HUDIGIT_FLAG, "'{}'".format(wbd_c.CORRECT_FLAG), 'PYTHON')
        try:
            self.polygonfc_updated = self.polygonfc_updated.split(';')
        except AttributeError:
            pass
        try:
            self.polygonfc_reference = self.polygonfc_reference.split(';')
        except AttributeError:
            pass

        try:
            self.huc_qc = self.huc_qc.split(';')
        except AttributeError:
            pass


        self.polygonfc_updated.sort()
        self.polygonfc_reference.sort()
        print(self.polygonfc_updated)
        print(self.polygonfc_reference)
        if len(self.polygonfc_updated) == 3:
            self.prepare_data_for_qc(self.polygonfc_updated[0])
            self.prepare_data_for_qc(self.polygonfc_updated[1])
            self.prepare_data_for_qc(self.polygonfc_updated[2])
            self.prepare_data_for_qc(self.polygonfc_reference[0])
            self.prepare_data_for_qc(self.polygonfc_reference[1])
            self.prepare_data_for_qc(self.polygonfc_reference[2])
            self.create_hucheck_fc(self.polygonfc_updated[2])
            self.select_copy_hus_to_check(self.polygonfc_updated[1], "selection")
            self.select_copy_hus_to_check(self.polygonfc_updated[0], "selection")
            arcpy.CreateFeatureclass_management(env.workspace, buffer_wbd, "", wbdhu_check)
            self.add_buffer_to_check(self.polygonfc_reference[2], "selection")
            self.add_buffer_to_check(self.polygonfc_reference[1], "selection")
            self.add_buffer_to_check(self.polygonfc_reference[0], "selection")
            # Add WBDHU_check (combined non-overlapping updated hu12, hu14, hu16s) to buffer
            arcpy.Append_management(wbdhu_check, buffer_wbd, "NO_TEST")
##### rest of if, elif, else not copied

# create lines from polygons using buffer fc to get huc a and huc b along outer boundary
        # buffer_wbd = "WBDbuffer"
        arcpy.PolygonToLine_management(buffer_wbd, intermed_lne)
        self.add_field(intermed_lne, "HUCA", "HUCB")

        self.join_and_calc_field("LEFT_FID", "ObjectID", "HUCA")
        arcpy.DeleteField_management(intermed_lne, "huc")
        self.join_and_calc_field("RIGHT_FID", "ObjectID", "HUCB")

        # joins original WBDLine to generated WBDLine_QC, creates a new feature class based on the join
        wbdline = "WBDLine"
        qc_lines = "WBDLine_QC"
        arcpy.SpatialJoin_analysis(wbdline, intermed_lne, qc_lines, "", "", "", "SHARE_A_LINE_SEGMENT_WITH", "", "")
        # add_field(qc_lines, "HUDigitAuto", "HUDigitProblem")

        # add new fields to WBDLine feature class
        # arcpy.AddField_management(wbdline, "HUDigit_calc", "TEXT", 2, "", "",
        #                           "", "NULLABLE")
        # arcpy.AddField_management(wbdline, "HUDigit_flag", "TEXT", 20, "", "", # TODO Uncomment
        #                           "", "NULLABLE")
        # join WBDLine_QC back to WBDLine based on object id
        print([f.name for f in arcpy.ListFields(qc_lines)])
        arcpy.JoinField_management(wbdline, "ObjectID", qc_lines, "ObjectID", ['HUCA', 'HUCB'])


        # iterating through rows tp check if original HUDigit matches generated HUDigit
        field = ""
        cursor = arcpy.UpdateCursor(wbdline)
        for row in cursor:
            oldflag = row.getValue(HUDIGIT_FLAG)
            HUCA = row.getValue("HUCA")
            HUCB = row.getValue("HUCB")
            if HUCA or HUCB:
                auto_digit = self.get_hudigit(HUCA, HUCB)
                print(HUCA, HUCB, auto_digit)
                row.setValue(HUDIGIT_CALC, auto_digit)
                if auto_digit != row.getValue(wbd_c.F_HUDIGIT):  # TODO tis will flag no data as wrong
                    row.setValue(HUDIGIT_FLAG, wbd_f.update_flag(oldflag, wbd_c.WRONG_FLAG))
            else:
                row.setValue(HUDIGIT_FLAG, wbd_f.update_flag(oldflag, wbd_c.NOTCHECKED_FLAG))
            cursor.updateRow(row)

        del cursor
        del row
        # cleans up fields in WBDLine_QC feature class
        arcpy.DeleteField_management(wbdline,
                                     ["Join_Count", "TARGET_FID", "tnmid_1", "hudigit_1", "humod_1", "linesource_1",
                                      "loaddate_1", "LEFT_FID", "RIGHT_FID", "Shape_Length_1", "HUCA", "HUCB",
                                      "tnmid_12",
                                      "metasourceid", "sourcedatadesc", "sourceoriginator", "sourcefeatureid",
                                      "loaddate_12",
                                      "noncontributingarea", "acresnoncontributing", "areasqkm", "areasqkm",
                                      "areaacres",
                                      "referencegnis_ids", "name", "states", "huc14", "hutype", "humod_12", "tohuc",
                                      "Shape_Length_12", "tnmid_12_13", "metasourceid_1", "sourcedatadesc_1",
                                      "sourceoriginator_1", "sourcefeatureid_1", "loaddate_12_13",
                                      "noncontributingareaacres_1",
                                      "noncontributingareasqkm_1", "areasqkm_1", "areaacres_1", "referencegnis_ids_1",
                                      "name_1",
                                      "states_1", "huc14_1", "hutype_1", "humod_12_13", "tohuc_1", "huc",
                                      "Shape_Length_12_13",
                                      "Shape_Area_1", "shape_Length_12_13_14", "noncontributingareaacres",
                                      "noncontributingareasqkm"])

        # deletes intermediate geodatabases
        #arcpy.Delete_management(wbdhu_check)
        #arcpy.Delete_management(buffer_wbd)
        #arcpy.Delete_management(intermed_lne)
        # arcpy.Delete_management(qc_lines)

        print("Maybe Success! Check the HUDigit_flag field in the WBDLine feature class to see if any of your HUDigits need "
              "to be updated.")
        return

if __name__ == '__main__':
    """
    Execute as standalone script.
    """
    hudigit = HUDigit()
    hudigit.callfrom_pyt = False
    arcpy.env.workspace = r'D:\WBD\EDH\HUDigitTool\WBD_02027001_HUDigitQC_12_3.gdb\WBD'
    params = (["WBDHU12", "WBDHU14", "WBDHU16"], ["HU12Ref", "HU14Ref", "HU16Ref"], "WBDLine", "02070010")
    with arcpy.EnvManager(overwriteOutput=True):
        hudigit.execute(params, None)

