#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'''
Sequencer module for Observation Blocks (OBs) in JSON format
'''

# System modules
import importlib
import json
import signal
import sys
from astropy.time import Time

# Custom modules
from utils.logger import log
# import templates


class Sequencer():
    '''
    Manage a sequence of JSON Observation Blocks (OBs)
    '''

    def __init__(self):
        '''Constructor'''
        self.ob = []
        self.ob_file = None
        self.error = []
        self.tpl = None # The running template
        signal.signal(signal.SIGINT, self.interrupt)
        self.original_sigint = signal.getsignal(signal.SIGINT)
        self.quitting = False

    def load_file(self, ob_file):
        '''Load a JSON file with an OB'''

        self.ob_file = ob_file

        with open(self.ob_file, encoding="utf-8") as jsonfile:
            log.info(f"SEQUENCER: Loading the ob from file {self.ob_file}")

            try:
                self.error = []
                self.ob = json.load(jsonfile)
            except json.decoder.JSONDecodeError as e:
                msg = "SEQUENCER: Probably malformed json"
                log.error(msg)
                log.error(e)
                self.error.append(msg)
                self.error.append(e)
            except UnicodeDecodeError as e:
                msg = "SEQUENCER: Not a text file"
                log.error(msg)
                log.error(e)
                self.error.append(msg)
                self.error.append(e)
            except Exception as e:
                msg = "SEQUENCER: Not handled exception"
                log.error(msg)
                log.error(e)
                self.error.append(msg)
                self.error.append(e)
                raise SystemExit(e) from e


    def execute(self):
        '''Execute the template(s) contained in the loaded OB'''
        self.quitting = False
        
        ob = self.ob if isinstance(self.ob, list) else [self.ob]
        
        log.info(f"There are {len(ob)} templates in the ob")

        now = Time.now()

        for template in ob:
            if self.quitting:
                break
            log.info("--------------------------------------------")
            try:
                self.error = []

                # Importing the module given its name in the json.
                tplmodule = importlib.import_module(
                    "templates." + template["template"])

                # tplmodule = getattr(templates, template["template"])

                # Every template module has a Template() class.
                self.tpl = tplmodule.Template()

                # Run the template given its parameters in the json.
                self.tpl.run(template["params"])

            except AttributeError as e:
                msg = "SEQUENCER: Attribute Error"
                log.error(msg)
                log.error(e)
                self.error.append(msg)
                self.error.append(e)

            except KeyError as e:
                msg = f"SEQUENCER: {e} is not defined"
                log.error(msg)
                self.error.append(msg)

        log.info("--------------------------------------------")
        log.debug(f"Total elapsed time: {(Time.now()-now).sec :.2f}s")



    def interrupt(self, signal, frame):
        '''Intercept a CTRL+C instead of raising a KeyboardInterrupt
        exception in order to be able to operate on the template class, for example to
        modify a pause attribute or call a method.

        '''

        #signal.signal(signal.SIGINT, self.original_sigint)

        try:
            answer = input("(P)ause, (R)esume, (N)ext template, (Q)uit:\n")

            if answer.lower().startswith('p'):
                self.pause()
            if answer.lower().startswith('r'):
                self.resume()
            if answer.lower().startswith('n'):
                self.abort()
            if answer.lower().startswith('q'):
                self.quit()
            else:
                pass

        except (KeyboardInterrupt, RuntimeError):
            # RuntimeError is catched because the
            # original_sigint strategy does not work

            self.quit()


    def pause(self):
        '''
        Put the pause attribute in the running template to True.
        It will be checked in a template method.
        '''
        if self.tpl:
            log.warning(f"SEQUENCER: pausing template {self.tpl.name} in ob {self.ob_file}")
            self.tpl.paused = True


    def resume(self):
        '''
        Restore the pause attribute in the template class to its
        original state. It will be checked in a template method.
        '''

        if self.tpl:
            log.warning(f"SEQUENCER: resuming template {self.tpl.name} in ob {self.ob_file}")
            self.tpl.paused = False


    def abort(self):
        '''
        Abort the running template. The sequencer will pass to the
        follwing one, if any.
        '''

        if self.tpl:
            msg = f"SEQUENCER: Aborting template {self.tpl.name} in OB {self.ob_file}"
            log.error(msg)
            self.error.append(msg)
            self.tpl.aborted = True


    def quit(self):
        '''Abort the execution of the whole OB'''
        msg = f"SEQUENCER: Aborting the OB {self.ob_file} and quit the sequencer"
        log.error(msg)
        self.error.append(msg)
        self.quitting = True # Flask doesn't take sys.exit??
        
        try:
            self.abort()
        except Exception as e:
            msg = f"SEQUENCER: Error trying to abort {self.ob_file}. Exiting anyway."
            log.error(msg)
            log.error(e)
            self.error.append(msg)
            self.error.append(e)
            
        sys.exit(1)
        

if __name__ == "__main__":

    seq = Sequencer()
    filename = sys.argv[1]
    seq.load_file(filename)
    seq.execute()
