from subsystems import Subsystem
import types
import numpy as np
import logging
import time
class CBPKeithley(Subsystem):
    expected_params = {'exptime': 1.0}
    tracked_properties=[]

    def exposure(self, exposure, fitsbuilder):
        params = self._process_params(exposure)
        charge = self.server.get_charge_timeseries(params['exptime'])
        fitsbuilder.add_table('PHOTOCOUNT', np.rec.fromarrays(charge, names=['time', 'phd']))

class GenericMount(Subsystem):
    ''' Implement default backlash correction
    You need to set variables "prop_backlash" for every tracked property "prop"
    '''
    def _default_setter_for_tracked_properties(self, name):
        def func(self, value):
            v = getattr(self, '_'+name)
            if value == 'IDLE':
                return v
            if v != value:
                setter = getattr(self.server, 'set_' + name)
                backlash = getattr(self, name + '_backlash')
                if (((v < value) and (backlash > 0)) or
                    ((v > value) and (backlash <0))):
                    logging.debug('%s: Setting %s to %s (backlash move)' % (self.name, name, str(value+backlash)))
                    setter(value+backlash)
                    time.sleep(0.1)
                logging.debug('%s: Setting %s to %s' % (self.name, name, str(value)))                    
                setter(value)
                setattr(self, '_' + name, value)
            return value
        return types.MethodType(func, self)

        
class GenericEQMount(GenericMount):
    expected_params = {'alpha': 'IDLE',
                       'delta': 'IDLE',}
    tracked_properties=['alpha', 'delta']
    alpha_backlash = 16*150
    delta_backlash = 0#16*250
    
class GenericAltAzMount(GenericMount):
    tracked_properties = ['alt', 'az']
    expected_params = {'alt': 'IDLE',
                       'az': 'IDLE'}
    alt_backlash = 0
    az_backlash = 15
    
class NewportRotator(Subsystem):
    tracked_properties = ['pos']
    expected_params = {'pos': 'IDLE'
    }

    def sync_tracked_properties(self):
        self.set_pos(0)
        
    def set_pos(self, pos):
        if pos != 'IDLE':
            self._pos = pos
            self.server.move_absolute(pos)
        return self._pos
    
    def get_pos(self):
        return self._pos
    
class CBPLaser(Subsystem):
    tracked_properties = ['wavelength']
    expected_params = {'wavelength': 'IDLE'}
        

class CBPFlipper(Subsystem):
    expected_params = {'open': True,
                       'exptime': 1}
    def exposure(self, exposure, fitsbuilder):
        res = self._process_params(exposure)
        if res['open']:
            self.server.run_flipper(2)
        time.sleep(res['exptime'])
        if res['open']:
            self.server.run_flipper(1)
            
class CBPWheel(Subsystem):
    tracked_properties=[
        'mask',
        'pinhole']
    expected_params={
        'pinhole': 'grid',
        'mask':2,
    }

    pinhole_set = ['pinhole', 'empty', 'grid', 'empty2', 'big pinhole']
    pinhole_map = dict([(p,i) for i, p in enumerate(pinhole_set)])
    maskmap = {'filter': 0,
               'another filter': 1,
               'field stop': 2,
               'black filter': 3,
               'blue filter': 4,
               }
    
    def sync_tracked_properties(self):
        self._mask, self._pinhole = self.server.get_position()
        self._pinhole = self.pinhole_set[self._pinhole]

    def do_position(self):
        self.server.do_position(self._mask, self.pinhole_map[self._pinhole])
        
    def set_mask(self, value):
        if value == 'IDLE':
            return self._mask
        if self._mask != value:
            logging.debug('Setting mask to %s' % (str(value)))
            self._mask = value
            self.do_position()
        return value

    def set_pinhole(self, value):
        if value == 'IDLE':
            return self._pinhole
        if self._pinhole != value:
            logging.debug('Setting pinhole to %s' % (str(value)))
            self._pinhole = value
            self.do_position()
        return value


