''' Muster 8 Generic script template class '''

''' Import Muster base modules '''
import MClientAPI
import MTemplateAPI

''' Import Python base lib modules '''
import os.path
import re

''' Get the template manager singleton '''
manager = MTemplateAPI.MManager.getUniqueClass()

''' Define a new class derived from MTemplateAPI.MTemplate base class , override required methods '''


class ShellTemplate(MTemplateAPI.MTemplate):
    def __init__(self):
        MTemplateAPI.MTemplate.__init__(self)
        self.setID(2008)
        self.setName("Denoise_Noice")
        self.setDescription("Denoise with Noice")
        self.setTemplateLogic(MTemplateAPI.kTemplateMultiframe)
        self.setDefaultPriority(1)
        self.setDefaultPools("arnold_16thread")
        self.setDefaultExcludedPools("")
        self.setMaximumLicenses(0)
        self.setEnableAdditionalFlagsField(1)

        ''' Submission form items allocation '''
        NoiceBin = MTemplateAPI.MTemplateItemFolder("NOICEBIN", "Noice Bin Path", "Specifies the Noice bin Path", "", 0,
                                                    1, 1)
        patchRadius = MTemplateAPI.MTemplateItemInteger("PATCHRADIUS", "PatchRadius", "Specify the patch radius", 3, 0,
                                                        0, 1)
        searchRadius = MTemplateAPI.MTemplateItemInteger("SEARCHRADIUS", "PatchRadius", "Specify the patch radius", 9,
                                                         0, 0, 1)
        variance = MTemplateAPI.MTemplateItemDouble("VARIANCE", "Variance", "Specifies the variance", 0.25, 0, 0, 1)
        lightAovString = MTemplateAPI.MTemplateItemString("LIGHTAOVSTRING", "Light AOV String",
                                                          "Specifies the AOVs to be denoised. Syntax: -aov aovname1 -aov aovname2 -aov aovname3",
                                                          "-aov rgba", 0, 0, 1)
        temporalFrames = MTemplateAPI.MTemplateItemInteger("TEMPORALFRAMES", "Temporal Frames",
                                                           "Specifies the temporal frames", 0, 0, 0, 1)
        denoiseThreads = MTemplateAPI.MTemplateItemInteger("DENOISETHREADS", "Threads", "Specify the denoise threads",
                                                           -2, 1, 0, 1)
        startFrame = MTemplateAPI.MTemplateItemInteger("NOICESTARTFRAME", "Start frame",
                                                       "Specifies the starting frame for the rendering job", 1001, 0, 0,
                                                       1)
        endFrame = MTemplateAPI.MTemplateItemInteger("NOICEENDFRAME", "End frame",
                                                     "Specifies the ending frame for the rendering job", 1010, 0, 0, 1)
        inputFile = MTemplateAPI.MTemplateItemFile("INPUTFILE", "Input File", "Specifies the File to be denoised", "",
                                                   0, 1, 1, "*.exr")
        outputFile = MTemplateAPI.MTemplateItemFile("OUTPUTFILE", "Output File", "Specifies the output File", "", 0, 1,
                                                    1, "*.exr")

        self.addSubmissionItem(NoiceBin)
        self.addSubmissionItem(patchRadius)
        self.addSubmissionItem(searchRadius)
        self.addSubmissionItem(variance)
        self.addSubmissionItem(lightAovString)
        self.addSubmissionItem(temporalFrames)
        self.addSubmissionItem(denoiseThreads)
        self.addSubmissionItem(startFrame)
        self.addSubmissionItem(endFrame)
        self.addSubmissionItem(inputFile)
        self.addSubmissionItem(outputFile)

        ''' items mapping to Muster default tags '''
        self.addMapping("INPUTFILE", "job_file")
        self.addMapping("NOICESTARTFRAME", "start_frame")
        self.addMapping("NOICEENDFRAME", "end_frame")

        ''' Windows support '''
        self.platformWindows.setPlatformEnabled(1)
        self.platformWindows.setEnableErrorCheck(1)
        self.platformWindows.setEnabledByDefault(1)
        self.platformWindows.setDetectionLogic(MTemplateAPI.kProcessChild)
        #self.platformWindows.setDetectionLogic(MTemplateAPI.kProcessDirect)
        self.platformWindows.setDetectionLogicProcessName("noice.exe")

        ''' OSX support '''
        self.platformOSX.setPlatformEnabled(0)
        self.platformOSX.setEnableErrorCheck(1)
        self.platformOSX.setEnabledByDefault(1)
        self.platformOSX.setDetectionLogic(MTemplateAPI.kProcessDirect)

        applicationPath = MTemplateAPI.MTemplateItemFile("SHELLCMD", "Script interpreter executable",
                                                         "Specifies the OS X script executable", "/bin/sh", 0, 0, 1,
                                                         "*.exe")
        self.platformOSX.addClientConfigurationItem(applicationPath)

        ''' Linux support '''
        self.platformLinux.setPlatformEnabled(0)
        self.platformLinux.setEnableErrorCheck(1)
        self.platformLinux.setEnabledByDefault(1)
        self.platformLinux.setDetectionLogic(MTemplateAPI.kProcessDirect)

        applicationPath = MTemplateAPI.MTemplateItemFile("SHELLCMD", "Script interpreter executable",
                                                         "Specifies the Linux script executable", "/bin/sh", 0, 0, 1,
                                                         "*.exe")
        self.platformLinux.addClientConfigurationItem(applicationPath)

    ''' virtual functions overrides '''

    def onBuildCommandLine(self, platform, job, chunk, clientTemplatePreferences, instanceNum):
        # sequence with sequence string bla.1001.exr
        fileIn = job.attributeGetString(manager.resolveMappingToJob(self.getID(), "INPUTFILE"))
        fileOut = job.attributeGetString(manager.resolveMappingToJob(self.getID(), "OUTPUTFILE"))

        # regex
        patternSeq = re.compile(r'\.\d\d\d+\.')

        # extract frame substring
        frame = patternSeq.findall(fileIn)
        # if there are more matches due to non-conform naming
        frame = frame[-1]
        chunkFrame = "." + str(int(chunk.getStartFrame())) + "."

        # replace padding with the startFrame of the chunk
        fileIn = fileIn.replace(frame, chunkFrame)
        fileOut = fileOut.replace(frame, chunkFrame)

        renderCmd = ""
        if platform == MClientAPI.kPlatformWindows:
            renderCmd += "/c "
            renderCmd += "\""
            renderCmd += job.attributeGetString("NOICEBIN") + "noice.exe"
            renderCmd += " -patchradius %s" % job.attributeGetInt("PATCHRADIUS")
            renderCmd += " -searchradius %s" % job.attributeGetInt("SEARCHRADIUS")
            renderCmd += " -variance %s" % job.attributeGetFloat("VARIANCE")
            renderCmd += " " + job.attributeGetString("LIGHTAOVSTRING")  # '-aov aovname1 -aov aovname2'
            renderCmd += " -i " + fileIn
            renderCmd += " -t " + job.attributeGetString("DENOISETHREADS")
            renderCmd += " -ef %s" % job.attributeGetInt("TEMPORALFRAMES")
            renderCmd += " -output " + fileOut
            renderCmd += "\""

            addFlags = job.attributeGetString("ADD_FLAGS")
            if len(addFlags) > 0:
                renderCmd += " " + addFlags
        return renderCmd

    def onCheckLog(self, job, chunk, clientTemplatePreferences, log, warnings, errors, silencedWarnings,
                   silencedErrors):
        ## WARNINGS
        noWarnings = []
        foundset = []

        for line in log.splitlines():
            if line.lower().find("warning") != -1:
                foundset.append(line)
        # remove no warning strings
        result = [x for x in foundset if not any(y in x for y in noWarnings)]
        if len(result) != 0:
            return MTemplateAPI.MTemplateError(2, "Warning keyword found",
                                               MTemplateAPI.MTemplateError.kTemplateErrorTypeWarning)
        ## ERRORS
        if log.lower().find("error") != -1:
            return MTemplateAPI.MTemplateError(1, "Error keyword found",
                                               MTemplateAPI.MTemplateError.kTemplateErrorTypeError)
        if log.lower().find("no licenses could be found to run this application") != -1:
            return MTemplateAPI.MTemplateError(1, "No license available",
                                               MTemplateAPI.MTemplateError.kTemplateErrorTypeError)
        return MTemplateAPI.MTemplateError()

    def onCheckLogLine(self, job, chunk, clientTemplatePreferences, line, lineNum, warnings, errors, silencedWarnings,
                       silencedErrors):
        return MTemplateAPI.MTemplateError()

    def onBuildEnvironment(this, job, chunk, clientTemplatePreferences, existingEnvironment):
        return existingEnvironment

    def onGetApplicationPath(self, job, chunk, clientTemplatePreferences, pathOut):
        platform = MClientAPI.GetPlatform()
        if platform == MClientAPI.kPlatformWindows:
            pathOut.setString("cmd.exe")
        else:
            pathOut.setString("/bin/sh")
        return MTemplateAPI.MTemplateError()

    def onGetApplicationStartingFolder(self, job, chunk, clientTemplatePreferences, pathOut):
        pathStart = "c:\\Windows\\System32"
        platform = MClientAPI.GetPlatform()
        if platform == MClientAPI.kPlatformWindows:
            #pathOut.setString(job.attributeGetString("NOICEBIN"))
            pathOut.setString(pathStart)
        else:
            pass
        return MTemplateAPI.MTemplateError()

    def onCheckForSubframeAdvancingString(self, job, chunk, line):
        return 0

    def onCheckForSubframeProgress(self, job, chunk, line, progressOut):
        return 0

    def onCheckExitCode(self, job, chunk, clientTemplatePreferences, exitCode):
        if exitCode != 0:
            return MTemplateAPI.MTemplateError(exitCode, "%d Exit code different from expected 0 value" % exitCode,
                                               MTemplateAPI.MTemplateError.kTemplateErrorTypeWarning)
        return MTemplateAPI.MTemplateError()

    def onApplicationFinder(self, moduleRegExp, moduleTag):
        return MTemplateAPI.kTemplateScanNone

    def onFindApplication(self, basePath, clientTemplatePreferences):
        pass

''' Create an instance of the template class and install it into the template manager context '''
template = ShellTemplate()
manager.installPyTemplate(template)
