#include "data_channel.h"

#include <system.h>
#include <stdio.h>
#include <io.h>

const uint32_t DATA_CHANNEL_DEPTH = 1024;
const uint32_t DATA_CHANNEL_COUNT = 7;

const uint32_t DATA_CHANNEL_BASE_LIST[] = {
    DATA_CHANNEL_0_BASE,
    DATA_CHANNEL_1_BASE,
    DATA_CHANNEL_2_BASE,
    DATA_CHANNEL_3_BASE,
    DATA_CHANNEL_4_BASE,
    DATA_CHANNEL_5_BASE,
    DATA_CHANNEL_6_BASE
};

#define REG_BINDING_OFFSET 0
#define BINDING_SINK_POS 0
#define BINDING_SOURCE_POS 1

#define REG_EMPTY_OFFSET 1
#define REG_FULL_OFFSET 2
#define REG_LEVEL_OFFSET 3
#define REG_SINK_OFFSET 4
#define REG_SOURCE_OFFSET 5
#define REG_CLEAR_OFFSET 6

void data_channel_bind( uint32_t base, const DataChannelBinding * binding ) {

    uint32_t value = binding->sink << BINDING_SINK_POS |
        binding->source << BINDING_SOURCE_POS;

    IOWR( base, REG_BINDING_OFFSET, value );
}

uint32_t data_channel_get_binding( uint32_t base ) {
    return IORD( base, REG_BINDING_OFFSET );
}

uint32_t data_channel_is_empty( uint32_t base ) {
    return IORD( base, REG_EMPTY_OFFSET );
}

uint32_t data_channel_is_full( uint32_t base ) {
    return IORD( base, REG_FULL_OFFSET );
}

uint32_t data_channel_level( uint32_t base ) {
    return IORD( base, REG_LEVEL_OFFSET );
}

void data_channel_clear( uint32_t base ) {
    IOWR( base, REG_CLEAR_OFFSET, 1 );
}

int data_channel_write( uint32_t base, uint32_t value ) {
    if ( data_channel_is_full( base ) ) {
        return 1;
    }
    IOWR( base, REG_SINK_OFFSET, value );
    return 0;
}

int data_channel_write_all( uint32_t base, const uint32_t * data, uint32_t len ) {
    for ( uint32_t i = 0; i < len; ++i ) {
        int ret = data_channel_write( base, data[ i ] );
        if ( ret ) return ret;
    }

    return 0;
}

int data_channel_read( uint32_t base, uint32_t * value ) {
    if ( data_channel_is_empty( base ) ) {
        return 1;
    }
    * value = IORD( base, REG_SOURCE_OFFSET );
    return 0;
}

int data_channel_read_all( uint32_t base, void * buffer ) {
    int i;
    for ( i = 0; i < DATA_CHANNEL_DEPTH; ++i ) {
        int ret = data_channel_read( base, buffer + i );
        if ( ret ) {
            break;
        }
    }

    return i;
}

uint32_t data_channel_base_from_number( uint32_t channel ) {
    return DATA_CHANNEL_BASE_LIST[ channel ];
}

