"""
Generation of the full cam DAG for one exposure and the jobs
associated to this 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.Camera import Camera as CameraConstants

import sys

#################################################################
class CamManager(Gpc1Manager):
    """Derived from the abstract Gpc1Manager to have database access
    """
    SCRIPT_NAME = '/home/panstarrs/ippdor/local/bin/generate_cam_jobs.py'

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

    def build_new_camera_jobs(self, stage_name = 'B010'):
        """Build Condor jobs files for stages B010 (regular new), B020, B030, B040 (3 possible reverts)
        """
        query = """
SELECT
    rawExp.exp_tag,
    camRun.cam_id,
    rawExp.camera,
    camRun.state,
    camRun.workdir,
    camRun.reduction,
    camRun.dvodb
FROM camRun
JOIN chipRun
    USING(chip_id)
JOIN rawExp
    USING(exp_id)
LEFT JOIN camProcessedExp
    USING(cam_id)
LEFT JOIN camMask
    ON camRun.label = camMask.label
WHERE
    chipRun.state = 'full'
    AND ((camRun.state = 'new' AND camProcessedExp.cam_id IS NULL) OR camRun.state = 'update')
    AND camMask.label IS NULL
    AND chipRun.label = '%s'
    AND rawExp.exp_name = '%s'
""" % (self.label, self.exposure_name)
        cameras = self.multiple_query(query)
        jobs_filename = '%s/%s.jobs' % (self.cam_directory, stage_name)
        message = 'Generating Condor jobs file [%s]' % (jobs_filename)
        message = '%s for processing of %d cam (stage %s)' % ( message,
                                                               len(cameras), 
                                                               stage_name)
        print message
        output = open(jobs_filename, 'w')
        output.write('Executable = %s/encapsulate_camera.sh\n' % GlobalConstants.IPPDOR_BINARY_PATH)
        output.write('Universe = vanilla\n')
        output.write('Output = %s/%s.%s.cam_%s.out\n' % (self.nfs_cam_directory,
                                                         self.label, self.exposure_name, 
                                                         stage_name))
        output.write('Error = %s/%s.%s.cam_%s.err\n' % (self.nfs_cam_directory,
                                                        self.label, self.exposure_name, 
                                                        stage_name))
        output.write('Log = %s/%s.%s.cam.log\n' % (self.cam_directory,
                                                   self.exposure_name, stage_name))
        output.write('Requirements = %s\n' % CameraConstants.REQUIREMENTS)
        output.write('Notification = Error\n')
        output.write('\n')
        output.write('getenv = True\n')
        output.write('\n')
        if len(cameras) > 0:
            camera = cameras[0]
            (exp_tag, cam_id, camera, state, workdir, reduction, dvodb) = camera
            if len(cameras) > 1:
                body = CameraConstants.EMAIL_BODY_MULTIPLE_CAM_ENTRIES % (self.label, 
                                                                          self.exposure_name, 
                                                                          cam_id)
                self.send_email('Multiple camRun entries for label %s / exposure %s' 
                                % (self.label, 
                                   self.exposure_name),
                                body)
            outroot = '%s/%s/%s.cm.%d' % (workdir, exp_tag, exp_tag, cam_id)
            arguments = "Arguments = %s" % GlobalConstants.CAMEXP
            arguments = "%s --exp_tag %s" % (arguments, exp_tag)
            arguments = "%s --cam_id %d" % (arguments, cam_id)
            arguments = "%s --camera %s" % (arguments, camera)
            arguments = "%s --outroot %s" % (arguments, outroot)
            arguments = "%s --run-state %s" % (arguments, state)
            if reduction is not None and reduction != 'NULL':
                arguments = '%s --reduction %s' % (arguments, reduction)
            if dvodb is not None and dvodb != 'NULL':
                arguments = '%s --dvodb %s' % (arguments, dvodb)
            output.write('%s\n' % arguments)
            output.write('Queue\n')
            output.write('\n')
        if len(cameras) == 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_camera_revert_jobs(self, stage_name):
        """Build the jobs to revert camera
        """
        query = """
DELETE FROM camProcessedExp
USING camProcessedExp, camRun, chipRun, rawExp
WHERE
    camRun.cam_id = camProcessedExp.cam_id
    AND camRun.chip_id = chipRun.chip_id
    AND chipRun.exp_id = rawExp.exp_id
    AND camProcessedExp.fault != 0
    AND camRun.state = 'new'
    AND camRun.label = '%s'
    AND chipRun.exp_id = %d
