#!/usr/bin/python
'''xmlrpc wrapper around the python-zwoasi wrapper'''

import ctypes
import datetime
import matplotlib.pyplot as plt
import numpy as np
import os
import stardice.daemon_servers
import time
import xmlrpc.client
import zwoasi
from zwoasi import zwolib
try:
    import pyfits
except ImportError:
    import astropy.io.fits as pyfits

if zwolib is None:
    zwoasi.init(os.path.expanduser('~/ASICAP/lib/libASICamera2.so'))
from zwoasi import zwolib

class AsiServer(object):
    def __init__(self):
        try:
            self.connect()
        except:
            self.connect()
        
    def default(self):
        self.disable_dark_subtract()
        self.set_image_type(zwoasi.ASI_IMG_RAW16)
        self._prepare_buffer()
        self.set_exptime(1)
    
    def connect(self, cam=0):
        self.cam = zwoasi.Camera(cam)
        for method in ['get_camera_property', 'get_controls', 'disable_dark_subtract', 'enable_dark_subtract', 'get_control_value', 'get_control_values', 'set_control_value']:
            setattr(self, method, getattr(self.cam, method))

    def list_cameras(self):
        return zwoasi.list_cameras()

    def _prepare_buffer(self):
        self.whbi = zwoasi._get_roi_format(self.cam.id)
        self.sz = self.whbi[0] * self.whbi[1] * self.nbytes
        self.buffer_1 = bytearray(self.sz)
        cbuf_type = ctypes.c_char * len(self.buffer_1)
        self.cbuf1 = cbuf_type.from_buffer(self.buffer_1)
        self.im1 = np.frombuffer(self.buffer_1, dtype='uint16')
        
    def set_image_type(self, val):
        self.cam.set_image_type(val)
        self.nbytes = {zwoasi.ASI_IMG_RAW16:2, zwoasi.ASI_IMG_RAW8:1}[val]

    def take_frame(self, is_dark=False):
        r = zwolib.ASIStartExposure(self.cam.id, is_dark)
        if r:
            raise zwoasi.zwo_errors[r]
        time.sleep((self.exptime)*1e-6)
        i = 0
        while (self.cam.get_exposure_status() != 2):
            print(('Data not ready %d ms' % i))
            time.sleep(0.01)
            i = i+10
        r = zwolib.ASIGetDataAfterExp(self.cam.id, self.cbuf1, self.sz)
        if r:
            raise zwoasi.zwo_errors[r]
        return self.im1.reshape(self.whbi[1::-1])

    def remote_exposure(self, is_dark=False):
        #self.set_exptime(int(exptime*1e6))
        utcnow = datetime.datetime.utcnow()
        data = self.take_frame(is_dark=is_dark)
        image = {'DATE-OBS': utcnow.isoformat(),
                 'EXPTIME': self.exptime,
                 # temperature information (Peltier module)
                 'shape': data.shape,
                 'binarydata': xmlrpc.client.Binary(self.buffer_1)
        }
        image.update(self.cam.get_camera_property())
        image.update(self.cam.get_control_values())
        return image
    
    def get_temperature(self):
        return self.cam.get_control_value(zwoasi.ASI_TEMPERATURE)[0]
    
    def photodiode(self):
        return float(self.take_frame()[500:1300,:].sum())

    def get_exptime(self):
        ''' Return exptime in seconds'''
        return self.cam.get_control_value(zwoasi.ASI_EXPOSURE)[0] * 1e-6
    
    def set_exptime(self, exptime):
        ''' Exptime in seconds
        '''
        self.exptime = int(exptime*1e6)
        self.cam.set_control_value(zwoasi.ASI_EXPOSURE, self.exptime)
        
    def get_gain(self):
        ''' Return gain'''
        return self.cam.get_control_value(zwoasi.ASI_GAIN)[0]
    
    def set_gain(self, gain):
        '''Digital gain between 0 and 600'''
        self.cam.set_control_value(zwoasi.ASI_GAIN, int(gain))
        
    def get_offset(self):
        self.cam.get_control_value(5)[0]
        
    def set_offset(self, offset):
        '''Digital bias between 0 and 250 ADUs'''
        self.cam.set_control_value(5, offset)
        
    def fits(self, filename, headerkeys={}):
        utcnow = datetime.datetime.utcnow()
        im = self.take_frame()
        f = pyfits.PrimaryHDU(im)
        f.header['DATE-OBS'] = utcnow.isoformat(),
        f.header.update(self.cam.get_camera_property())
        f.header.update(self.cam.get_control_values())
        f.header.update(headerkeys)
        f.writeto(filename)
        
    def status(self):
        res = self.cam.get_camera_property()
        res.update(self.cam.get_control_values())
        return res

