import numpy as np
import nidaqmx as ni
import nidaqmx.stream_readers
import nidaqmx.constants as niconstants


def list_devices():
    """
    Prints the detected devices to stdout

    :return: None
    """
    # Lets's list devices
    system = nidaqmx.system.System.local()
    print(system.driver_version)

    print("Devices:")
    for device in system.devices:
        print("\t", device)

def collect_sample():

    return collect_ai_sample()

def collect_ci_sample(samplerate: float, sampleduration: float, 
                    channels=[dict(chan='cDAQ1Mod3/ctr0',type='ang_encoder', name='encoder')]
                      ):
    """
    main function for collecting a segment of digital input data

    """

    # Setup acquisition
    nsamples = int(sampleduration * samplerate)

    import time
    t_start = time.process_time()

    # print("Samples to Acquire: ", nsamples)
    with ni.Task() as task:
        # Define the number of channels and an expected time vector
        nchan = 1
        t = np.linspace(0, 1 / samplerate * nsamples, nsamples)

        # Pre-allocate the numpy arrays for speed.
        ddata = np.ndarray([nsamples])

        # Setup the task
        for channel in channels:
            if channel['type'] is 'ang_encoder':
                task.ci_channels.add_ci_ang_encoder_chan(channel['chan'],
                                                         name_to_assign_to_channel=channel['name'],
                                                         decoding_type=ni.constants.EncoderType.X_1)
                                                         

        task.timing.cfg_samp_clk_timing(rate=samplerate, samps_per_chan=nsamples,
                                        sample_mode=niconstants.AcquisitionType.FINITE)
        
        stream = ni.stream_readers.CounterReader(task.in_stream)
        print(task.in_stream.input_buf_size)
        task.in_stream.input_buf_size = 3 * nsamples

        task.start()

        # Read the DAQ
        task.wait_until_done()
        samps = stream.read_many_sample_double(data=ddata,
                                    number_of_samples_per_channel=niconstants.READ_ALL_AVAILABLE,
                                    timeout=10 * sampleduration)
        task.stop()

        return ddata, t, samps



def collect_ai_sample(samplerate: float, sampleduration: float,
                   channels=(dict(chan='cDAQ1Mod2/ai0', type='voltage', name='Voltage0'),
                                dict(chan='cDAQ1Mod2/ai1', type='voltage', name='Voltage1'),
                                dict(chan='cDAQ1Mod1/ai0', type='current', name='Current0'))
                      ):
    """
    main function for collecting a segment of data

    :param samplerate: sampling rate in hertz
    :param sampleduration: sampling duration in seconds
    :param channels: dict with kws' chan, type, and name
    :return: tuple with data ndarray and elapse time ndarray use list_devices() to get channel names
    #dict(chan='cDAQ1Mod3/ai0', type='voltage', name='Vibration'))
    """
    # Setup acquisition
    nsamples = int(sampleduration * samplerate)

    import time
    t_start = time.process_time()

    # print("Samples to Acquire: ", nsamples)
    with ni.Task() as task:
        # Define the number of channels and an expected time vector
        nchan = 3
        t = np.linspace(0, 1 / samplerate * nsamples, nsamples)

        # Pre-allocate the numpy arrays for speed.
        adata = np.ndarray([nchan, nsamples])

        # Setup the task
        for channel in channels:
            if channel['type'] is 'voltage':
                task.ai_channels.add_ai_voltage_chan(channel['chan'], name_to_assign_to_channel=channel['name'])
            elif channel['type'] is 'current':
                task.ai_channels.add_ai_current_chan(channel['chan'], name_to_assign_to_channel=channel['name'])
           
        task.timing.cfg_samp_clk_timing(rate=samplerate, samps_per_chan=nsamples,
                                        sample_mode=niconstants.AcquisitionType.FINITE)
        stream = ni.stream_readers.AnalogMultiChannelReader(task.in_stream)
        print(task.in_stream.input_buf_size)
        task.in_stream.input_buf_size = 3 * nsamples

        task.start()

        # Read the DAQ
        task.wait_until_done()
        samps = stream.read_many_sample(data=adata,
                                    number_of_samples_per_channel=niconstants.READ_ALL_AVAILABLE,
                                    timeout=10 * sampleduration)
        task.stop()

        return adata, t, samps


def change_do():
    """
    TODO Change a digital output

    :return:
    """
    pass

if __name__ == "__main__":
    list_devices()
    collect_ai_sample(1600.0, .1)
    (ddata, t, samps) = collect_ci_sample(1600.0, 0.1)
    
