import os
import glob
import tempfile
import copy

from Ganga.Utility.Config import getConfig
from Ganga.GPIDev.Lib.GangaList.GangaList import GangaList

from Ganga.GPIDev.Base.Proxy import isType, stripProxy, getName

from Ganga.Utility.logging import getLogger
logger = getLogger()

def outputFilePostProcessingOnWN(job, outputFileClassName):
    """
    Checks if the output files of a given job(we are interested in the backend) 
    should be postprocessed on the WN, depending on job.backend_output_postprocess dictionary
    """
    return outputFilePostProcessingTestForWhen(job, outputFileClassName, 'WN')


def outputFilePostProcessingOnClient(job, outputFileClassName):
    """
    Checks if the output files of a given job(we are interested in the backend) 
    should be postprocessed on the client, depending on job.backend_output_postprocess dictionary
    """
    return outputFilePostProcessingTestForWhen(job, outputFileClassName, 'client')


def outputFilePostProxessingOnSubmit(job, outputFileClassName):
    """
    Checks if the output files of a given job(we are interested in the backend)
    should have been postprocessed on job submission, depending on job.backend_output_postprocess dictionary
    """
    return outputFilePostProcessingTestForWhen(job, outputFileClassName, 'submit')


def outputFilePostProcessingTestForWhen(job, outputFileClassName, when):
    """
    Checks if the output files of a given job(we are interested in the backend)
    should be postprocessed 'when', depending on job.backend_output_postprocess dictionary
    """
    backendClassName = getName(job.backend)

    backend_output_postprocess = stripProxy(job).getBackendOutputPostprocessDict()
    if backendClassName in backend_output_postprocess:
        if outputFileClassName in backend_output_postprocess[backendClassName]:
            if backend_output_postprocess[backendClassName][outputFileClassName] == when:
                return True

    return False

def getOutputSandboxPatterns(job):
    """
    Intended for grid backends where we have to set the outputsandbox patterns for the output file types that have to be processed on the client
    """
    outputPatterns = []

    if len(job.outputfiles) > 0:

        outputPatterns.append(getConfig('Output')['PostProcessLocationsFileName'])

        for outputFile in job.outputfiles:

            outputFileClassName = getName(outputFile)

            if outputFilePostProcessingOnClient(job, outputFileClassName):
                if outputFile.namePattern not in outputPatterns:
                    if outputFile.compressed:
                        outputPatterns.append('%s.gz' % outputFile.namePattern)
                    else:
                        outputPatterns.append(outputFile.namePattern)

    return outputPatterns

def getInputFilesPatterns(job):
    """
    we have to set the inputsandbox patterns for the input files that will be copied from the client, also write the commands for downloading input files from the WN
    """

    tmpDir = tempfile.mkdtemp()

    inputPatterns = []

    # if GangaDataset is used, check if they want the inputfiles transferred
    inputfiles_list = copy.deepcopy(job.inputfiles if job.inputfiles else [])
    from Ganga.GPIDev.Lib.Dataset.GangaDataset import GangaDataset
    if not job.subjobs and job.inputdata and isType(job.inputdata, GangaDataset) and job.inputdata.treat_as_inputfiles:
        inputfiles_list += job.inputdata.files

    for inputFile in inputfiles_list:

        inputFileClassName = getName(inputFile)

        if outputFilePostProcessingOnClient(job, inputFileClassName):

            # download in temp dir
            inputFile.copyTo(tmpDir)

            for currentFile in glob.glob(os.path.join(inputFile.localDir, inputFile.namePattern)):
                if currentFile not in inputPatterns:
                    inputPatterns.append(currentFile)

    return inputPatterns, tmpDir


