#include <string.h>

#include "TDevice.h"

const QString TDevice::TICKER_SERIAL_NUMBER("TICKER");

const QString TDevice::TICKER_CHANNEL_ID("TICKER:00");

TDevice::TDevice(TProvider *provider, const QString &serialNumber, const QString &productName, const Version &version)
    : provider(provider), serialNumber(serialNumber), productName(productName), version(version), event(NONE), port(0),
      flags(0) {
    memset(user_calibration_enabled, 0, sizeof(user_calibration_enabled));
}

TDevice::TDevice(TProvider *provider, Source *source)
    : provider(provider), serialNumber(source->device->serial_number), productName(source->device->product_name),
      version(source->device->version), event(NONE), port(source->device->port), flags(source->device->flags),
      timestamp(source->timestamp) {

    List *channels_list = &source->device->channels;
    List *vchannels_list = &source->virtual_channels;
    int c = 0;

    // Physical channels

    for (; c < channels_list->size; c++) {

        Channel *ch = LIST_GET(channels_list, c, Channel);
        Channel *calch = LIST_GET(&source->calibrated_channels, c, Channel);
        TChannel *channel = new TChannel(this, c, ch->chip_id);
        channel->calpoints_count = ch->calpoints_count;
        channels.append(channel);
    }

    // Virtual channels

    LIST_FOR(vchannels_list) {

        VirtualChannel *vc = LIST_CUR(VirtualChannel);
        Channel *ch = &vc->channel;
        TChannel *channel = new TChannel(this, c, ch->chip_id);
        channels.append(channel);
        c++;
    }

    memcpy(user_calibration_enabled, source->user_calibration_enabled, sizeof(user_calibration_enabled));
}

TDevice::TDevice(const TDevice &device)
    : provider(device.provider), serialNumber(device.serialNumber), productName(device.productName),
      version(device.version), event(device.event), port(device.port), flags(device.flags), timestamp(device.timestamp),
      _isLocalTenkinet(device._isLocalTenkinet) {

    const int n = device.channels.size();
    channels.resize(n);

    for (int i = 0; i < n; i++) {
        TChannel *channel = new TChannel(this, *device.channels[i]);
        channels[i] = channel;
    }

    memcpy(user_calibration_enabled, device.user_calibration_enabled, sizeof(user_calibration_enabled));
}

TDevice::~TDevice() {

    for (int i = 0; i < channels.size(); i++) {
        delete channels[i];
    }
}

void TDevice::update(const TDevice *device) {

    if (channels.size() != device->channels.size()) {
        return;
    }

    port = device->port;
    flags = device->flags;
    timestamp = device->timestamp;

    for (int i = 0; i < channels.size(); i++) {

        TChannel *dst = channels[i];
        TChannel *src = device->channels[i];

        dst->quantity = src->quantity;
        dst->calibratedQuantity = src->calibratedQuantity;
        dst->calpoints_count = src->calpoints_count;
        memcpy(dst->calpoints, src->calpoints, sizeof(dst->calpoints));
    }

    memcpy(user_calibration_enabled, device->user_calibration_enabled, sizeof(user_calibration_enabled));
}

void TDevice::update(Source *source) {

    List *channels_list = &source->device->channels;
    List *vchannels_list = &source->virtual_channels;

    if (channels.size() != channels_list->size + vchannels_list->size) {
        return;
    }

    int c = 0;

    // Physical channels

    for (; c < channels_list->size; c++) {

        Channel *ch = LIST_GET(channels_list, c, Channel);
        Channel *calch = LIST_GET(&source->calibrated_channels, c, Channel);
        TChannel *channel = channels[c];

        channel->quantity = ch->quantity;
        channel->calibratedQuantity = calch->quantity;
        channel->calpoints_count = ch->calpoints_count;
        memcpy(channel->calpoints, calch->calpoints, sizeof(channel->calpoints));
    }

    // Virtual channels

    LIST_FOR(vchannels_list) {

        VirtualChannel *vc = LIST_CUR(VirtualChannel);
        Channel *ch = &vc->channel;
        TChannel *channel = channels[c];

        channel->quantity = ch->quantity;
        channel->calibratedQuantity = ch->quantity;

        c++;
    }

    port = source->device->port;
    flags = source->device->flags;
    timestamp = source->timestamp;

    memcpy(user_calibration_enabled, source->user_calibration_enabled, sizeof(user_calibration_enabled));
}

bool TDevice::isAlive() const { return flags & DEVICE_FLAG_ALIVE; }

bool TDevice::isLocal() const { return flags & DEVICE_FLAG_LOCAL; }

bool TDevice::isTenkinet() const { return flags & DEVICE_FLAG_TENKINET; }

bool TDevice::isMath() const { return flags & DEVICE_FLAG_MATH; }

bool TDevice::isTicker() const { return flags & DEVICE_FLAG_TICKER; }

bool TDevice::isSpecial() const { return flags & (DEVICE_FLAG_TICKER | DEVICE_FLAG_MATH); }

bool TDevice::isUserCalibrationSupported(int index) const {
    if (flags & DEVICE_FLAG_CALPOINTS) {
        if (flags & DEVICE_FLAG_TENKINET) {
            return 1;
        } else {
            return channels[index]->calpoints_count;
        }
    }
    return 0;
}

bool TDevice::isUserCalibrationEnabled(int index) const {

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

    return (user_calibration_enabled[i] & mask) != 0;
}

void TDevice::setAlive(bool alive) {

    if (alive) {
        flags |= DEVICE_FLAG_ALIVE;
    } else {
        flags &= ~DEVICE_FLAG_ALIVE;
    }
}

void TDevice::convertUnits() {

    for (int i = 0; i < channels.size(); i++) {
        channels[i]->convertUnits();
    }
}

void TDevice::invalidate() {

    for (int i = 0; i < channels.size(); i++) {
        channels[i]->invalidate();
    }
}
