import usb.core 
import struct
import numpy as np
import time
REQ =\
    b"\x37\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
    b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
    b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
    b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

class AtikEFW(object):
    idVendor = 0x04d8
    idProduct = 0x003f

    def __init__(self):
        self.dev = usb.core.find(idVendor=self.idVendor, idProduct=self.idProduct)
        if self.dev is None:
            raise RuntimeError('Cannot find usb atik EFW2')
        if self.dev.is_kernel_driver_active(0):
            self.dev.detach_kernel_driver(0)
        #self.dev.set_configuration()
        self.cfg = self.dev.get_active_configuration()
        self.intf = self.cfg[(0, 0)]
        # match the first OUT endpoint
        self.epo = usb.util.find_descriptor(
            self.intf, custom_match = lambda e:
            usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT)
        # match the first IN endpoint
        self.epi = usb.util.find_descriptor(
            self.intf, custom_match = lambda e:
            usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN)
        self.struct = struct.Struct('>BHBBB')
        self._parse()
        
    def _read(self):
        self.epo.write(REQ)
        return self.epi.read(64, timeout=4000)

    def _parse(self):
        _, _, self.moving, self.slot, self.nslots = self.struct.unpack(self._read()[:6])
        return self.moving, self.slot, self.nslots

    def _move(self, pos):
        if (pos < 9) and (pos >= 0):
            self.epo.write(struct.pack('>BB', 0x80, pos))
        else:
            raise ValueError(f'Filter wheel has only {self.slots} slots')

    def get_filter(self):
        ''' Return the id of the currently positionned slot

        A number between 0 and nslots-1
        '''
        return self.slot

    def set_filter(self, pos):
        ''' Position the requested filter

        Pos must be a number between 0 and nslots-1 (raise ValueError)
        The call blocks until the requested filter is in place
        '''
        self._move(pos)
        self._parse()
        while (self.moving or (self.slot != pos)):
            time.sleep(0.1)
            self._parse()
        return self.slot

    def get_nslots(self):
        ''' Return the number of slots in the filterwheel
        '''
        return self.nslots

if __name__ == '__main__':
    import datetime
    import os
    import stardice.daemon_servers
    import optparse
    
    now = datetime.datetime.now()
    logdir = os.path.join(os.getenv("HOME"), "logs")
    logname = os.path.join(logdir, 
                           'atikefw-server-%s.log' % now.date().isoformat())
    logsymlink = os.path.join(logdir, "atikefw-server.log")
    if os.path.islink(logsymlink):
        try:
            os.unlink(logsymlink)
            os.symlink(logname, logsymlink)
        except OSError:
            pass
    
    parser = optparse.OptionParser(usage="%prog [-l log] [-d]")
    parser.add_option('-d', '--daemon', default=False, 
                      action='store_true', 
                      help='Run as a background daemon')
    parser.add_option('-p', '--port', default=8000, 
                      action='store', type='int', 
                      help='Listen on port')
    #parser.add_option('-H', '--hostname', default=os.environ['HOSTNAME'], 
    #                  help='server address')
    #                  action='store', 
    parser.add_option('-l', '--log-file', default=logname, 
                      action='store', 
                      help='specify a log file')
    (options, args) = parser.parse_args()
    
    #SERVER_HOSTNAME = options.hostname
    SERVER_PORT = options.port
    
    name = 'atikefw-server'
    stardice.daemon_servers.setup_logging(name, logfile=options.log_file)
         
    atikefwserver = AtikEFW()
    
    #server = stardice.daemon_servers.BasicServer((SERVER_HOSTNAME, SERVER_PORT), name, atikefwserver)
    
    #if options.daemon:
    #    stardice.daemon_servers.daemonize(options, args, server)
    #else:
    #    server.main(options, args)