class CBPSpectro(Subsystem):
    expected_params = {'exptime': 1.}
    tracked_properties=[]

    def integration_loop(self, exptime):
        self.spectra = []
        while(self._integrate):
            self.spectra.append(self.server.get_spectrograph(int(exptime * 1e6)))

    def non_blocking_integration_loop(self, exptime):
        from threading import Thread
        self.p = Thread(target=self.integration_loop, args=(exptime,))
        self._integrate=True
        self.p.start()

    def exposure(self, exposure, fitsbuilder):
        params = self._process_params(exposure)
        self.non_blocking_integration_loop(params['exptime'])
        #spec = self.server.get_spectrograph(int(params['exptime'] * 1e6))      
        #fitsbuilder.add_table('SPECTRUM', np.rec.fromarrays(spec, names=['wl', 'flux']))

    def join(self):
        self._integrate=False
        self.p.join()
        return np.rec.fromarrays([self.spectra[0][0]]+[s[1] for s in self.spectra], names=['wl'] + [f'flux{n}' for n in range(len(self.spectra))])

    def after_exposure(self, exposure, fitsbuilder):
        fitsbuilder.add_table('SPECTRA', self.join())


#class CBP(CBPComponents):
#    tracked_properties=[
#        #'ndfilter',
#        #'wavelength',
#        'alt', 'az',
#        #'aperture',
#        'mask',
#        'pinhole']
#    expected_params={
#        #'wavelength': 'IDLE',
#        #'ndfilter': 'IDLE',
#        #'exptime': 1.0,
#        'pinhole': 'grid',
#        #'aperture': 'open',
#        'alt': 0.0,
#        'az': 0.0,
#    }
#    pinhole_map = {'big pinhole': 4,
#                   'empty': 3,
#                   'grid': 2,
#                   'empty2': 1,
#                   'pinhole': 0}
#    maskmap = {'filter': 0,
#               'another filter': 1,
#               'field stop': 2,
#               'black filter': 3,
#               'blue filter': 4,
#               }
#    def _config(self):
#        self._mask=2
#        self._pinhole='empty'
#        self._alt=0
#        self._az = 0
#        
#    def adjust_filters(self):
#        command = ['python', '/home/pi/Code/cbp_2/bin/filter_wheel.py', '--doPosition', '-m', str(self._mask), '-f', str(self.pinhole_map[self._pinhole])]
#        return self._remote_command(command)
#
#    def set_pinhole(self, pinhole):
#        if pinhole == 'IDLE':
#            return
#        self._pinhole=pinhole
#        return self.adjust_filters()
#        
#    def set_mask(self, mask):
#        if mask == 'IDLE':
#            return
#        self._mask=mask
#        return self.adjust_filters()
#
#    def set_wavelength(self, wavelength):
#        if wavelength == 'IDLE':
#            return
#        command = ['python', '/home/pi/Code/cbp_2/cbp/laser.py', wavelength]
#        self._wavelength=wavelength
#        return self._remote_command(command)
#
#    def tiptilt(self, axis, n):
#        command = ['python', '/home/pi/Code/cbp_2/bin/tiptilt.py', '--doSteps', '-m', str(axis), '-n', str(n)]
#        return self._remote_command(command)
#        #-m can be 1 or 2 (the two axes)
#        #-n is the number of steps
#        
#    def set_alt(self, n):
#        if n == 'IDLE':
#            return
#        else:
#            self._alt += n
#            return self.tiptilt(1, n)
#
#    def set_az(self, n):
#        if n == 'IDLE':
#            return None
#        else:
#            self._az += n
#            return self.tiptilt(2, n)
#        
#    def before_exposure(self, exposure, fitsbuilder):
#        super().before_exposure(exposure, fitsbuilder)
#    def exposure(self, exposure, fitsbuilder):
#        pass
#
#    def set_wavelength(self,wavelength):
#        pass
#
