# -*- coding: utf-8 -*-
"""
    lantz.drivers.legacy.coherent.innova
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Implements the drivers for Innova 300 Series gas lasers.


    Implementation Notes
    --------------------

    There are currently 3 drivers implemented Innova300C, ArgonInnova300C
    and KryptonInnova300C. The last two only add to the first the
    corresponding wavelength selection.

    Sources::

        - Innova 300C Manual

    :copyright: 2015 by Lantz Authors, see AUTHORS for more details.
    :license: BSD, see LICENSE for more details.

"""


from lantz import Q_, Action, Feat, DictFeat
from lantz.errors import InvalidCommand
from lantz.drivers.legacy.serial import SerialDriver


def make_feat(command, **kwargs):

    def get(self):
        return self.query('PRINT {}'.format(command))

    def set(self, value):
        return self.query('{}={}'.format(command, value))

    if kwargs.pop('readonly', None):
        return Feat(fget=get, **kwargs)
    elif kwargs.pop('writeonly', None):
        return Feat(fset=set, **kwargs)

    return Feat(get, set, **kwargs)


class Innova300C(SerialDriver):
    """Innova300 C Series.
    """

    ENCODING = 'ascii'

    SEND_TERMINATION = '\r\n'
    RECV_TERMINATION = '\r\n'


    def __init__(self, port=1, baudrate=1200, **kwargs):
        super().__init__(port, baudrate, bytesize=8, parity='None',
                         stopbits=1, **kwargs)

    def initialize(self):
        super().initialize()
        self.echo_enabled = False

    def query(self, command, *, send_args=(None, None), recv_args=(None, None)):
        """Send query to the laser and return the answer, after handling
        possible errors.

        :param command: command to be sent to the instrument
        :type command: string

        :param send_args: (termination, encoding) to override class defaults
        :param recv_args: (termination, encoding) to override class defaults
        """
        ans = super().query(command, send_args=send_args, recv_args=recv_args)
        # TODO: Echo handling
        if ans == 'Out of Range':
            raise ValueError()
        elif ans.startswith('Syntax Error'):
            raise InvalidCommand()
        elif ans == 'Laser must be off':
            raise Exception('Laser must be off')

        return ans


    # General information and communication

    idn = make_feat('ID',
                    readonly=True,
                    doc='Laser identification, should be I300.',
                    read_once=True)

    software_rev = make_feat('SOFTWARE',
                             readonly=True,
                             doc='Software revision level in the power supply.',
                             read_once=True)

    head_software_rev = make_feat('HEAD SOFTWARE',
                                  readonly=True,
                                  doc='Software revision level in the laser head board.',
                                  read_once=True)

    echo_enabled = make_feat('ECHO',
                             writeonly=True,
                             doc='Echo mode of the serial interface.',
                             values={True: 1, False: 0})

    baudrate = Feat(values={110, 300, 1200, 2400, 4800, 9600, 19200})

    @baudrate.setter
    def baudrate(self, value):
        """RS-232/422 baud rate, the serial connection will be reset after.
        """
        self.query('BAUDRATE={}'.format(value))
        #TODO: RESET Connection


    # Interface

    analog_relative = make_feat('ANALOG MODE',
                                doc='Analog Interface input mode.',
                                values={True: 1, False: 0})

    analog_enabled = make_feat('ANALOGINT',
                               doc='Analog Interface input state.',
                               values={True: 1, False: 0})

    current_range = make_feat('CURRENT RANGE',
                              doc='Current corresponding to 5 Volts at the input'\
                                  ' or output lines of the Analog Interface.',
                              units='A',
                              limits=(10, 100, 1))

    control_pin_high = make_feat('CONTROL',
                                 readonly=True,
                                 doc='State of the input pin 10 of the Analog Interface.',
                                 values={True: 1, False: 0})

    output_pin_high = make_feat('STATUS',
                                doc='State of the output pin 24 and 25 of the Analog Interface.',
                                values={(False, False): 0, (True, False): 1,
                                     (False, True): 2, (True, True): 3})

    # Diagnostics

    @Feat()
    def faults(self):
        """List of all active faults.
        """
        return self.query('PRINT FAULT').split('&')

    autofill_delta = make_feat('AUTOFILL DELTA',
                               readonly=True,
                               doc='Tube voltage minus the autofill setting.',
                               units='V')

    autofill_needed = make_feat('AUTOFILL STATUS',
                                readonly=True,
                                doc='Is the autofill needed (wheter fill is enabled or not)',
                                values={True: 1, False: 0})

    remaining_time = make_feat('HRSTILSHUTDOWN',
                               readonly=True,
                               doc='Number of hours remaining before the laser '\
                                   'will shut down automatically.',
                               units='hour')

    cathode_current = make_feat('CATHODE CURRENT',
                                readonly=True,
                                doc='Laser cathode current (AC).',
                                units='A')

    cathode_voltage = make_feat('CATHODE VOLTAGE',
                                readonly=True,
                                doc='Laser cathode voltage (AC).',
                                units='V')

    time_to_start = make_feat('START',
                              readonly=True,
                              doc='Timer countdown during the start delay cycle.',
                              units='second')

    @Feat()
    def is_in_start_delay(self):