def getOutputSandboxPatternsForInteractive(job):
    """
    This should be used from only from Interactive backend
    """

    patternsToSandbox = [getConfig('Output')['PostProcessLocationsFileName']]
    patternsToZip = []

    for outputFile in job.outputfiles:

        outputFileClassName = getName(outputFile)

        if outputFilePostProcessingOnClient(job, outputFileClassName):
            if outputFile.compressed:
                patternsToSandbox.append('%s.gz' % outputFile.namePattern)
                patternsToZip.append(outputFile.namePattern)
            else:
                patternsToSandbox.append(outputFile.namePattern)

    return (patternsToSandbox, patternsToZip)


def getWNCodeForOutputSandbox(job, files, jobid):
    """
    This should be used from Local and Batch backend, where there is code on the WN for
    sending the output(optionally compressed before that) to the outputsandbox
    """

    patternsToSandbox = []
    patternsToZip = []

    if len(job.outputfiles) > 0:

        files.append(getConfig('Output')['PostProcessLocationsFileName'])

        for outputFile in job.outputfiles:

            outputFileClassName = getName(outputFile)

            if outputFilePostProcessingOnClient(job, outputFileClassName):
                patternsToSandbox.append(outputFile.namePattern)

    insertScript = """\n
from files import recursive_copy
import glob
import os

f_to_copy = ###FILES###

for patternToSandbox in ###PATTERNSTOSANDBOX###:
    for currentFile in glob.glob(patternToSandbox):
        f_to_copy.append(currentFile)

filesToZip = []

for patternToZip in ###PATTERNSTOZIP###:
    for currentFile in glob.glob(patternToZip):
        os.system("gzip %s" % currentFile)
        filesToZip.append(currentFile)
            
final_list_to_copy = []

for f in f_to_copy:
    if f in filesToZip:
        final_list_to_copy.append('%s.gz' % f)  
    else:       
        final_list_to_copy.append(f)            

for fn in final_list_to_copy:
    try:
        if not os.path.exists(sharedoutputpath):
            os.makedirs(sharedoutputpath)
        if os.path.exists(fn):
            recursive_copy(fn, sharedoutputpath)
    except Exception as x:
        print('ERROR: (job'+###JOBID###+')',x)
"""
    insertScript = insertScript.replace('###FILES###', str(files))
    insertScript = insertScript.replace('###PATTERNSTOSANDBOX###', str(patternsToSandbox))
    insertScript = insertScript.replace('###PATTERNSTOZIP###', str(patternsToZip))
    insertScript = insertScript.replace('###JOBID###', jobid)

    return insertScript


def getWNCodeForDownloadingInputFiles(job, indent):
    """
    Generate the code to be run on the WN to download input files
    """

    from Ganga.GPIDev.Lib.Dataset.GangaDataset import GangaDataset

    def doIHaveInputFiles(job):
        """ helper function for determining if a job has inputfiles it needs to make available
        Args: job(Job) This is the job which we're testing for inputfiles """
        if job.inputfiles is not None and len(job.inputfiles) != 0:
            return True
        if (job.inputdata is not None and len(job.inputdata) != 0) and isinstance(job.inputdata, GangaDataset) and job.inputdata.treat_as_inputfiles:
            return True
        return False

    if job.master is not None:
        if not doIHaveInputFiles(job.master) and not doIHaveInputFiles(job):
            return ""
    else:
        if not doIHaveInputFiles(job):
            return ""

    insertScript = """\n
"""

    inputfiles_list = []
    if not job.inputfiles:
        # if GangaDataset is used, check if they want the inputfiles transferred
        if job.master is not None:
            inputfiles_list = job.master.inputfiles
    else:
        inputfiles_list = job.inputfiles

    if job.inputdata:
        if job.inputdata and isType(job.inputdata, GangaDataset) and job.inputdata.treat_as_inputfiles:
            inputfiles_list += job.inputdata.files
    elif job.master is not None:
        if job.master.inputdata and isType(job.master.inputdata, GangaDataset) and job.master.inputdata.treat_as_inputfiles:
            inputfiles_list += job.master.inputdata.files

    for inputFile in inputfiles_list:

        inputfileClassName = getName(inputFile)

        inputFile.processWildcardMatches()
        if inputFile.subfiles:
            for subfile in inputFile.subfiles:
                insertScript += subfile.getWNScriptDownloadCommand(indent)
        else:
            insertScript += inputFile.getWNScriptDownloadCommand(indent)

    insertScript = insertScript.replace('###INDENT###', indent)

    return insertScript


