# codes to implement the class in grm.dll
import ctypes
import ctypes.util
import enum
import os
import sys
from ctypes import Structure

dll_path = (
    os.path.dirname(os.path.realpath(__file__)).replace("lib", "") + "\DLL\GRM.dll"
)
gdl_path = ctypes.util.find_library(dll_path)

if not gdl_path:
    print("Unable to find the specified library.")
    sys.exit()
try:
    gdl = ctypes.CDLL(gdl_path)
except OSError:
    print("Unable to load the system C library")
    sys.exit()


class unSaturatedKType(enum.Enum):
    Constant = 0
    Linear = 1
    Exponential = 2
    usKNone = 3


class PETmethod(enum.Enum):
    PenmanMonteith = 1
    BlaneyCriddle = 2
    Hamon = 3
    PriestleyTaylor = 4
    Hargreaves = 5
    JensenHaise = 6
    Turc = 7
    Constant = 8
    UserData = 9
    petNone = 10


class SnowMeltMethod(enum.Enum):
    Anderson = 1
    Constant = 8
    UserData = 9
    smNone = 10


class InterceptionMethod(enum.Enum):
    LAIRatio = 1
    Constant = 8
    UserData = 9
    icNone = 10


class swsParameters(Structure):
    _fields_ = [
        ("wsid", ctypes.c_int),
        ("iniSaturation", ctypes.c_double),
        ("minSlopeOF", ctypes.c_double),
        ("unSatKType", ctypes.c_int),
        ("coefUnsaturatedK", ctypes.c_double),
        ("minSlopeChBed", ctypes.c_double),
        ("minChBaseWidth", ctypes.c_double),
        ("chRoughness", ctypes.c_double),
        ("dryStreamOrder", ctypes.c_int),
        ("iniFlow", ctypes.c_double),
        ("ccLCRoughness", ctypes.c_double),
        ("ccPorosity", ctypes.c_double),
        ("ccWFSuctionHead", ctypes.c_double),
        ("ccHydraulicK", ctypes.c_double),
        ("ccSoilDepth", ctypes.c_double),
        ("interceptMethod", ctypes.c_int),
        ("potentialETMethod", ctypes.c_int),
        ("etCoeff", ctypes.c_double),
        ("snowMeltMethod", ctypes.c_int),
        ("tempSnowRain", ctypes.c_double),
        ("smeltingTemp", ctypes.c_double),
        ("snowCovRatio", ctypes.c_double),
        ("smeltCoef", ctypes.c_double),
        ("userSet", ctypes.c_int),
    ]


