#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Dec  6 11:51:18 2017

@author: l_frojdh
"""

from .detector import Detector
from .temperature import Temperature, DetectorTemperature
from .dacs import DetectorDacs
import _slsdet
dacIndex = _slsdet.slsDetectorDefs.dacIndex
from .detector_property import DetectorProperty

class EigerVcmp:
    """
    Convenience class to be able to loop over vcmp for Eiger
    
    .. todo::
        
        Support single assignment and perhaps unify with Dac class
    
    """
    
    def __init__(self, detector):
        _dacs = [ dacIndex.VCMP_LL,
                  dacIndex.VCMP_LR,
                  dacIndex.VCMP_RL,
                  dacIndex.VCMP_RR]
        self.set = []
        self.get = []
        for i in range(detector.size()):
            if i % 2 == 0:
                dacs = _dacs
            else:
                dacs = _dacs[::-1]
            for d in dacs:
                self.set.append(lambda x, d=d, i=i : detector.setDAC(d, x, False, [i]))
                self.get.append(lambda d=d, i=i : detector.getDAC(d, False, [i])[0])
    
    def __getitem__(self, key):
        if key == slice(None, None, None):
            return [_d() for _d in self.get]
        return self.get[key]()
    
    def __setitem__(self, i, value):
        self.set[i](value)

    def __repr__(self):
        return 'vcmp: '+ str(self[:])


class EigerDacs(DetectorDacs):
    """
    Eiger specific dacs
    """
    _dacs = [('vsvp',     dacIndex.VSVP,0, 4000,    0),
             ('vtrim',    dacIndex.VTRIM,0, 4000, 2500),
             ('vrpreamp', dacIndex.VRPREAMP,0, 4000, 3300),
             ('vrshaper', dacIndex.VRSHAPER,0, 4000, 1400),
             ('vsvn',     dacIndex.VSVN,0, 4000, 4000),
             ('vtgstv',   dacIndex.VTGSTV,0, 4000, 2556),
             ('vcmp_ll',  dacIndex.VCMP_LL,0, 4000, 1500),
             ('vcmp_lr',  dacIndex.VCMP_LR,0, 4000, 1500),
             ('vcal',    dacIndex.VCAL,0, 4000, 4000),
             ('vcmp_rl',  dacIndex.VCMP_RL,0, 4000, 1500),
             ('rxb_rb',   dacIndex.RXB_RB,0, 4000, 1100),
             ('rxb_lb',   dacIndex.RXB_LB,0, 4000, 1100),
             ('vcmp_rr',  dacIndex.VCMP_RR,0, 4000, 1500),
             ('vcp',      dacIndex.VCP,0, 4000,  200),
             ('vcn',      dacIndex.VCN,0, 4000, 2000),
             ('vishaper', dacIndex.VISHAPER,0, 4000, 1550),
             ('iodelay',  dacIndex.IO_DELAY,0, 4000,  660)]
    _dacnames = [_d[0] for _d in _dacs]


from .detector import freeze

@freeze
class Eiger(Detector):
    """
    Subclassing Detector to set up correct dacs and detector specific 
    functions. 
    """
    _detector_dynamic_range = [4, 8, 16, 32]


    _settings = ['standard', 'highgain', 'lowgain', 'veryhighgain', 'verylowgain']
    """available settings for Eiger, note almost always standard"""

    def __init__(self, id=0):
        super().__init__(id)
        self._frozen = False 
        self._dacs = EigerDacs(self)
        self._vcmp = EigerVcmp(self)

        # Eiger specific adcs
        self._temp = DetectorTemperature()
        self._temp.fpga = Temperature('temp_fpga', dacIndex.TEMPERATURE_FPGA, self)
        self._temp.fpgaext = Temperature('temp_fpgaext', dacIndex.TEMPERATURE_FPGAEXT, self)
        self._temp.t10ge = Temperature('temp_10ge', dacIndex.TEMPERATURE_10GE, self)
        self._temp.dcdc = Temperature('temp_dcdc', dacIndex.TEMPERATURE_DCDC, self)
        self._temp.sodl = Temperature('temp_sodl', dacIndex.TEMPERATURE_SODL, self)
        self._temp.sodr = Temperature('temp_sodl', dacIndex.TEMPERATURE_SODR, self)
        self._temp.temp_fpgafl = Temperature('temp_fpgafl', dacIndex.TEMPERATURE_FPGA2, self)
        self._temp.temp_fpgafr = Temperature('temp_fpgafr', dacIndex.TEMPERATURE_FPGA3, self)



    @property
    def dacs(self):
        """

        An instance of DetectorDacs used for accessing the dacs of a single
        or multi detector.

        Examples
        ---------

        ::

            d = Eiger()

            #Set all vrf to 1500
            d.dacs.vrf = 1500

            #Check vrf
            d.dacs.vrf
            >> vrf       :  1500,  1500

            #Set a single vtr
            d.dacs.vtr[0] = 1800

            #Set vrf with multiple values
            d.dacs.vrf = [3500,3700]
            d.dacs.vrf
            >> vrf       :  3500,  3700

            #read into a variable
            var = d.dacs.vrf[:]

            #set multiple with multiple values, mostly used for large systems
            d.dacs.vcall[0,1] = [3500,3600]
            d.dacs.vcall
            >> vcall     :  3500,  3600

            d.dacs
            >>
            ========== DACS =========
            vsvp      :     0,     0
            vtr       :  4000,  4000
            vrf       :  1900,  1900
            vrs       :  1400,  1400
            vsvn      :  4000,  4000
            vtgstv    :  2556,  2556
            vcmp_ll   :  1500,  1500
            vcmp_lr   :  1500,  1500
            vcall     :  4000,  4000
            vcmp_rl   :  1500,  1500
            rxb_rb    :  1100,  1100
            rxb_lb    :  1100,  1100
            vcmp_rr   :  1500,  1500
            vcp       :  1500,  1500
            vcn       :  2000,  2000
            vis       :  1550,  1550
            iodelay   :   660,   660

        """
        return self._dacs


    @property
    def vcmp(self):
        """
        Convenience function to get and set the individual vcmp of chips
        Used mainly in the calibration code.

        Examples
        ---------

        ::

            #Reading
            d.vcmp[:]
            >> [500, 500, 500, 500, 500, 500, 500, 500]

            #Setting
            d.vcmp = [500, 500, 500, 500, 500, 500, 500, 500]


        """

        return self._vcmp
    
    @vcmp.setter
    def vcmp(self, values):
        if len(values) == len(self._vcmp.set):
            for i, v in enumerate(values):
                self._vcmp.set[i](v)
        else:
            raise ValueError('vcmp only compatible with setting all')

#     @property
#     def rx_udpport(self):
#         """
#         UDP port for the receiver. Each module has two ports referred to
#         as rx_udpport and rx_udpport2 in the command line interface
#         here they are grouped for each detector
        
