##########################################################################
# Ganga - a computational task management tool for easy access to Grid resources
# http://cern.ch/ganga
#
# $Id: Composite.py,v 1.2 2009-06-09 14:31:42 moscicki Exp $
#
# Copyright (C) 2003-2007 The Ganga Project
#
# This file is part of GangaCore.
#
# Ganga is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Ganga is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
##########################################################################

import sys
from GangaCore.GPIDev.Adapters.IMonitoringService import IMonitoringService

class CompositeMonitoringService(IMonitoringService):

    """ IMonitoringService container:
    Wrapper object containing a list of IMonitoringService(s) inside and delegating
    the interface methods to each of them. (composite design pattern) 
    This object is used automatically to transparently wrap the list of monitoring services set in the configuration    
    """

    def __init__(self, monClasses, jobInfos, configInfos):
        """Create a new composite monitoring service based on the lists of
        monitoring classes, jobs and configs (all the same length).

        If this is called in the Ganga client, i.e. from Ganga/GPIDev/MonitoringServices,
        then jobInfos is a list of Job (all the same), configInfos is a list of
        Config (specific to each monitoring class).

        If this is called on the worker node, i.e. from the text generated by
        getWrapperScriptConstructorText(), the jobInfos are dictionaries (specific
        to each monitoring class) and configInfos are dictionaries of effective
        config options (specific to each monitoring class).
        """

        if not (len(monClasses) == len(jobInfos) == len(configInfos)):
            raise Exception(
                "cannot create monitoring object, list of monitoring classes, jobs and configs are not the same length.")

        IMonitoringService.__init__(self, jobInfos, configInfos)

        # init the logger
        try:
            import GangaCore.Utility.logging
            self.logger = GangaCore.Utility.logging.getLogger()
        except ImportError:
            # on the worker node we don't have access to Ganga logging facilities
            # so we simple print out the log message
            self.logger = None

        # init the monitoring services
        self.monMonServices = []
        for i in range(len(monClasses)):
            try:
                monClass = monClasses[i]
                # allow for existing monitoring classes which do not take
                # config_info in constructor
                if configInfos[i] is None:
                    monService = monClass(jobInfos[i])
                else:
                    monService = monClass(jobInfos[i], configInfos[i])
                self.monMonServices.append(monService)
            except Exception as e:
                # discard errors in initialization of monitoring services
                self.logger.warning("Failed to init %s monitoring service...discarding it" % str(monClass))
                from GangaCore.Utility.logging import log_user_exception
                log_user_exception(self.logger)

    def start(self, **opts):
        """Application is about to start on the worker node.
        Called by: job wrapper.
        """
        ret = {}
        for monService in self.monMonServices:
            try:
                monClass = str(monService.__class__)
                ret[monClass] = monService.start(**opts)
            except Exception as e:
                # discard errors in initialization of monitoring services
               self.logger.warning("%s monitoring service failed to *start*: %s" % (monClass, e))

        return ret

    def progress(self, **opts):
        """Application execution is in progress (called periodically, several times a second).
        Called by: job wrapper. """

        ret = {}
        for monService in self.monMonServices:
            try:
                monClass = str(monService.__class__)
                ret[monClass] = monService.progress(**opts)
            except Exception as e:
                # discard errors in initialization of monitoring services
                self.logger.warning("%s monitoring service failed to *progress*: %s" % (monClass, e))

        return ret

    def stop(self, exitcode, **opts):
        """Application execution finished.
        Called by: job wrapper. """

        ret = {}
        for monService in self.monMonServices:
            try:
                monClass = str(monService.__class__)
                ret[monClass] = monService.stop(exitcode, **opts)
            except Exception as e:
                # discard errors in initialization of monitoring services
                self.logger.warning("%s monitoring service failed to *stop*: %s" % (monClass, e))
        return ret

    def prepare(self, **opts):
        """Preparation of a job.
        Called by: ganga client. """

        ret = {}
        for monService in self.monMonServices:
            try:
                monClass = str(monService.__class__)
                ret[monClass] = monService.prepare(**opts)
            except Exception as e:
                # discard errors in initialization of monitoring services
                self.logger.warning("%s monitoring service failed in job *prepare*" % monClass)
        return ret

    def submitting(self, **opts):
        """Submission of a job.
        Called by: ganga client. """

        ret = {}
        for monService in self.monMonServices:
            try:
                monClass = str(monService.__class__)
                ret[monClass] = monService.submitting(**opts)
            except Exception as e:
                # discard errors in initialization of monitoring services
                self.logger.warning("%s monitoring service failed in job *submitting*" % monClass)
        return ret

    def submit(self, **opts):
        """Submission of a job.
        Called by: ganga client. """

        ret = {}
        for monService in self.monMonServices:
            try:
                monClass = str(monService.__class__)
                ret[monClass] = monService.submit(**opts)
            except Exception as e:
                # discard errors in initialization of monitoring services
                self.logger.warning("%s monitoring service failed in job *submit*" % monClass)
                from GangaCore.Utility.logging import log_user_exception
                log_user_exception(self.logger)
        return ret

    def complete(self, **opts):
        """Completion of a job.
        Called by: ganga client. """

        ret = {}
        for monService in self.monMonServices:
            try:
                monClass = str(monService.__class__)
                ret[monClass] = monService.complete(**opts)
            except Exception as e:
                # discard errors in initialization of monitoring services
                self.logger.warning("%s monitoring service failed in job *complete*" % monClass)
        return ret

    def fail(self, **opts):
        """Failure of a job.
        Called by: ganga client. """

        ret = {}
        for monService in self.monMonServices:
            try:
                monClass = str(monService.__class__)
                ret[monClass] = monService.fail(**opts)
            except Exception as e:
                # discard errors in initialization of monitoring services
                self.logger.warning("%s monitoring service failed in job *fail*" % monClass)
        return ret

    def kill(self, **opts):
        """Killing of a job.
        Called by: ganga client. """

        ret = {}
        for monService in self.monMonServices:
            try:
                monClass = str(monService.__class__)
                ret[monClass] = monService.kill(**opts)
            except Exception as e:
                # discard errors in initialization of monitoring services
                self.logger.warning("%s monitoring service failed in job *kill*" % monClass)
        return ret

    def rollback(self, **opts):
        """Rollback of a job to new state (caused by error during submission).
        Called by: ganga client. """

        ret = {}
        for monService in self.monMonServices:
            try:
                monClass = str(monService.__class__)
                ret[monClass] = monService.rollback(**opts)
            except Exception as e:
                # discard errors in initialization of monitoring services
                self.logger.warning("%s monitoring service failed in job *rollback*" % monClass)
        return ret

    def getJobInfo(self):
        """ Return a static info object which static information about the job
        at submission time. Called by: ganga client.

        The info object is passed to the contructor. Info
        object may only contain the standard python types (such as lists,
        dictionaries, int, strings). 
        Implementation details:
         return the job info objects as a map for each compound Monitoring Service
         @see getWrapperScriptConstructorText() method
        """

        infos = {}
        for monService in self.monMonServices:
            try:
                monClass = str(monService.__class__)
                infos[monClass] = monService.getJobInfo()
            except Exception as e:
                # discard errors in initialization of monitoring services
                self.logger.warning("%s monitoring service failed in *getJobInfo*: %s" % (monClass, e))
        return infos

