"""
 Tool Name:  TerrainPreprocessing
 Description: Chains all of the preprocessing steps together.
 Author: Patrick Longley (plongley@usgs.gov)
 Created: 09/17/2020
 Language: Written in python3 (arcpro). Does not work in python2 (arcmap).
 History: 20201006 metadata, 20201006 constants, 20201006 check fields in validation (not needed)
 TODO: Doc strings
 TODO: Output to map
 TODO: Change archydro steps:
    1) create drainage line structure (ouput used for dem reconditioning only)
    2) DEM reconditioning
    3) Fill sinks/build walls
    4) Flow direction
    5) + Flow accumultaion
    6) + Stream definition, 10k cells (overwrites str raster)
    7) + Stream link (overwrites strlnk raster)
    8) Catchment grid delineation
    9) Catchement polygons
    10) + Drainage line (overwrites drianage line fc)
    11) Adjoint catchments
"""

import os
import sys
import arcpy
import wbd_params
import wbd_f
import wbd_c
# ARCHydro tools
from archydro.createdrainagelinestructures import CreateDrainageLineStructures
from archydro.demreconditioning import DEMReconditioning
from archydro.buildwalls import BuildWalls
from archydro.fillsinks import FillSinks
from archydro.flowdirection_ah import FlowDirection
from archydro.adjustflowdirectioninstreams import AdjustFlowDirectioninStreams
from archydro.catchmentgriddelineation import CatchmentGridDelineation
from archydro.catchmentpolygonprocessing import CatchmentPolygonProcessing
from archydro.adjointcatchmentprocessing import AdjointCatchmentProcessing

#PYTHON 2/3 issues
python_version = sys.version_info.major

