"""
 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: Output to map
 
  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.buildwalls import BuildWalls
from archydro.fillsinks import FillSinks
from archydro.flowdirection_ah import FlowDirection
from archydro.flowaccumulation_ah import FlowAccumulation
from archydro.streamdefinition import StreamDefinition
from archydro.streamsegmentation import StreamSegmentation
from archydro.catchmentgriddelineation import CatchmentGridDelineation
from archydro.catchmentpolygonprocessing import CatchmentPolygonProcessing
from archydro.drainagelineprocessing import DrainageLineProcessing
from archydro.adjointcatchmentprocessing import AdjointCatchmentProcessing

# WHITEBOX WORKFLOW
# d8 vs fd8 (use different algorythims). use d8?

# 1) fix dem
# raise walls (creates walls)
# dem
# walls (vectors)
# fill burn (burns in streams)
# dem
# streams (vectors)
# breach depressions least cost, breach depressions, fill depressions (fills depressions)
# dem

# 2) extract streams >>> stream raster

# 3) stream order
# strahler stream order
# set no data value (1st order streams to no data)

# 4) create flow direction
# d8 flow pointer >>> d8 flow direction raster
# fixed dem

# 3) subbasins (catchments)
# d8 pointer
# stream raster (stream order raster)

# 4) watershed (subwatershed delineation)
# pour point shape file
# d8 pointers

# 5) raster to vector lines (converts watersheds and catchments to lines)

# doesn't create adjoint catchments (why are these even needed)

# 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.buildwalls = BuildWalls()
        self.buildwalls.bCallFromPYT = False
        self.fillsinks = FillSinks()
        self.fillsinks.bCallFromPYT = False
        self.flowdirection = FlowDirection()
        self.flowdirection.bCallFromPYT = False
        self.flowaccumulation = FlowAccumulation()
        self.flowaccumulation.bCallFromPYT = False
        self.streamdefinition = StreamDefinition()
        self.streamdefinition.bCallFromPYT = False
        self.streamsegmentation = StreamSegmentation()
        self.streamsegmentation.bCallFromPYT = False
        self.catchmentgriddelineation = CatchmentGridDelineation()
        self.catchmentgriddelineation.bCallFromPYT = False
        self.catchmentpolygonprocessing = CatchmentPolygonProcessing()
        self.catchmentpolygonprocessing.bCallFromPYT = False
        self.drainagelineprocessing = DrainageLineProcessing()
        self.drainagelineprocessing.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)

        # Build walls and fill sinks
        if self.innerwalls_fc or self.outerwalls_fc:
            _, walleddem = self.buildwalls.execute(
                [
                    self.dem_raw,
                    500,
                    0,
                    0,
                    os.path.join(self.raster_folder, f"{wbd_c.WALLEDDEM}.tif"),
                    self.outerwalls_fc,
                    self.innerwalls_fc,
                    None,
                ],
                None,
            )
            _, fil = self.fillsinks.execute(
                [walleddem, os.path.join(self.raster_folder, f"{wbd_c.FIL}.tif"), None, None, "ISSINK_NO"], None
            )
        else:
            _, fil = self.fillsinks.execute(
                [self.dem_raw, os.path.join(self.raster_folder, f"{wbd_c.FIL}.tif"), None, None, "ISSINK_NO"], None
            )
        # Flow direction
        _, fdr = self.flowdirection.execute(
            [fil, os.path.join(self.raster_folder, f"{wbd_c.FDR}.tif"), self.outerwalls_fc], None
        )
        # Flow accumulation
        _, fac = self.flowaccumulation.execute([fdr, os.path.join(self.raster_folder, f"{wbd_c.FAC}.tif"),], None)
        # Stream definition
        _, strras = self.streamdefinition.execute(
            [fac, 10000, os.path.join(self.raster_folder, f"{wbd_c.STR}.tif"),],
            None,  # number of cells to define streams
        )
        # Stream segmentation
        _, strlnk = self.streamsegmentation.execute(
            [strras, fdr, os.path.join(self.raster_folder, f"{wbd_c.STRLNK}.tif"),], None
        )
        # Catchment grid delineation
        _, cat = self.catchmentgriddelineation.execute(
            [fdr, strlnk, os.path.join(self.raster_folder, f"{wbd_c.CAT}.tif")], None
        )
        # Catchements to polygons/lines
        _, catchment = self.catchmentpolygonprocessing.execute([cat, os.path.join(self.fdpath, wbd_c.CATCHMENT)], None)
        # Drainage Line processing
        _, drainageline, _ = self.drainagelineprocessing.execute(
            [
                strlnk,
                fdr,
                os.path.join(self.fdpath, wbd_c.DRAINAGELINE),
                os.path.join(self.fdpath, wbd_c.DRAINAGELINE_FS),
            ],
            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"D:\OneDrive - DOI\WBD_Collaboration\AK\Work\IfSAR_Updates\download_0528\data_check.gdb"
    flowlines = r"D:\OneDrive - DOI\WBD_Collaboration\AK\Work\IfSAR_Updates\download_0528\19020201_prep\19020201_data.gdb\NHDFlowline_19020201"
    dem = r"D:\OneDrive - DOI\WBD_Collaboration\AK\Work\IfSAR_Updates\download_0528\19020201_prep\DEM_19020201\dem_19020201.tif"
    use_firstorder = False
    params = (flowlines, dem, None, None, use_firstorder)
    terrain_preprocessing = TerrainPreprocessing()
    terrain_preprocessing.callfrom_pyt = False
    terrain_preprocessing.execute(params, None)