class grmWSinfo(object):
    def __init__(
        self,
        fpnGMP_OR_fdirType,
        fpnDomain="",
        fpnSlope="",
        fpnFdir="",
        fpnFac="",
        fpnStream="",
        fpnLandCover="",
        fpnSoilTexture="",
        fpnSoilDepth="",
        fpnIniSoilSaturationRatio="",
        pfnIniChannelFlow="",
        fpnChannelWidth="",
    ):
        gdl.grmWSinfo_new_inputFiles.argtypes = [
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
        ]

        gdl.grmWSinfo_new_inputFiles.restype = ctypes.c_void_p
        gdl.grmWSinfo_new_gmpFile.argtypes = [ctypes.c_char_p]
        gdl.grmWSinfo_new_gmpFile.restype = ctypes.c_void_p
        gdl.isInWatershedArea.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
        gdl.isInWatershedArea.restype = ctypes.c_bool
        gdl.upStreamWSIDs.argtypes = [ctypes.c_void_p, ctypes.c_int]
        gdl.upStreamWSIDs.restype = ctypes.POINTER(ctypes.c_int)
        gdl.upStreamWSCount.argtypes = [ctypes.c_void_p, ctypes.c_int]
        gdl.upStreamWSCount.restype = ctypes.c_int
        gdl.downStreamWSIDs.argtypes = [ctypes.c_void_p, ctypes.c_int]
        gdl.downStreamWSIDs.restype = ctypes.POINTER(ctypes.c_int)
        gdl.downStreamWSCount.argtypes = [ctypes.c_void_p, ctypes.c_int]
        gdl.downStreamWSCount.restype = ctypes.c_int
        gdl.watershedID.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
        gdl.watershedID.restype = ctypes.c_int
        gdl.flowDirection.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
        gdl.flowDirection.restype = ctypes.c_char_p
        gdl.flowAccumulation.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
        gdl.flowAccumulation.restype = ctypes.c_int
        gdl.slope.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
        gdl.slope.restype = ctypes.c_double
        gdl.streamValue.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
        gdl.streamValue.restype = ctypes.c_int
        gdl.cellFlowTypeACell.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
        gdl.cellFlowTypeACell.restype = ctypes.c_char_p
        gdl.landCoverValue.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
        gdl.landCoverValue.restype = ctypes.c_int
        gdl.soilTextureValue.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
        gdl.soilTextureValue.restype = ctypes.c_int
        gdl.soilDepthValue.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
        gdl.soilDepthValue.restype = ctypes.c_int
        gdl.cellCountInUpstreamArea.argtypes = [
            ctypes.c_void_p,
            ctypes.c_int,
            ctypes.c_int,
        ]
        gdl.cellCountInUpstreamArea.restype = ctypes.c_int
        gdl.allCellsInUpstreamArea.argtypes = [
            ctypes.c_void_p,
            ctypes.c_int,
            ctypes.c_int,
        ]
        gdl.allCellsInUpstreamArea.restype = ctypes.POINTER(ctypes.c_char_p)
        gdl.setOneSWSParsAndUpdateAllSWSUsingNetwork.argtypes = [
            ctypes.c_void_p,
            ctypes.c_int,
            ctypes.c_double,
            ctypes.c_double,
            ctypes.c_int,
            ctypes.c_double,
            ctypes.c_double,
            ctypes.c_double,
            ctypes.c_double,
            ctypes.c_int,
            ctypes.c_double,
            ctypes.c_double,
            ctypes.c_double,
            ctypes.c_double,
            ctypes.c_double,
            ctypes.c_int,
            ctypes.c_int,
            ctypes.c_double,
            ctypes.c_int,
            ctypes.c_double,
            ctypes.c_double,
            ctypes.c_double,
            ctypes.c_double,
            ctypes.c_double,
        ]

        gdl.setOneSWSParsAndUpdateAllSWSUsingNetwork.restype = ctypes.c_bool
        gdl.updateAllSubWatershedParametersUsingNetwork.argtypes = [ctypes.c_void_p]
        gdl.updateAllSubWatershedParametersUsingNetwork.restype = None
        gdl.subwatershedPars.argtypes = [ctypes.c_void_p, ctypes.c_int]
        gdl.subwatershedPars.restype = swsParameters
        gdl.removeUserParametersSetting.argtypes = [ctypes.c_void_p, ctypes.c_int]
        gdl.removeUserParametersSetting.restype = ctypes.c_bool
        gdl.facMaxCellxCol.argtypes = [ctypes.c_void_p]
        gdl.facMaxCellxCol.restype = ctypes.c_int
        gdl.facMaxCellyRow.argtypes = [ctypes.c_void_p]
        gdl.facMaxCellyRow.restype = ctypes.c_int
        gdl.WSIDsAll.argtypes = [ctypes.c_void_p]
        gdl.WSIDsAll.restype = ctypes.POINTER(ctypes.c_int)
        gdl.WScount.argtypes = [ctypes.c_void_p]
        gdl.WScount.restype = ctypes.c_int
        gdl.mostDownStreamWSIDs.argtypes = [ctypes.c_void_p]
        gdl.mostDownStreamWSIDs.restype = ctypes.POINTER(ctypes.c_int)
        gdl.mostDownStreamWSCount.argtypes = [ctypes.c_void_p]
        gdl.mostDownStreamWSCount.restype = ctypes.c_int
        gdl.cellCountInWatershed.argtypes = [ctypes.c_void_p]
        gdl.cellCountInWatershed.restype = ctypes.c_int
        gdl.cellSize.argtypes = [ctypes.c_void_p]
        gdl.cellSize.restype = ctypes.c_double
        gdl.FDtype.argtypes = [ctypes.c_void_p]
        gdl.FDtype.restype = ctypes.c_char_p
        bfpnGmpORfdirType = fpnGMP_OR_fdirType.encode("utf-8")

        if fpnDomain == "":
            self.obj = gdl.grmWSinfo_new_gmpFile(bfpnGmpORfdirType)
        else:
            bfpnDomain = fpnDomain.encode("utf-8")
            bfpnSlope = fpnSlope.encode("utf-8")
            bfpnFdir = fpnFdir.encode("utf-8")
            bfpnFac = fpnFac.encode("utf-8")
            bfpnStream = fpnStream.encode("utf-8")
            bfpnLandCover = fpnLandCover.encode("utf-8")
            bfpnSoilTexture = fpnSoilTexture.encode("utf-8")
            bfpnSoilDepth = fpnSoilDepth.encode("utf-8")
            bfpnIniSoilSaturationRatio = fpnIniSoilSaturationRatio.encode("utf-8")
            bpfnIniChannelFlow = pfnIniChannelFlow.encode("utf-8")
            bfpnChannelWidth = fpnChannelWidth.encode("utf-8")
            self.obj = gdl.grmWSinfo_new_inputFiles(
                bfpnGmpORfdirType,
                bfpnDomain,
                bfpnSlope,
                bfpnFdir,
                bfpnFac,
                bfpnStream,
                bfpnLandCover,
                bfpnSoilTexture,
                bfpnSoilDepth,
                bfpnIniSoilSaturationRatio,
                bpfnIniChannelFlow,
                bfpnChannelWidth,
            )

        self.facMaxCellxCol = gdl.facMaxCellxCol(self.obj)
        self.facMaxCellyRow = gdl.facMaxCellyRow(self.obj)
        self.WSIDsAll = gdl.WSIDsAll(self.obj)
        self.WScount = gdl.WScount(self.obj)
        self.mostDownStreamWSIDs = gdl.mostDownStreamWSIDs(self.obj)
        self.mostDownStreamWSCount = gdl.mostDownStreamWSCount(self.obj)
        self.cellCountInWatershed = gdl.cellCountInWatershed(self.obj)
        self.cellSize = gdl.cellSize(self.obj)
        self.fdType_fromGMP = gdl.FDtype(self.obj)

    def isInWatershedArea(self, colXAryidx, rowYAryidx):
        return gdl.isInWatershedArea(self.obj, colXAryidx, rowYAryidx)

    def upStreamWSIDs(self, currentWSID):
        return gdl.upStreamWSIDs(self.obj, currentWSID)

    def upStreamWSCount(self, currentWSID):
        return gdl.upStreamWSCount(self.obj, currentWSID)

    def downStreamWSIDs(self, currentWSID):
        return gdl.downStreamWSIDs(self.obj, currentWSID)

    def downStreamWSCount(self, currentWSID):
        return gdl.downStreamWSCount(self.obj, currentWSID)

    def watershedID(self, colXAryidx, rowYAryidx):
        return gdl.watershedID(self.obj, colXAryidx, rowYAryidx)

    def flowDirection(self, colXAryidx, rowYAryidx):
        return gdl.flowDirection(self.obj, colXAryidx, rowYAryidx)

    def flowAccumulation(self, colXAryidx, rowYAryidx):
        return gdl.flowAccumulation(self.obj, colXAryidx, rowYAryidx)

    def slope(self, colXAryidx, rowYAryidx):
        return gdl.slope(self.obj, colXAryidx, rowYAryidx)

    def streamValue(self, colXAryidx, rowYAryidx):
        return gdl.streamValue(self.obj, colXAryidx, rowYAryidx)

    def cellFlowTypeACell(self, colXAryidx, rowYAryidx):
        return gdl.cellFlowTypeACell(self.obj, colXAryidx, rowYAryidx)

    def landCoverValue(self, colXAryidx, rowYAryidx):
        return gdl.landCoverValue(self.obj, colXAryidx, rowYAryidx)

    def soilTextureValue(self, colXAryidx, rowYAryidx):
        return gdl.soilTextureValue(self.obj, colXAryidx, rowYAryidx)

    def soilDepthValue(self, colXAryidx, rowYAryidx):
        return gdl.soilDepthValue(self.obj, colXAryidx, rowYAryidx)

    def allCellsInUpstreamArea(self, colXAryidx, rowYAryidx):
        return gdl.allCellsInUpstreamArea(self.obj, colXAryidx, rowYAryidx)

    def cellCountInUpstreamArea(self, colXAryidx, rowYAryidx):
        return gdl.cellCountInUpstreamArea(self.obj, colXAryidx, rowYAryidx)

    def setOneSWSParsAndUpdateAllSWSUsingNetwork(
        self,
        wsid,
        iniSat,
        minSlopeLandSurface,
        unSKType,
        coefUnsK,
        minSlopeChannel,
        minChannelBaseWidth,
        roughnessChannel,
        dryStreamOrder,
        ccLCRoughness,
        ccPorosity,
        ccWFSuctionHead,
        ccSoilHydraulicCond,
        ccSoilDepth,  # 이 변수 위치 변경됨. 여기로 이동. 2023.03.02
        # ==========추가됨. 2023.03.02===
        interceptMethod,
        potentialETMethod,
        etCoeff,
        snowMeltMethod,
        tempSnowRain,
        smeltingTemp,
        snowCovRatio,
        smeltCoef,
        # =========================
        iniFlow=0.0,
    ):
        return gdl.setOneSWSParsAndUpdateAllSWSUsingNetwork(
            self.obj,
            wsid,
            iniSat,
            minSlopeLandSurface,
            unSKType,
            coefUnsK,
            minSlopeChannel,
            minChannelBaseWidth,
            roughnessChannel,
            dryStreamOrder,
            ccLCRoughness,
            ccPorosity,
            ccWFSuctionHead,
            ccSoilHydraulicCond,
            ccSoilDepth,  # 이 변수 위치 변경됨. 여기로 이동. 2023.03.02
            # ==========추가됨. 2023.03.02===
            interceptMethod,
            potentialETMethod,
            etCoeff,
            snowMeltMethod,
            tempSnowRain,
            smeltingTemp,
            snowCovRatio,
            smeltCoef,
            # =========================
            iniFlow,
        )

    def updateAllSubWatershedParametersUsingNetwork(self):
        return gdl.updateAllSubWatershedParametersUsingNetwork(self.obj)

    def subwatershedPars(self, wsid):
        return gdl.subwatershedPars(self.obj, wsid)

    def removeUserParametersSetting(self, wsid):
        return gdl.removeUserParametersSetting(self.obj, wsid)
