#!/usr/bin/env python 

import os
import datetime

import logging
from optparse import OptionParser
from stardice.orgfiles import fromorg
import fitsbuilder
import allsubsystems
from threading import Thread

#from importlib import reload
#reload(subsystems)

def dict_to_org(d):
    l = list(d.keys())
    l.sort()
    print(('|' + ' | '.join(l) + ' |'))
    print(('|' + ' | '.join([str(d[k]) for k in l]) + ' |'))
    
class Scheduler(object):
    def __init__(self, config, device_list=None):
        """
        Creates a scheduler instance that will handle a schedule and in particular
        handle taking the images

        Data will be saved in 
        [config.Output.dir]/year-month-day/
        """
        self.aborting = False
        self.current_jobs = []
        
        self.config = config
        if device_list is None:
            device_list = list(config.Subsystems.__dict__.items())
        else:
            device_list = [(name, getattr(config.Subsystems, name)) for name in device_list]
        self.subsystems = [(name, getattr(allsubsystems, comp)(config, name, self)) for name, comp in device_list]

        for name, s in self.subsystems:
            self.async_run(s.configure, [])
            #s.configure()
        self.join()
        now = datetime.datetime.now()
        output_dir_now = '%04d-%02d-%02d' % (now.year, now.month, now.day)
        self.output_dir = os.path.join(self.config.Output.path, output_dir_now)
        
        # For conveniency
        for name, sys in self.subsystems:
            setattr(self, name, sys)

        #self.analyse._late_config()

    def async_run(self, func, args):
        t = Thread(target=func, args=args)
        self.current_jobs.append(t)
        t.start()

    def join(self):
        while(self.current_jobs):
            t = self.current_jobs.pop()
            t.join()

    def shoot(self, state, output_dir=None):
        """
        Shoots an exposure: runs every subprocess before_exposure, exposure and after_exposure
        and dumps the exposure in output_dir.

        If output_dir isn't set, uses the scheduler output_dir
        """
        if output_dir is None:
            output_dir = self.output_dir
        
        fb = fitsbuilder.FitsBuilder(output_dir = output_dir)
        logging.info(str(state))
        for func in ['before_exposure', 'exposure', 'after_exposure']:
            for name, subsystem in self.subsystems:
                #print func, name
                if self.aborting:
                    raise StopIteration("Aborted observation program")
                if state[name]:
                    self.async_run(getattr(subsystem, func), [state, fb])
                    #getattr(subsystem, func)(state, fb)
            self.join()
        logging.info('dumping files -> %s' % output_dir)
        return fb.dump(output_dir=output_dir)
         
        
    def state(self):
        '''Print system status
        '''
        st = ''
        for name, subsystem in self.subsystems:
            st += '\n' + name + '\n----------\n'
            st += subsystem.state()
        return st
        

    def execute(self, program_file, output_dir=None, startline=0):
        from stardice.obsprog import prog_fields
        self.aborting = False
        logging.info('Executing program %s' % program_file)
        program, k = fromorg(program_file)
        program = program.astype(prog_fields)
        for exp, exposure in enumerate(program[startline:], startline + 1):
            self.accepted = False
            while not self.accepted:
                logging.info('Processing exposure %d/%d' % (exp, len(program)))
                filename = self.shoot(exposure, output_dir)
                yield filename, exposure
                
    def abort(self):
        self.aborting = True

    def mount_init(self):
        ''' Mount synchronisation with the result of last analysis
        '''
        fra, fdec = self.analyse.fra, self.analyse.fdec
        a = eval(input('Do you want set current pointing to %f,%f (y/n)? : ' % (fra,fdec)))
        if a == 'y':
            self.mount.set_radec(fra, fdec)
        return self.mount.get_radec()


    def get_default(self):
        res = {}
        for n, s in self.subsystems:
            res[n] = True
            for k in s.expected_params:
                res['%s.%s' % (n, k)] = s.expected_params[k]
        return res
    
if __name__ == "__main__":
    logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)
    import argparse
    from stardice import configfiles
    parser = argparse.ArgumentParser(
        description='Supervise the execution of an observation program')
    parser.add_argument(
        'programs', nargs='*',
        help='program to be executed')
    parser.add_argument(
        '-c', '--config', default='scheduler.cfg',
        help='Name of the configuration file')
    parser.add_argument(
        '-o', '--output-dir', default='./',
        help='Path to the directory where images will be stored. (Created if not existing)')
    parser.add_argument(
        '-r', '--release', default=False,
        help='if True, release the servers at the end of execution; set to False if you want to reuse the scheduler after the first execution.')

    args = parser.parse_args()
    
    config = configfiles.Config(args.config)
    output_dir = args.output_dir
    
    scheduler = Scheduler(config)
    for program in args.programs:
        print(("Executing program ", program))
        scheduler.execute(program, output_dir)
        
    #logging.info('Executing run...')

    
    #program.d_leds_calibration_unit[18].process(servers, output_dir)
    #program.B_leds.process(servers, output_dir)
    #program.V_leds.process(servers, output_dir)

    #program.R_leds.process(servers, output_dir)
    #program.I_leds.process(servers, output_dir)

    #program.d_faint_leds_calibration_unit[28].process(servers, output_dir)
    
    #logging.info('Done executing run')
    # if args.release:
    #     logging.info('Releasing servers...')
    #     servers.release()
    #     logging.info('Done releasing servers')