def getWNCodeForOutputPostprocessing(job, indent):

    # dict containing the list of outputfiles that need to be processed on the
    # WN for every file type
    outputFilesProcessedOnWN = {}
    patternsToZip = []

    if len(job.outputfiles) == 0:
        return ""
    else:
        for outputFile in job.outputfiles:

            outputfileClassName = getName(outputFile)
            backendClassName = getName(job.backend)

            if outputFile.compressed:
                patternsToZip.append(outputFile.namePattern)

            if outputfileClassName not in outputFilesProcessedOnWN.keys():
                outputFilesProcessedOnWN[outputfileClassName] = []

            if outputFilePostProcessingOnWN(job, outputfileClassName):
                outputFilesProcessedOnWN[outputfileClassName].append(outputFile)

    if not patternsToZip:
        if not any(outputFilesProcessedOnWN.values()):
            return ""

    logger.debug("Process: '%s' on WN" % str(outputFilePostProcessingOnWN))

    shortScript = """\n
import os, glob
for patternToZip in ###PATTERNSTOZIP###:
    for currentFile in glob.glob(os.path.join(os.getcwd(),patternToZip)):
        if os.path.isfile(currentFile):
            os.system("gzip %s" % currentFile)

postprocesslocations = open(os.path.join(os.getcwd(), '###POSTPROCESSLOCATIONSFILENAME###'), 'a+')  
"""

    from Ganga.GPIDev.Lib.File import FileUtils
    shortScript = FileUtils.indentScript(shortScript, '###INDENT###')

    insertScript = shortScript

    insertScript = insertScript.replace('###PATTERNSTOZIP###', str(patternsToZip))
    insertScript = insertScript.replace('###POSTPROCESSLOCATIONSFILENAME###', getConfig('Output')['PostProcessLocationsFileName'])

    for outputFileName in outputFilesProcessedOnWN.keys():

        if len(outputFilesProcessedOnWN[outputFileName]) > 0:

            insertScript += outputFilesProcessedOnWN[outputFileName][0].getWNInjectedScript(outputFilesProcessedOnWN[outputFileName], indent, patternsToZip, 'postprocesslocations')

    insertScript += """\n
###INDENT###postprocesslocations.close()
"""
    insertScript = insertScript.replace('###INDENT###', indent)

    return insertScript

import re

wildcardregex = re.compile('[*?\[\]]')


def iexpandWildCards(filelist):
    for f in filelist:
        if wildcardregex.search(f.namePattern):
            f.processWildcardMatches()
            for subfile in f.subfiles:
                yield subfile
        else:
            yield f


def expandWildCards(filelist):
    """

    """
    l = GangaList()
    l.extend(iexpandWildCards(filelist))
    return l


def getWNCodeForInputdataListCreation(job, indent):
    """generate the code to create ths inputdata list on the worker node
    Args:
        job (Job): Job object which this file is being generated for
        indent (str): Indent level in the script of choice which is to be placed infront of generated code
    """
    insertScript = """\n
###INDENT###open("__GangaInputData.txt__", "w").write( "\\n".join( ###FILELIST### ) )
"""

    insertScript = insertScript.replace('###INDENT###', indent)

    if job.inputdata and hasattr(job.inputdata, "getFilenameList"):
        insertScript = insertScript.replace(
            '###FILELIST###', "%s" % job.inputdata.getFilenameList())
    else:
        insertScript = insertScript.replace('###FILELIST###', "[]")

    return insertScript