""" % (self.label, self.get_exposure_id(self.exposure_name))
        self.multiple_query(query)
        self.build_new_camera_jobs(stage_name)

    def advance_camera(self):
        """Last phase (B050) in camera: 
        * If there is something to revert (we already reverted 3 times): send 
          an e-mail to the operator and abort any further processing
        * If it has quality != 0: send an e-mail to the operator and
          abort any further processing
        """
        query = """
SELECT camProcessedExp.fault, camProcessedExp.quality
FROM camProcessedExp 
  JOIN camRun USING(cam_id) 
  JOIN chipRun USING(chip_id) 
  JOIN rawExp USING(exp_id)
WHERE
  ( ( camProcessedExp.fault != 0
      AND camRun.state = 'new' )
    OR ( camProcessedExp.quality != 0) )
  AND camRun.label = '%s'
  AND chipRun.exp_id = %d
""" % (self.label, self.get_exposure_id(self.exposure_name))
        cam_status = self.multiple_query(query)
        if len(cam_status) > 0:
            # Send e-mail to recipient
            print 'Default for exposure [%s] at cam level' % (self.exposure_name)
            body = CameraConstants.EMAIL_BODY_REVERT_FAILURE % (self.exposure_name,
                                                                self.label, 
                                                                self.exposure_name)
            self.send_email('Camera stage failed for label %s / exposure %s)' % (self.label, 
                                                                                 self.exposure_name), 
                            body)
            print 'EMail sent to [%s]' % self.recipient
            raise IppException('Camera stage failed for label %s / exposure %s)' 
                               % (self.label, 
                                  self.exposure_name))
        # Generate the B050 stage
        jobs_filename = '%s/B050.jobs' % (self.cam_directory)
        print "Generating Condor jobs file [%s] for processing of camera (stage B050)" % (jobs_filename)
        output = open(jobs_filename, 'w')
        output.write('Executable = %s/encapsulate_camera.sh\n' % GlobalConstants.IPPDOR_BINARY_PATH)
        output.write('Universe = vanilla\n')
        output.write('Output = %s/%s.%s.cam_B050.out\n' % (self.nfs_cam_directory,
                                                           self.label, self.exposure_name))
        output.write('Error = %s/%s.%s.cam_B050.err\n' % (self.nfs_cam_directory,
                                                          self.label, self.exposure_name))
        output.write('Log = %s/%s.%s.cam_B050.log\n' % (self.cam_directory,
                                                        self.label, self.exposure_name))
        output.write('Requirements = %s\n' % CameraConstants.REQUIREMENTS)
        output.write('Notification = Error\n')
        output.write('\n')
        output.write('getenv = True\n')
        output.write('\n')
        arguments = 'echo Nothing to do for camera exposure %s/%s (stage B050)' % (self.exposure_name, self.label)
        output.write('Arguments = %s\n' % arguments)
        output.write('Queue\n')
        output.write('\n')
        output.close()

    def build_camera_dag(self):
        """Generate the camera DAG
        """
        dag_name = '%s/camera.dag' % (self.cam_directory)
        output = open(dag_name, 'w')
        output.write('# Generated DAG\n')
        output.write('JOB\tB010\t%s/B010.jobs\n' % self.cam_directory)
        output.write('SCRIPT POST B010 %s B020 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
                                                              self.label, self.exposure_name, self.recipient))
        output.write('JOB\tB020\t%s/B020.jobs\n' % self.cam_directory)
        output.write('SCRIPT POST B020 %s B030 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
                                                              self.label, self.exposure_name, self.recipient))
        output.write('JOB\tB030\t%s/B030.jobs\n' % self.cam_directory)
        output.write('SCRIPT POST B030 %s B040 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
                                                              self.label, self.exposure_name, self.recipient))
        output.write('JOB\tB040\t%s/B040.jobs\n' % self.cam_directory)
        output.write('SCRIPT POST B040 %s B050 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
                                                              self.label, self.exposure_name, self.recipient))
        output.write('JOB\tB050\t%s/B050.jobs\n' % self.cam_directory)
        output.write('\n')
        output.write('PARENT B010 CHILD B020\n')
        output.write('PARENT B020 CHILD B030\n')
        output.write('PARENT B030 CHILD B040\n')
        output.write('PARENT B040 CHILD B050\n')
        output.write('\n')
        output.close()
        return dag_name

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