# $Header: https://oberon.oapd.inaf.it/shins/trunk/shins/src/scripts/templates/tpl/shinsapp.py 3962 2022-05-24 07:31:39Z depa $

import sys
import Ice
import sharknir
from time import sleep
from datetime import datetime

from util import variables as uvars
from util import functions as ufun
from util import exception
from util.shinsLogger import logger


class SHINSApp(object):
    '''class handling the connection to the ZeroCIce proxy. Used by all of
    the template scripts, which on their side overload the run method.'''


    def __init__(self, id=""):
        '''Constructor'''

        self.name = __name__
        self.error = []
        self.paused = False
        self.aborted = False
        self.id = id
        self._status = uvars.EXIT_SUCCESS
        self.filenames = None
        self.other = None # for example centroid origin after CloseLoop
        self._ic = None
        self.obsController = None
        self.todo = 0
        self.paragraph_number = 0
        self.tpl_output = {}


    def os(self):
        '''Create a connection with Ice and then with python bindings of C++ OS '''

        ##################
        # ICE connection #
        ##################

        proxy  = "ObsControlAdapterSeq:default -p 10500"
        try:
            print("setting up ICE controller")
            self._ic = Ice.initialize(sys.argv)
            base  = self._ic.stringToProxy(proxy)
        except Exception as e:
            print("Unable to initialize ICE controller: {}".format(e))
            self._ic.destroy()
            #sys.exit()

        #################
        # OS connection #
        #################

        try:
            print("Setting up obscontroller")
            self.obsController  = sharknir.obsControl.SequencerPrx.checkedCast(base)
            return self.obsController

        except Ice.ConnectionRefusedException as e:
            logger.error('''Probably OS is down. Take a look to the monit page.
                                      First check and eventually restart the monit services.
                                      Then check and eventually restart OS.''')
            logger.error(e)
            self._ic.destroy()
            self._status = uvars.EXIT_FAILURE
            #sys.exit(1)

        except Exception as e:
            logger.error("Unable to initialize ICE controller")
            logger.error(e)
            self._ic.destroy()
            self._status = uvars.EXIT_FAILURE
            #sys.exit(1)


    def close_ice(self):
        '''Close and destroy the Ice connection'''

        if self._ic:
            # Clean up
            try:
                self._ic.destroy()
                logger.warning("Ice Closed")
            except:
                self._status = uvars.EXIT_FAILURE
                logger.debug(self._status)
                logger.error("Error closing Ice")
        else:
            logger.warning("No Ice to Close!")



    def run(self, ins_s=None, det_s=None, tel_s=None, rtc_s=None, **kwargs):
        '''Here the inherited template will be implemented'''
        pass


    def paragraph(self, title=""):
        '''Divide the code in paragraphs. Calling self.paragraph("blabla")
           produces a log, and exceptions can be treated from there'''

        self.check_pause_or_abort()

        is_todo = self.paragraph_number == self.todo

        if is_todo:
            logger.info("--------------------------------------------------------------------------")
            logger.info("| {:^70d} |".format(self.paragraph_number))
            logger.info("| {:^70s} |".format(title))
            logger.info("--------------------------------------------------------------------------")
            self.todo += 1

        self.paragraph_number += 1

        return is_todo


    def check_pause_or_abort(self):
        '''
        Pause/Resume the execution of the template.
        '''

        if self.aborted:
            logger.warning("Abort flag detected!")
            self.abort()

        if self.paused:
            logger.warning("Pause flag detected!")
            logger.info("--------------------------------------------------------------------------")
            logger.info("| {:^70s} |".format("PAUSED"))
            logger.info("--------------------------------------------------------------------------")

        while self.paused:
            sleep(0.1)
            if self.aborted:
                logger.warning("Abort flag detected!")
                self.abort()
                

    def show_template_output(self, **kwargs):
        ''' Print a fancy resume of of significative info at the end of each template'''
        if self.tpl_output:
            #logger.info("FINAL RESUME:")
            logger.info("--------------------------------------------------------------------------")
            logger.info("| {:^70s} |".format("TEMPLATE FINAL RESUME"))
            for element, value in self.tpl_output.items():
                this_line = " {} : {} ".format(element, str(value))
                logger.info("| {:>35s} : {:<35s} |".format(element, str(value)))
            logger.info("--------------------------------------------------------------------------")
            
        else:
            pass
        self.tpl_output = {}

    def resetOSContext(self, ob_context={}):
        #print("begin of resetOSContext")
        self.obsController = self.os()
        logger.debug("Reset context arguments in OS")
        for k in ob_context.keys():
            ob_context[k] = ""
        self._status = self.obsController.setContext(ob_context)
        self.close_ice()
        #print("end of resetOSContext")
        return self._status
        
    def main(self, ins_s=None, det_s=None, tel_s=None, rtc_s=None, info_s=None, **kwargs):
        '''Launch the run method'''

        logger.info("ShinsApp: main started")

        ######################################
        # Creating an obscontroller instance #
        ######################################

        self.obsController = self.os()

        # Send OB name and Template name, to put it in the file header
        context = kwargs.copy()
        # all non-string kwargs element must not be passed to OS
        for (k,v) in context.items():
            if type(v) != str:
                context.pop(k)

        self.obsController.setContext(context)
        # print("Is context = kwargs ? : {}".format(context==kwargs))
        # print("context = {}".format(context))
        # print("kwargs  = {}".format(kwargs))

        #################################
        # Update Authorized Instruments #
        #################################
        left_ins, right_ins = ufun.checkLBTAuthorization(self.obsController)
        
        #####################
        # Enumerating setup #
        #####################

        if self.obsController:

            if kwargs:
                logger.info("Context arguments")
             
                for element, value in kwargs.items():
                    logger.info("{:>22} : {:<22}".format(element, str(value)))

            if info_s:
                logger.info("Info Setup")
                for element, value in info_s.items():
                    logger.info("{:>22} : {:<22}".format(element, str(value)))
                self.obsController.setContext(info_s) #Passing info to OS , not to template script
            # else:
            #     keys = ["OBJECT", "OBSERVER", "PARTNER", "COMMENT", "PROPID", "PI_COI"]
            #     info_s = {}
            #     for k in keys:
            #         info_s[k] = ""
            #     self.obsController.setContext(info_s) #Passing info to OS , not to template script
            if ins_s:
                logger.info("Instrument Setup")
                for element, value in ins_s.items():
                    logger.info("{:>22} : {:<22}".format(element, str(value)))
            if det_s:
                logger.info("Detector Setup")
                for element, value in det_s.items():
                    logger.info("{:>22} : {:<22}".format(element, str(value)))
            if tel_s:
                logger.info("Telescope Setup")
                for element, value in tel_s.items():
                    logger.info("{:>22} : {:<22}".format(element, str(value)))
            if rtc_s:
                logger.info("Tiptilt Setup")
                for element, value in rtc_s.items():
                    logger.info("{:>22} : {:<22}".format(element, str(value)))

            if (ins_s and det_s):
                if "InstrumentMode" in ins_s:
                    det_s["InstrumentMode"] = ins_s["InstrumentMode"]
                    logger.debug('Instrument Mode correctly passed to Detector Setup')
                else:
                    logger.debug('No InstrumentMode specified, GEN as default')
            ####################
            # Running template #
            ####################

            try:
                self.run(ins_s, det_s, tel_s, rtc_s, **kwargs)
            except Exception as e:
                logger.error("Cannot run template. I call abort()")
                logger.error(e)

                self.abort()

            logger.debug("Shinsapp: just exit run, status is: {}".format(self.status))
            logger.debug("Shinsapp: just exit run, filenames are: {}".format(self.filenames))
            logger.debug("Shinsapp: just exit run, other is: {}".format(self.other))

            self.show_template_output()
            
        if(self._status == uvars.EXIT_SUCCESS):
            logger.info("ShinsApp:  main successfully completed")
        else:
            logger.info("ShinsApp: main completed with errors")
            #raise exception.TemplateException("Shinsapp: Template exception raised")
            
        ##########################
        # Closing the connection #
        ##########################

        self.close_ice()

        return self._status, self.filenames, self.other


    def abort(self):
        '''
        Abort the template.
        '''

        msg = "Aborting current template"
        logger.error(msg)
        self.error.append(msg)
        self.obsController.emergencyAbortAll()
        logger.error("EmergencyAbortAll method done")
        self.close_ice()
        self.aborted = True
        raise exception.TemplateException("Shinsapp: Template exception raised")


    @property
    def status(self):
        '''Get property status (FAILURE/SUCCESS)'''
        return self._status


    @status.setter
    def status(self, value):
        '''Set property status (FAILURE/SUCCESS)'''
        if value > 0:
            self._status = uvars.EXIT_FAILURE # failure
        if value <= 0:
            self._status = uvars.EXIT_SUCCESS # success