#         ::
            
#             [0:rx_udpport, 0:rx_udpport2, 1:rx_udpport ...]
        
#         Examples
#         -----------
        
#         ::
        
#             d.rx_udpport
#             >> [50010, 50011, 50004, 50005]
            
#             d.rx_udpport = [50010, 50011, 50012, 50013]
        
#         """
#         p0 = self._api.getReceiverUDPPort()
#         p1 = self._api.getReceiverUDPPort2()
#         return [int(val) for pair in zip(p0, p1) for val in pair]
    
#     @rx_udpport.setter
#     def rx_udpport(self, ports):
#         """Requires iterating over elements two and two for setting ports"""
#         a = iter(ports)
#         for i, p in enumerate(zip(a, a)):
#             self._api.setReceiverUDPPort(p[0], i)
#             self._api.setReceiverUDPPort2(p[1], i)

    @property
    def rx_zmqport(self):
        """
        Return the receiver zmq ports. Note that Eiger has two ports per receiver!
        This functions therefore differ from the base class.

        ::

            e.rx_zmqport
            >> [30001, 30002, 30003, 30004]


        """
        ports = self.getRxZmqPort()
        return [p + i for p in ports for i in range(2)]


#     @rx_zmqport.setter
#     def rx_zmqport(self, port):
#         if isinstance(port, Iterable):
#             for i, p in enumerate(port):
#                 self._api.setReceiverStreamingPort(p, i)
#         else:
#             self._api.setReceiverStreamingPort(port, -1)

    @property
    def temp(self):
        """
        An instance of DetectorAdcs used to read the temperature
        of different components

        Examples
        -----------

        ::

            detector.temp
            >>
            temp_fpga     :  36.90°C,  45.60°C
            temp_fpgaext  :  31.50°C,  32.50°C
            temp_10ge     :   0.00°C,   0.00°C
            temp_dcdc     :  36.00°C,  36.00°C
            temp_sodl     :  33.00°C,  34.50°C
            temp_sodr     :  33.50°C,  34.00°C
            temp_fpgafl   :  33.81°C,  30.93°C
            temp_fpgafr   :  27.88°C,  29.15°C

            a = detector.temp.fpga[:]
            a
            >> [36.568, 45.542]


        """
        return self._temp

