#include "test_data_channel.h"

#include "../device_test.h"
#include "../../software/signal_processing/system/data_channel.h"

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

int assert_data_channel_binding_eq( uint32_t base, uint32_t expected ) {
    uint32_t binding = data_channel_get_binding( base );
    if ( binding != expected ) {
        printf( TEST_FAIL " assert_data_channel_binding_eq %" PRIu32 " != %" PRIu32 "\n", expected, binding );
    }

    return binding;
}

int assert_data_channel_empty( uint32_t base ) {
    uint32_t empty = data_channel_is_empty( base );
    if ( ! empty ) {
        printf( TEST_FAIL " assert_data_channel_empty\n" );
    }

    return empty;
}

int assert_not_empty( uint32_t base ) {
    uint32_t empty = data_channel_is_empty( base );
    if ( empty ) {
        printf( TEST_FAIL " assert_not_empty\n" );
    }

    return ! empty;
}

int assert_data_channel_full( uint32_t base ) {
    uint32_t full = data_channel_is_full( base );
    if ( ! full ) {
        printf( TEST_FAIL " assert_data_channel_full\n" );
    }

    return full;
}

int assert_not_full( uint32_t base ) {
    uint32_t full = data_channel_is_full( base );
    if ( full ) {
        printf( TEST_FAIL " assert_not_full\n" );
    }

    return ! full;
}

int assert_data_channel_level_eq( uint32_t base, uint32_t expected ) {
    uint32_t level = data_channel_level( base );
    if ( level != expected ) {
        printf( TEST_FAIL " assert_data_channel_level_eq %" PRIu32 " != %" PRIu32 "\n", expected, level );
    }
    return level == expected;
}

int assert_data_channel_read_eq( uint32_t base, uint32_t expected ) {
    uint32_t readdata;
    int ret = data_channel_read( base, & readdata );

    if ( ret ) {
        printf( TEST_FAIL " %s data_channel empty\n", __func__ );
        return 0;
    }

    if ( readdata != expected ) {
        printf( TEST_FAIL " %s %" PRIu32 " != %" PRIu32 "\n", __func__, expected, readdata );
        return 0;
    }

    return 1;
}

int test_data_channel_reset_values( uint32_t channel ) {
    printf( "    %s %" PRIu32 " ...", __func__, channel );
    uint32_t base = data_channel_base_from_number( channel );
    data_channel_clear( base );

    if ( ! assert_data_channel_empty( base ) ) {
        return 1;
    }

    if ( ! assert_not_full( base ) ) {
        return 1;
    }

    if ( ! assert_data_channel_level_eq( base, 0 ) ) {
        return 1;
    }

    printf( TEST_OK "\n" );

    return 0;
}

int test_data_channel_sw_single_write_read( uint32_t channel ) {
    printf( "    %s %" PRIu32 " ...", __func__, channel );

    uint32_t base = data_channel_base_from_number( channel );
    DataChannelBinding binding = { BINDING_SW, BINDING_SW };
    data_channel_bind( base, & binding );

    uint32_t value = 0xa5a5f00f;
    data_channel_write( base, value );

    if ( ! assert_data_channel_level_eq( base, 1 ) ) {
        return 1;
    }

    if ( ! assert_data_channel_read_eq( base, value ) ) {
        return 1;
    }

    printf( TEST_OK "\n" );
    return 0;
}

int test_data_channel_sw_fill_read( uint32_t channel ) {
    printf( "    %s ...", __func__ );
    uint32_t base = data_channel_base_from_number( channel );
    DataChannelBinding binding = { BINDING_SW, BINDING_SW };
    data_channel_bind( base, & binding );
    data_channel_clear( base );

    if ( ! assert_data_channel_empty( base ) ) {
        return 1;
    }

    printf( " write ..." );
    for ( uint32_t i = 0; i < DATA_CHANNEL_DEPTH; ++i ) {
        if ( ! assert_data_channel_level_eq( base, i ) ) {
                return 1;
        }
        data_channel_write( base, i );
    }

    if ( ! assert_data_channel_full( base ) ) {
        return 1;
    }

    if ( ! assert_data_channel_level_eq( base, 0 ) ) {
            return 1;
    }

    printf( " read ..." );
    for ( uint32_t i = 0; i < DATA_CHANNEL_DEPTH; ++i ) {
        assert_data_channel_read_eq( base, i );
        if ( ! assert_data_channel_level_eq( base, DATA_CHANNEL_DEPTH - i - 1 ) ) {
                return 1;
        }
    }

    if ( ! assert_data_channel_empty( base ) ) {
        return 1;
    }

    if ( ! assert_data_channel_level_eq( base, 0 ) ) {
            return 1;
    }

    printf( TEST_OK "\n" );

    return 0;
}

int test_data_channel_sw_fill_clear( uint32_t channel ) {
    printf( "    %s ...", __func__ );
    uint32_t base = data_channel_base_from_number( channel );
    DataChannelBinding binding = { BINDING_SW, BINDING_SW };
    data_channel_bind( base, & binding );

    if ( ! assert_data_channel_empty( base ) ) {
        return 1;
    }

    for ( uint32_t i = 0; i < DATA_CHANNEL_DEPTH; ++i ) {
        if ( ! assert_data_channel_level_eq( base, i ) ) {
                return 1;
        }
        data_channel_write( base, i );
    }

    if ( ! assert_data_channel_full( base ) ) {
        return 1;
    }

    if ( ! assert_data_channel_level_eq( base, 0 ) ) {
            return 1;
    }

    data_channel_clear( base );

    if ( ! assert_data_channel_empty( base ) ) {
        return 1;
    }

    if ( ! assert_data_channel_level_eq( base, 0 ) ) {
            return 1;
    }

    printf( TEST_OK "\n" );

    return 0;
}

int test_data_channel( uint32_t channel ) {
    int ret = 0;
    ret |= test_data_channel_reset_values( channel );
    ret |= test_data_channel_sw_single_write_read( channel );
    ret |= test_data_channel_sw_fill_read( channel );
    ret |= test_data_channel_sw_fill_clear( channel );

    return ret;
}

int test_data_channels( void ) {
    printf( "%s\n", __func__ );
    int ret = 0;
    for ( uint32_t i = 0; i < DATA_CHANNEL_COUNT; ++i ) {
        ret |= test_data_channel( i );
    }
    printf( TEST_DONE );
    return ret;
}


