from collections import OrderedDict

from . import (EpicsSignalRO, EpicsSignal, Component as Cpt,
               DynamicDeviceComponent as DDCpt, Signal,
               Kind, kind_context)
from .areadetector import (EpicsSignalWithRBV,
                           ImagePlugin, StatsPlugin, DetectorBase,
                           SingleTrigger, ADBase)
from .status import DeviceStatus


def _current_fields(attr_base, field_base, range_, **kwargs):
    defn = OrderedDict()
    for i in range_:
        attr = '{attr}{i}'.format(attr=attr_base, i=i)
        suffix = '{field}{i}'.format(field=field_base, i=i)
        defn[attr] = (EpicsSignal, suffix, kwargs)

    return defn


class QuadEMPort(ADBase):
    port_name = Cpt(Signal, value='')

    def __init__(self, port_name, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.port_name.put(port_name)


class QuadEM(SingleTrigger, DetectorBase):
    # These settings intentionally shadow the settings inherited from
    # DetectorBase.
    _default_read_attrs = None
    _default_configuration_attrs = None

    _status_type = DeviceStatus  # overrriding the default in SingleTrigger

    # This is needed because ophyd verifies that it can see all
    # of the nodes in the asyn pipeline, however these IOCs do not
    # expose their port name via a PV, but nevertheless server as the
    # root node for the plugins.
    # Leaving this port_name here for compatibility
    integration_time = Cpt(EpicsSignalWithRBV, 'IntegrationTime',
                           kind='config')
    averaging_time = Cpt(EpicsSignalWithRBV, 'AveragingTime', kind='config')
    with kind_context('omitted') as OCpt:
        conf = Cpt(QuadEMPort, port_name='NSLS_EM')
        model = OCpt(EpicsSignalRO, 'Model')
        firmware = OCpt(EpicsSignalRO, 'Firmware')

        acquire_mode = OCpt(EpicsSignalWithRBV, 'AcquireMode')
        acquire = OCpt(EpicsSignal, 'Acquire')

        read_format = OCpt(EpicsSignalWithRBV, 'ReadFormat')
        em_range = OCpt(EpicsSignalWithRBV, 'Range')
        ping_pong = OCpt(EpicsSignalWithRBV, 'PingPong')

        num_channels = OCpt(EpicsSignalWithRBV, 'NumChannels')
        geometry = OCpt(EpicsSignalWithRBV, 'Geometry')
        resolution = OCpt(EpicsSignalWithRBV, 'Resolution')

        bias_state = OCpt(EpicsSignalWithRBV, 'BiasState')
        bias_interlock = OCpt(EpicsSignalWithRBV, 'BiasInterlock')
        bias_voltage = OCpt(EpicsSignalWithRBV, 'BiasVoltage')
        hvs_readback = OCpt(EpicsSignalRO, 'HVSReadback')
        hvv_readback = OCpt(EpicsSignalRO, 'HVVReadback')
        hvi_readback = OCpt(EpicsSignalRO, 'HVIReadback')

        values_per_read = OCpt(EpicsSignalWithRBV, 'ValuesPerRead')
        sample_time = OCpt(EpicsSignalRO, 'SampleTime_RBV')  # yay consistency
        num_average = OCpt(EpicsSignalRO, 'NumAverage_RBV')
        num_averaged = OCpt(EpicsSignalRO, 'NumAveraged_RBV')
        num_acquire = OCpt(EpicsSignalWithRBV, 'NumAcquire')
        num_acquired = OCpt(EpicsSignalRO, 'NumAcquired')
        read_data = OCpt(EpicsSignalRO, 'ReadData')
        ring_overflows = OCpt(EpicsSignalRO, 'RingOverflows')
        trigger_mode = OCpt(EpicsSignal, 'TriggerMode')
        reset = OCpt(EpicsSignal, 'Reset')

    current_names = DDCpt(_current_fields('ch', 'CurrentName', range(1, 5),
                                          string=True))
    current_offsets = DDCpt(_current_fields('ch', 'CurrentOffset',
                                            range(1, 5)))
    current_offset_calcs = DDCpt(_current_fields('ch', 'ComputeCurrentOffset',
                                                 range(1, 5)))
    current_scales = DDCpt(_current_fields('ch', 'CurrentScale', range(1, 5)))

    position_offset_x = Cpt(EpicsSignal, 'PositionOffsetX')
    position_offset_y = Cpt(EpicsSignal, 'PositionOffsetY')

    position_offset_calc_x = Cpt(EpicsSignal, 'ComputePosOffsetX')
    position_offset_calc_y = Cpt(EpicsSignal, 'ComputePosOffsetY')

    position_scale_x = Cpt(EpicsSignal, 'PositionScaleX')
    position_scale_Y = Cpt(EpicsSignal, 'PositionScaleY')

    image = Cpt(ImagePlugin, 'image1:')
    current1 = Cpt(StatsPlugin, 'Current1:')
    current2 = Cpt(StatsPlugin, 'Current2:')
    current3 = Cpt(StatsPlugin, 'Current3:')
    current4 = Cpt(StatsPlugin, 'Current4:')

    sum_all = Cpt(StatsPlugin, 'SumAll:')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.stage_sigs.update([('acquire', 0),  # if acquiring, stop
                                ('acquire_mode', 2)  # single mode
                                ])
        self._acquisition_signal = self.acquire

        for i in range(1, 5):
            current = getattr(self, 'current{}'.format(i))
            current.mean_value.kind = Kind.hinted


class NSLS_EM(QuadEM):
    ...


class TetrAMM(QuadEM):
    conf = Cpt(QuadEMPort, port_name='TetrAMM', kind='omitted')


class APS_EM(QuadEM):
    conf = Cpt(QuadEMPort, port_name='APS_EM', kind='omitted')
