#include <stdlib.h>
#include <string.h>

#include "source.h"


Source *source_new(Device *device, VirtualOptions *opt) {

    Source *source = malloc(sizeof(Source));
    source_init(source, device, opt);

    return source;

}

void source_delete(Source *source) {

    source_clear(source);
    free(source);

}

void source_init(Source *source, Device *device, VirtualOptions *opt) {

    source->device = device;

    // Add calibrated channels
    list_init(&source->calibrated_channels);
    list_grow(&source->calibrated_channels, device->channels.size);
    LIST_FOR(&device->channels) {
        Channel *channel = LIST_CUR(Channel);
        Channel *calchannel = malloc(sizeof(Channel));
        *calchannel = *channel;
        list_add(&source->calibrated_channels, calchannel);
    }

    // Enable or disable user calibration by default
    int bits = device_flag_check(device, DEVICE_FLAG_CALPOINTS) ? 0xff : 0;
    memset(source->user_calibration_enabled, bits, sizeof(source->user_calibration_enabled));

    // Add virtual channels
    list_init(&source->virtual_channels);
    virtual_channels_add(&source->virtual_channels, &source->calibrated_channels, opt);

}

void source_clear(Source *source) {

    LIST_FOR(&source->virtual_channels) {
        VirtualChannel *vc = LIST_CUR(VirtualChannel);
        virtual_channel_free(vc);
    }

    list_clear(&source->virtual_channels);

    LIST_FOR(&source->calibrated_channels) {
        Channel *channel = LIST_CUR(Channel);
        free(channel);
    }

    list_clear(&source->calibrated_channels);

}

int source_is_user_calibration_supported(Source *source, uint8_t channel_index) {

    if (!device_flag_check(source->device, DEVICE_FLAG_CALPOINTS)) {
        return 0;
    }

    Channel * channel = list_get(&(source->device->channels), channel_index);
    return channel->calpoints_count;

}


int source_is_user_calibration_enabled(Source *source, uint8_t channel_index) {

    if (!source_is_user_calibration_supported(source, channel_index)) {
        return 0;
    }

    uint32_t i = channel_index / 32;
    uint32_t b = channel_index % 32;
    uint32_t mask = 1 << b;

    return (source->user_calibration_enabled[i] & mask) != 0;

}

void source_set_user_calibration(Source *source, uint8_t channel_index, char enabled) {

    if (!source_is_user_calibration_supported(source, channel_index)) {
        return;
    }

    uint32_t i = channel_index / 32;
    uint32_t b = channel_index % 32;
    uint32_t mask = 1 << b;

    if (enabled) {
        source->user_calibration_enabled[i] |= mask;
    }
    else {
        source->user_calibration_enabled[i] &= ~mask;
    }

}

void source_refresh(Source *source) {

    Device *device = source->device;
    List *channels = &device->channels;

    // Copy channels and apply calibration, if enabled

    for (int c = 0; c < channels->size; c++) {

        Channel *channel = LIST_GET(channels, c, Channel);
        Channel *calchannel = LIST_GET(&source->calibrated_channels, c, Channel);
        *calchannel = *channel;

        if (source_is_user_calibration_enabled(source, c)) {
            calpoints_apply(&calchannel->quantity, calchannel->calpoints);
        }

    }

    // Refresh virtual channels

    LIST_FOR(&source->virtual_channels) {
        VirtualChannel *vc = LIST_CUR(VirtualChannel);
        virtual_channel_refresh(vc);
    }

}
