"""
Generate the full chip DAG for one exposure
Generate the Condor job files for chip stage
"""
from ipp.Gpc1Manager import Gpc1Manager as Gpc1Manager
from ipp.helpers import mkdir
from ipp.exceptions.IppException import IppException
from ipp.constants.Globals import Globals as GlobalConstants
from ipp.constants.Chip import Chip as ChipConstants

#################################################################
class ChipManager(Gpc1Manager):
    """
    Generate the full chip DAG for one exposure
    Generate the Condor job files for chip stage
    """
    SCRIPT_NAME = '%s/generate_chip_jobs.py' % GlobalConstants.IPPDOR_BINARY_PATH

    def __init__(self,
                 label,
                 exposure_name,
                 recipient):
        """
        Creates a connector to the gpc1 database as well as local and
        nfs directories (their name is chip)
        """
        Gpc1Manager.__init__(self, recipient=recipient)
        self.label = label
        self.exposure_name = exposure_name
        self.chip_directory = '%s/%s/%s/chip' % (GlobalConstants.LOCAL_TMP_DIR, 
                                                 self.label, self.exposure_name)
        mkdir(self.chip_directory)
        self.nfs_chip_directory = '%s/%s/%s/chip' % (GlobalConstants.NFS_TMP_DIR, 
                                                     self.label, self.exposure_name)
        mkdir(self.nfs_chip_directory)

    def build_new_chips_jobs(self, stage_name = 'A010'):
        """Build the jobs file for the A010 node
        """
        query = """
SELECT
    chipRun.exp_id,
    chipRun.chip_id,
    chipImfile.chip_imfile_id,
    rawImfile.class_id,
    rawImfile.uri,
    rawExp.camera,
    chipRun.state,
    rawImfile.magicked as raw_magicked,
    (rawImfile.user_1 is not NULL and rawImfile.user_1 > 0.5) as deburned,
    chipRun.workdir,
    rawExp.exp_tag,
    chipRun.reduction
FROM chipRun
JOIN rawExp
    USING(exp_id)
JOIN rawImfile
    ON rawExp.exp_id = rawImfile.exp_id
    AND rawImfile.ignored = 0
JOIN chipImfile
    USING(chip_id, class_id)
LEFT JOIN chipProcessedImfile
    ON chipRun.chip_id = chipProcessedImfile.chip_id
    AND rawImfile.exp_id = chipProcessedImfile.exp_id
    AND rawImfile.class_id = chipProcessedImfile.class_id
LEFT JOIN chipMask
    ON chipRun.label = chipMask.label
LEFT JOIN Label ON chipRun.label = Label.label
WHERE
    chipRun.state = 'new'
    AND chipProcessedImfile.chip_id IS NULL
    AND chipProcessedImfile.exp_id IS NULL
    AND chipProcessedImfile.class_id IS NULL
    AND chipMask.label IS NULL
    AND chipRun.exp_id = %d
    AND chipRun.label = '%s'
""" % (self.get_exposure_id(self.exposure_name), self.label)
        chips = self.multiple_query(query)
        if len(chips) != 60:
            message = "(Stage %s) There are %d chips (instead of 60) " % (stage_name, 
                                                                          len(chips))
            message = "%s for exposure [%s] (label: [%s])" % (message,
                                                              self.exposure_name, 
                                                              self.label)
            print message
        jobs_filename = '%s/%s.jobs' % (self.chip_directory, stage_name)
        message = 'Generating Condor jobs file [%s] ' % jobs_filename
        message = '%s for processing of %d chips (stage %s)' % (message, 
                                                                len(chips), 
                                                                stage_name)
        output = open(jobs_filename, 'w')
        output.write('Executable = %s/encapsulate_chip.sh\n' % GlobalConstants.IPPDOR_BINARY_PATH)
        output.write('Universe = vanilla\n')
        output.write('Output = %s/%s.%s.chip_%s.out\n' % (self.nfs_chip_directory, 
                                                          self.label, self.exposure_name, stage_name))
        output.write('Error = %s/%s.%s.chip_%s.err\n' % (self.nfs_chip_directory, 
                                                         self.label, self.exposure_name, stage_name))
        output.write('Log = %s/%s.%s.chip_%s.log\n' % (self.chip_directory,
                                                       self.label, self.exposure_name,
                                                       stage_name))
        output.write('Requirements = %s\n' % ChipConstants.REQUIREMENTS)
        output.write('Notification = Error\n')
        output.write('\n')
        output.write('getenv = True\n')
        output.write('\n')
        for chip in chips:
            (exp_id, chip_id, chip_imfile_id, ota, uri, camera, state, raw_magicked, 
             deburned, work_dir, exp_tag, reduction) = chip
            outroot = '%s/%s/%s.ch.%d' % (work_dir, exp_tag, exp_tag, chip_id)
            arguments = 'Arguments = %s' % GlobalConstants.CHIPIMFILE
            arguments = '%s --exp_id %d' % (arguments, exp_id)
            arguments = '%s --chip_id %d' % (arguments, chip_id)
            arguments = '%s --chip_imfile_id %d' % (arguments, chip_imfile_id)
            arguments = '%s --class_id %s' % (arguments, ota)
            arguments = '%s --uri %s' % (arguments, uri)
            arguments = '%s --camera %s' % (arguments, camera)
            arguments = '%s --run-state %s' % (arguments, state)
            arguments = '%s --deburned %d' % (arguments, deburned)
            arguments = '%s --outroot %s' % (arguments, outroot)
            if raw_magicked > 0:
                arguments = '%s --magicked %d' % (arguments, raw_magicked)
            if reduction is not None:
                arguments = '%s --reduction %s' % (arguments, reduction)
            output.write('%s\n' % arguments)
            output.write('Queue\n')
            output.write('\n')
        if len(chips) == 0:
            arguments = 'Arguments = echo Nothing to do for stage %s' % stage_name
            output.write('%s\n' % arguments)
            output.write('Queue\n')
            output.write('\n')
        output.close()

    def build_chip_dag(self):
        """Build the full DAG file for the chip (aka A) stage
        """
        dag_name = '%s/chip.dag' % (self.chip_directory)
        output = open(dag_name, 'w')
        output.write('# Generated DAG\n')
        output.write('JOB\tA010\t%s/A010.jobs\n' % self.chip_directory)
        output.write('SCRIPT POST A010 %s A020 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
                                                              self.label, self.exposure_name,
                                                              self.recipient))
        output.write('JOB\tA020\t%s/A020.jobs\n' % self.chip_directory)
        output.write('SCRIPT POST A020 %s A030 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
                                                              self.label, self.exposure_name,
                                                              self.recipient))
        output.write('JOB\tA030\t%s/A030.jobs\n' % self.chip_directory)
        output.write('SCRIPT POST A030 %s A040 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
                                                              self.label, self.exposure_name,
                                                              self.recipient))
        output.write('JOB\tA040\t%s/A040.jobs\n' % self.chip_directory)
        output.write('SCRIPT POST A040 %s A050 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
                                                              self.label, self.exposure_name,
                                                              self.recipient))
        output.write('JOB\tA050\t%s/A050.jobs\n' % self.chip_directory)
        output.write('\n')
        output.write('PARENT A010 CHILD A020\n')
        output.write('PARENT A020 CHILD A030\n')
        output.write('PARENT A030 CHILD A040\n')
        output.write('PARENT A040 CHILD A050\n')
        output.write('\n')
        output.close()
        return dag_name

    def build_chip_revert_jobs(self, stage_name):
        """Build the jobs to revert chips
        """
        query = """
DELETE FROM chipProcessedImfile
USING chipProcessedImfile, chipRun, rawExp
WHERE
    chipRun.chip_id = chipProcessedImfile.chip_id
    AND rawExp.exp_id = chipProcessedImfile.exp_id
    AND chipRun.state = 'new'
    AND chipProcessedImfile.fault != 0
    AND chipRun.exp_id = %d
    AND chipRun.label = '%s'
""" % (self.get_exposure_id(self.exposure_name), self.label)
        print "Reverting with %s" % query
        self.multiple_query(query)
        self.build_new_chips_jobs(stage_name)

    def advance_chips(self):
        """
        Last phase in Chip:
        * If *all* chips have failed: send an e-mail to the operator
          and abort any further processing
          (Matching example: MD08.photfest.20100923 o5331g0350o)
        * Create the job that runs 'chiptool -advanceexp' for the exposure
        """
        query = """
SELECT class_id
FROM chipProcessedImfile JOIN chipRun USING(chip_id) JOIN rawExp ON rawExp.exp_id = chipRun.exp_id
WHERE chipRun.state = 'new'
    AND chipProcessedImfile.fault != 0
    AND chipRun.exp_id = %d
    AND chipRun.label = '%s'""" % (self.get_exposure_id(self.exposure_name), self.label)
        chips = self.multiple_query(query)
        if len(chips) != 0:
            # Send e-mail to recipient
            body = ChipConstants.EMAIL_BODY_FAILED_REVERT.replace('@COUNT_FAILED_CHIPS@',
                                                                  len(chips))
            body = body.replace('@LABEL@', self.label)
            body = body.replace('@EXPOSURE_NAME@', self.exposure_name)
            query = 'SELECT exp_name, chip_id, class_id, chipProcessedImfile.fault, path_base'
            query = '%s FROM chipProcessedImfile JOIN chipRun USING(chip_id) JOIN rawExp' % query
            query = "%s ON chipRun.exp_id = rawExp.exp_id " % query
            query = "%s WHERE chipRun.label = '%s'" % (query, self.label)
            query = "%s  AND exp_name = '%s' " % (query, self.exposure_name)
            query = "%s AND chipProcessedImfile.fault!=0" % (query)
            rows = self.multiple_query(query)
            updateprocessedimfile = ''
            for row in rows:
                chip_id = row[1]
                class_id = row[2]
                updateprocessedimfile = "%s\tchiptool -dbname gpc1 -updateprocessedimfile" % updateprocessedimfile
                updateprocessedimfile = "%s -fault 0 -set_quality 42" % updateprocessedimfile
                updateprocessedimfile = "%s -chip_id %s -class_id %s\n" % (updateprocessedimfile, 
                                                                           chip_id, 
                                                                           class_id)
            body = body.replace('@CHIPTOOL_UPDATEPROCESSEDIMFILE@', updateprocessedimfile)
            self.send_email(ChipConstants.EMAIL_SUBJECT_FAILED_REVERT % (self.label, 
                                                                         self.exposure_name), 
                            body % (GlobalConstants.LOCAL_TMP_DIR))
            print 'EMail sent to [%s]' % self.recipient
            raise IppException('Chip stage failed for label %s / exposure %s)' % (self.label, self.exposure_name))
        # It seems that sometimes we create more than one entry in
        # camRun. Maybe chiptool is not thread-safe
        query = "SELECT count(*) FROM camRun JOIN chipRun USING(chip_id)"
        query = "%s WHERE chipRun.label='%s' AND chipRun.chip_id = %d" % (query,
                                                                          self.label, 
                                                                          self.get_chip_id(self.exposure_name, 
                                                                                           self.label))
        cam_count = int(self.simple_query(query)[0])
        if (cam_count != 0):
            body = 'Error: More than one entry in camRun'
            body = '%s for chip_id %d' % (body,
                                          self.get_chip_id(self.exposure_name, 
                                                           self.label))
            self.send_email('Cam entries for %s: %d' % (self.exposure_name, cam_count), 
                            body)
            raise IppException('Invalid cam entries count for chip %d' 
                               % self.get_chip_id(self.exposure_name, 
                                                  self.label) )
        # Resume to normal case...
        jobs_filename = '%s/A050.jobs' % (self.chip_directory)
        print "Generating Condor jobs file [%s] for processing of %d chips (stage A050)" % (jobs_filename, len(chips))
        output = open(jobs_filename, 'w')
        output.write('Executable = %s/encapsulate_chip.sh\n' % GlobalConstants.IPPDOR_BINARY_PATH)
        output.write('Universe = vanilla\n')
        output.write('Output = %s/%s.%s.chip_A050.out\n' % (self.nfs_chip_directory,
                                                            self.label, self.exposure_name))
        output.write('Error = %s/%s.%s.chip_A050.err\n' % (self.nfs_chip_directory,
                                                           self.label, self.exposure_name))
        output.write('Log = %s/%s.%s.chip_A050.log\n' % (self.chip_directory,
                                                        self.label, self.exposure_name))
        output.write('Requirements = %s\n' % ChipConstants.REQUIREMENTS)
        output.write('Notification = Error\n')
        output.write('\n')
        output.write('getenv = True\n')
        output.write('\n')
        arguments = '%s -advanceexp -chip_id %d -label %s' % (GlobalConstants.CHIPTOOL,
                                                              self.get_chip_id(self.exposure_name, 
                                                                               self.label), 
                                                              self.label)
        output.write('Arguments = %s\n' % arguments)
        output.write('Queue\n')
        output.write('\n')
        output.close()

#################################################################
if __name__ == '__main__':
    import sys
    print '<Error>: Not intended to be used as an independent program'
    print '  Use %s' % ChipManager.SCRIPT_NAME
    sys.exit(1)