#cam.set_control_value(zwoasi.ASI_HIGH_SPEED_MODE,0)

#cbuf2 = cbuf_type.from_buffer(buffer_2)

#Yim2 = np.frombuffer(buffer_2, dtype='uint16')
#titi = toto.reshape((-1,1936))
'''
if __name__=='__main__':
    c = AsiServer()
    c.connect()
    c.default()
    c.set_exptime(int(1e6))
    res = []
    for i in range(3600):
        res.append(c.take_frame()[500:1300,:].sum())
    res = np.array(res)
    np.save('stability.npy', res)
'''
if __name__ == "__main__":
    now = datetime.datetime.now()
    logdir = os.path.join(os.getenv("HOME"), "logs")
    logname = os.path.join(logdir, 
                           'zwoasi-server-%s.log' % now.date().isoformat())
    logsymlink = os.path.join(logdir, "zwoasi-server.log")
    if os.path.islink(logsymlink):
        try:
            os.unlink(logsymlink)
            os.symlink(logname, logsymlink)
        except OSError:
            pass

    import optparse 
    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('--dummy', default=False, 
                      action='store_true', 
                      help='Run a fake instance instead')
    parser.add_option('-p', '--port', default=8910, 
                      action='store', type='int', 
                      help='Listen on port')
    parser.add_option('-H', '--hostname', default='localhost', 
                      action='store', 
                      help='server address')
    parser.add_option('-l', '--log-file', default=logname, 
                      action='store', 
                      help='specify a log file')
    parser.add_option('-t', '--tty', default='/dev/encoders', 
                      dest='tty', action='store', 
                      help='specify the serial port')
    (options, args) = parser.parse_args()
    
    SERVER_HOSTNAME = options.hostname
    SERVER_PORT = options.port
    
    name = 'zwoasi-server'
    stardice.daemon_servers.setup_logging(name, logfile=options.log_file)
         
    asiserver = AsiServer()
    '''
    if options.dummy:
        instance = Encoders(port=options.tty)
    else:
        focuser_instance = Focuser(port=options.tty)
    server = FocusServer((SERVER_HOSTNAME, SERVER_PORT))
    '''
    server = stardice.daemon_servers.BasicServer((SERVER_HOSTNAME, SERVER_PORT), name, asiserver)
    if options.daemon:
        stardice.daemon_servers.daemonize(options, args, server)
    else:
        #pass
        server.main(options, args)

'''
cam.start_video_capture()
r = zwolib.ASIGetVideoData(cam.id, cbuf1, sz, int(timeout))
if r:
    raise zwo_errors[r]
tstart = time.time()
n=100
exp= [15000,30000,60000,120000,240000,480000,960000]
nexp = len(exp)
res = np.zeros((3, n*nexp))

for e_, exptime in enumerate(exp):
    cam.set_control_value(zwoasi.ASI_EXPOSURE, exptime)
    timeout = exptime+50000
    

    for i in range(n):
        r = zwolib.ASIGetVideoData(cam.id, cbuf1, sz, int(timeout))
        if r:
            raise zwo_errors[r]
        r = zwolib.ASIGetVideoData(cam.id, cbuf2, sz, int(timeout))
        if r:
            raise zwo_errors[r]
    
        #zwoasi._get_video_data(cam.id, timeout, buffer_)
        #time.sleep((exptime)*1e-6)
        res[:, i+e_*n] = im1.mean(), im2.mean(), (im1.astype(float)-im2.astype(float)).std()
    print exptime, res[0, i+e_*n]
cam.stop_video_capture()
tstop = time.time()
print tstop - tstart
#plt.plot((res[0,:]-res[1, :])**2, res[2,:], 'o')
goods = np.abs(res[0,:] - res[1, :]) < 1.
p = np.polyfit(res[1,goods] + res[0,goods], res[2, goods] ** 2, 3)
print p[-1]/p[-2]
plt.plot(res[1,goods] + res[0,goods], res[2, goods] ** 2, 'o')
val = np.linspace(0, res[0,:].max()*2)
plt.plot(val,np.polyval(p, val), 'k-')
plt.show()
'''
#plt.matshow(titi)
#plt.show()