class TerrainPreprocessing(object):
    def __init__(self):
        """
        Initialize variables
        """
        self.label       = "1) Terrain Preprocessing"
        self.description = "This tool performs all of the terrain preprocessing steps."
        self.callfrom_pyt = True
        self.category = 'Elevation derived hydrography'

    def getParameterInfo(self):
        """
        Define the parameters for use in arcmap/pro.
        """
        params = [wbd_params.nhdline_fc,
                  wbd_params.dem_in,
                  wbd_params.inner_walls,
                  wbd_params.outer_walls]
        return params

    def initialize_archydrotools(self):
        """
        Initialize archydro tools and set the tools to be called from another script/tool.
        """
        self.createdrainagelinestructures = CreateDrainageLineStructures()
        self.createdrainagelinestructures.bCallFromPYT = False
        self.demreconditioning = DEMReconditioning()
        self.demreconditioning.bCallFromPYT = False
        self.buildwalls = BuildWalls()
        self.buildwalls.bCallFromPYT = False
        self.fillsinks = FillSinks()
        self.fillsinks.bCallFromPYT = False
        self.flowdirection = FlowDirection()
        self.flowdirection.bCallFromPYT = False
        self.adjustflowdirectioninstreams = AdjustFlowDirectioninStreams()
        self.adjustflowdirectioninstreams.bCallFromPYT = False
        self.catchmentgriddelineation = CatchmentGridDelineation()
        self.catchmentgriddelineation.bCallFromPYT = False
        self.catchmentpolygonprocessing = CatchmentPolygonProcessing()
        self.catchmentpolygonprocessing.bCallFromPYT = False
        self.adjointcatchmentprocessing = AdjointCatchmentProcessing()
        self.adjointcatchmentprocessing.bCallFromPYT = False
        
    def execute(self, params, messages):
        """
        Performs archydro terrain preprocessing.
        """
        arcpy.env.parallelProcessingFactor = "100%"
        # parameters
        if self.callfrom_pyt:
            self.nhdline_fc = params[0].valueAsText
            self.dem_raw = params[1].valueAsText
            self.innerwalls_fc = params[2].valueAsText
            self.outerwalls_fc = params[3].valueAsText
        else:
            self.nhdline_fc = params[0]
            self.dem_raw = params[1]
            self.innerwalls_fc = params[2]
            self.outerwalls_fc = params[3]

        self.initialize_archydrotools()
        self.fdpath, self.dbpath, self.raster_folder = wbd_f.ah_workspaces(python_version, self.callfrom_pyt)

        # Create drainage line structures
        returns = self.createdrainagelinestructures.execute([self.dem_raw,
                                                             self.nhdline_fc,
                                                             os.path.join(self.raster_folder, wbd_c.FDRSTR),
                                                             os.path.join(self.raster_folder, wbd_c.STRLNK),
                                                             os.path.join(self.fdpath, wbd_c.DRAINAGELINE),
                                                             os.path.join(self.dbpath, wbd_c.DRAINAGELINE_FS),
                                                             os.path.join(self.fdpath, wbd_c.EDITPOINT)
                                                             ],
                                                            None)
        fdrstr = returns[1]
        strlnk = returns[2]
        drainageline = returns[3]
        # Burn in nhd
        _, agreedem = self.demreconditioning.execute([self.dem_raw,
                                                      strlnk,
                                                      2,
                                                      10,
                                                      1000,
                                                      os.path.join(self.raster_folder, wbd_c.AGREEDEM),
                                                      "NEGATIVE_NO"],
                                                     None)
        # Build walls and fill sinks
        if self.innerwalls_fc or self.outerwalls_fc:
            _, walleddem = self.buildwalls.execute([agreedem,
                                                    500,   
                                                    0,
                                                    0,
                                                    os.path.join(self.raster_folder, wbd_c.WALLEDDEM),
                                                    self.outerwalls_fc,
                                                    self.innerwalls_fc,
                                                    None],
                                                   None)
            _, fil = self.fillsinks.execute([walleddem,
                                             os.path.join(self.raster_folder, wbd_c.FIL),
                                             None,
                                             None,
                                             "ISSINK_NO"],
                                            None)
        else:
            _, fil = self.fillsinks.execute([agreedem,
                                             os.path.join(self.raster_folder, wbd_c.FIL),
                                             None,
                                             None,
                                             "ISSINK_NO"],
                                            None)
        # Flow direction
        _, fdr = self.flowdirection.execute([fil,
                                             os.path.join(self.raster_folder, wbd_c.FDR),
                                             self.outerwalls_fc],
                                            None)
        # Adjust Flow Direction in Streams
        _, fdrstradj = self.adjustflowdirectioninstreams.execute([fdr,
                                                                  fdrstr,
                                                                  os.path.join(self.raster_folder, wbd_c.FDRSTRADJ)],
                                                                 None)
        # Catchment grid delineation
        _, cat = self.catchmentgriddelineation.execute([fdrstradj,
                                                        strlnk,
                                                        os.path.join(self.raster_folder, wbd_c.CAT)],
                                                       None)
        # Append coastal catchements?
        # Catchement polygon processing
        _, catchment = self.catchmentpolygonprocessing.execute([cat,
                                                                os.path.join(self.fdpath, wbd_c.CATCHMENT)],
                                                               None)
        # Adjoint catchment processing
        self.adjointcatchmentprocessing.execute([drainageline,
                                                 catchment,
                                                 os.path.join(self.fdpath, wbd_c.ADJOINTCATCHMENT),
                                                 os.path.join(self.dbpath, wbd_c.CATCHMENT_FS),
                                                 None],
                                                None)

if __name__ == '__main__':
    """
    Execute as standalone script.
    """
    # set workspace to prevent this script from saving in the scratch database in an inconvenient location
    arcpy.env.workspace = r'C:\GIS_Project\WBD\AK\Work\hu19080305\19080305_proj.gdb'
    nhd_line = r'C:\GIS_Project\WBD\AK\Work\hu19080305\19080305_prep\19080305_data.gdb\initial_data\NHDFlowline_19080305'
    dem = r'C:\GIS_Project\WBD\AK\Work\hu19080305\19080305_prep\DEM_19080305\dem_19080305.tif'
    params = (nhd_line, dem, None, None)
    terrain_preprocessing = TerrainPreprocessing()
    terrain_preprocessing.callfrom_pyt = False
    terrain_preprocessing.execute(params, None)



