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

#include "device.h"
#include "chip.h"


Device *device_new() {

    Device *device = calloc(1, sizeof(Device));
    // no need to call device_init() since calloc() sets the memory to zero

    return device;

}

void device_delete(Device *device) {

    device_clear(device);
    free(device);

}

void device_init(Device *device) {

    memset(device, 0, sizeof(Device));

}

void device_clear(Device *device) {

    LIST_FOR(&device->channels) {
        Channel *channel = LIST_CUR(Channel);
        free(channel);
    }

    list_clear(&device->channels);
    device_init(device);

}

Device *device_clone(const Device *src) {

    Device* dst = malloc(sizeof(Device));
    *dst = *src;

    list_init(&dst->channels);
    device_add_channels(dst, src->channels.size);
    device_copy_channels(dst, src);

    return dst;

}

void device_add_channels(Device *device, uint8_t n) {

    List *channels = &device->channels;
    list_grow(channels, channels->size + n);

    for (uint8_t i = 0; i < n; i++) {

        Channel *channel = malloc(sizeof(Channel));
        list_add(channels, channel);

        channel->chip_id = USBTENKI_CHIP_NONE;
        channel->quantity = QUANTITY_NO_DATA;

        // no calpoints by default; set to all NAN
        memset(channel->calpoints, 0xff, sizeof(channel->calpoints));

    }

}

void device_trim_channels(Device *device) {

    // Unused channels usually occur at the end of the channel list,
    // so let's iterate in reverse.

    LIST_FOR_REVERSE(&device->channels) {

        Channel *channel = LIST_CUR(Channel);

        if (channel->chip_id == USBTENKI_CHIP_NONE) {
            free(channel);
            LIST_CUR_REMOVE_REVERSE(&device->channels);
        }

    }

}

void device_copy_channels(Device *dst, const Device *src) {

    size_t dst_n = dst->channels.size;
    size_t src_n = src->channels.size;
    size_t n = (dst_n < src_n) ? dst_n : src_n;

    for (size_t i = 0; i < n; i++) {
        Channel *dst_channel = LIST_GET(&dst->channels, i, Channel);
        Channel *src_channel = LIST_GET(&src->channels, i, Channel);
        *dst_channel = *src_channel;
    }

}

void device_invalidate(Device *device) {

    LIST_FOR(&device->channels) {
        Channel *channel = LIST_CUR(Channel);
        channel->quantity = QUANTITY_NO_DATA;
    }

}

Device *device_find(List *list, const char *serial_number, char remove) {

    LIST_FOR(list) {
        Device *device = LIST_CUR(Device);
        if (DEVICE_SERIAL_NUMBERS_EQUAL(device->serial_number, serial_number)) {
            if (remove) {
                LIST_CUR_REMOVE(list);
            }
            return device;
        }
    }

    return NULL;

}
