/************************************************************************************************
 *  Copyright DEWETRON GmbH 2015                                                                *
 *                                                                                              *
 *   This file is a DMD reader library example                                                  *
 ************************************************************************************************/

/**
 * @file functions.c
 * @author DEWETRON GmbH
 * @date 2 Feb 2015
 * @brief DMD Reader lib example usage.
 */

#include "functions.h"
#include <inttypes.h>
#include <stdlib.h>
#include "dmd_reader_api_load.h"


const char* WriteDMDMarkersToCSV(DMDReader_FileHandle dmd_handle, FILE* csv_handle)
{
    DMDReader_uint64 status = 0, num_markers = 0;
    if (DMDReader_GetNumMarkers(dmd_handle, &num_markers) != DMDReader_NoError)
        return "error while counting markers";

    fprintf(csv_handle, "Time;Source;Type;Text\n");

    DMDReader_uint64 marker_idx;
    for (marker_idx = 0; marker_idx < num_markers; ++marker_idx)
    {
        DMDReader_MARKEREVENT marker;
        if ((DMDReader_GetMarkers(dmd_handle, marker_idx, 1, &marker, &status) != DMDReader_NoError) || (status != 1))
            return "error reading marker information";
        fprintf(csv_handle, "%f;%d;%d;%s\n", marker.time, marker.source, marker.type, marker.text);
    }
    return NULL;
}

DMDReader_uint64 GetSampleSize(DMDReader_SampleType t, DMDReader_SampleDimension d)
{
    switch (t)
    {
        case DMDReader_SAMPLETYPE_DOUBLE: return 8;
        case DMDReader_SAMPLETYPE_REDUCED: return 32;
        case DMDReader_SAMPLETYPE_SINT32: return 4;
        case DMDReader_SAMPLETYPE_DOUBLE_VECTOR: return 8 * d;
        case DMDReader_SAMPLETYPE_COMPLEX_VECTOR: return 16 * d;
        default: return 0;
    }
}

void WriteSampleToCSV(FILE* csv_handle, DMDReader_TIMESTAMP_SECONDS ts, void *data, DMDReader_SampleType t, DMDReader_SampleDimension d)
{
    switch (t)
    {
        case DMDReader_SAMPLETYPE_DOUBLE:
            fprintf(csv_handle, "%f;%f\n", ts, *(double*)data);
            break;
        case DMDReader_SAMPLETYPE_DOUBLE_VECTOR:
        {
            DMDReader_SampleDimension i;
            double* samples = (double*)data;
            fprintf(csv_handle, "%f", ts);
            for (i = 0; i < d; ++i)
            {
                fprintf(csv_handle, ";%f", samples[i]);
            }
            fprintf(csv_handle, "\n");
            break;
        }
        case DMDReader_SAMPLETYPE_COMPLEX_VECTOR:
        {
            DMDReader_SampleDimension i;
            DMDReader_P_SAMPLEVALUE_COMPLEX samples = (DMDReader_P_SAMPLEVALUE_COMPLEX)data;
            fprintf(csv_handle, "%f", ts);
            for (i = 0; i < d; ++i)
            {
                fprintf(csv_handle, ";%f+%fi", samples[i].real, samples[i].imag);
            }
            fprintf(csv_handle, "\n");
            break;
        }
    }
}

const char* WriteDMDChannelToCSV(DMDReader_FileHandle dmd_handle, int channel_index, FILE* csv_handle)
{
#define BLOCK_SIZE 10000
    DMDReader_ChannelHandle ch_handle;
    DMDReader_uint64 status, num_sweeps;
    DMDReader_CHANNELINFORMATION ch_info;
    DMDReader_SampleType sample_type = DMDReader_SAMPLETYPE_INVALID;
    DMDReader_SampleType reduced_sample_type = DMDReader_SAMPLETYPE_INVALID;
    DMDReader_SampleDimension max_sample_dimension = 0;
    DMDReader_uint64 sweep_idx;
    DMDReader_uint64 sample_size = 0;
    char* values = NULL;

    if ((DMDReader_GetChannels(dmd_handle, DMDReader_CHANNELTYPE_ALL_CHANNELS, channel_index, 1, &ch_handle, &status) != DMDReader_NoError) || (status != 1))
        return "invalid channel index";
    if (DMDReader_GetChannelInformation(ch_handle, &ch_info) != DMDReader_NoError)
        return "error while reading channel information";

    if (DMDReader_GetVectorSampleType(ch_handle, &sample_type, &reduced_sample_type, &max_sample_dimension) != DMDReader_NoError)
        return "error while reading sample format";

    sample_size = GetSampleSize(sample_type, max_sample_dimension);
    if (sample_size == 0)
        return "unsupported sample type";

    /* TODO: convert UTF-8 to ASCII for CSV */
    fprintf(csv_handle, "Time;%s\n", ch_info.name);
    fprintf(csv_handle, "s;%s\n", ch_info.unit);

    if (DMDReader_GetNumDataSweeps(ch_handle, &num_sweeps) != DMDReader_NoError)
        return "error while reading sweep information";

    values = (char*)malloc((size_t)(sample_size * BLOCK_SIZE));
    if (!values)
        return "failed to allocate memory";

    for (sweep_idx = 0; sweep_idx < num_sweeps; ++sweep_idx)
    {
        DMDReader_SWEEP sweep_info;
        if ((DMDReader_GetDataSweeps(ch_handle, sweep_idx, 1, &sweep_info, &status) != DMDReader_NoError) || (status != 1))
        {
            free(values);
            return "error while reading sweep information";
        }
        printf("Converting sweep %llu (%fs)", sweep_idx, sweep_info.endTime - sweep_info.startTime);

        if (sweep_info.sampleFrequency > 0.0)
        {
            DMDReader_SampleIndex sample_idx = sweep_info.firstSample;
            DMDReader_SampleIndex valid_samples, next_sample = sample_idx;
            DMDReader_SampleIndex sample_count = 0;
            DMDReader_SampleDimension sample_dimensions[BLOCK_SIZE];

            while (next_sample <= sweep_info.lastSample)
            {
                DMDReader_ErrorCode ec;
                DMDReader_uint64 i;

                switch (sample_type)
                {
                    case DMDReader_SAMPLETYPE_DOUBLE:
                        ec = DMDReader_GetSamplesAndTS_ScaledValue_Seconds(ch_handle, sample_idx, BLOCK_SIZE, (DMDReader_P_SAMPLEVALUE_SCALED)values, NULL, &valid_samples, &next_sample);
                        break;
                    case DMDReader_SAMPLETYPE_DOUBLE_VECTOR:
                        ec = DMDReader_GetSamplesAndTS_ScalarVector_Seconds(ch_handle, sample_idx, BLOCK_SIZE, max_sample_dimension, (DMDReader_P_SAMPLEVALUE_SCALED)values, NULL, sample_dimensions, &valid_samples, &next_sample);
                        break;
                    case DMDReader_SAMPLETYPE_COMPLEX_VECTOR:
                        ec = DMDReader_GetSamplesAndTS_ComplexVector_Seconds(ch_handle, sample_idx, BLOCK_SIZE, max_sample_dimension, (DMDReader_P_SAMPLEVALUE_COMPLEX)values, NULL, sample_dimensions, &valid_samples, &next_sample);
                        break;
                }
                if (ec != DMDReader_NoError)
                {
                    printf("\n");
                    free(values);
                    return "error while reading samples";
                }
                for (i = 0; i < valid_samples; ++i, ++sample_count)
                {
                    DMDReader_TIMESTAMP_SECONDS ts = sweep_info.startTime + (sample_count) / sweep_info.sampleFrequency;
                    WriteSampleToCSV(csv_handle, ts, values + GetSampleSize(sample_type, max_sample_dimension) * i, sample_type, sample_dimensions[i]);
                }
                sample_idx = next_sample;
                printf(".");
            }
        }
        else
        {
            DMDReader_TIMESTAMP_SECONDS times[BLOCK_SIZE];
            DMDReader_SampleIndex sample_idx = sweep_info.firstSample;
            DMDReader_SampleIndex valid_samples, next_sample = sample_idx;
            DMDReader_SampleDimension sample_dimensions[BLOCK_SIZE];

            while (next_sample <= sweep_info.lastSample)
            {
                DMDReader_ErrorCode ec;
                DMDReader_uint64 i;

                switch (sample_type)
                {
                    case DMDReader_SAMPLETYPE_DOUBLE:
                        ec = DMDReader_GetSamplesAndTS_ScaledValue_Seconds(ch_handle, sample_idx, BLOCK_SIZE, (DMDReader_P_SAMPLEVALUE_SCALED)values, times, &valid_samples, &next_sample);
                        break;
                    case DMDReader_SAMPLETYPE_DOUBLE_VECTOR:
                        ec = DMDReader_GetSamplesAndTS_ScalarVector_Seconds(ch_handle, sample_idx, BLOCK_SIZE, max_sample_dimension, (DMDReader_P_SAMPLEVALUE_SCALED)values, times, sample_dimensions, &valid_samples, &next_sample);
                        break;
                    case DMDReader_SAMPLETYPE_COMPLEX_VECTOR:
                        ec = DMDReader_GetSamplesAndTS_ComplexVector_Seconds(ch_handle, sample_idx, BLOCK_SIZE, max_sample_dimension, (DMDReader_P_SAMPLEVALUE_COMPLEX)values, times, sample_dimensions, &valid_samples, &next_sample);
                        break;
                }
                if (ec != DMDReader_NoError)
                {
                    printf("\n");
                    free(values);
                    return "error while reading samples";
                }
                for (i = 0; i < valid_samples; ++i)
                {
                    WriteSampleToCSV(csv_handle, times[i], values + GetSampleSize(sample_type, max_sample_dimension) * i, sample_type, sample_dimensions[i]);
                }
                sample_idx = next_sample;
                printf(".");
            }
        }

        printf("\n");
    }
    free(values);
    return NULL;
#undef BLOCK_SIZE
}

const char* WriteDMDChannelToCSV_Reduced(DMDReader_FileHandle dmd_handle, int channel_index, FILE* csv_handle)
{
    DMDReader_ChannelHandle ch_handle;
    DMDReader_uint64 status, num_sweeps;
    DMDReader_CHANNELINFORMATION ch_info;

    if ((DMDReader_GetChannels(dmd_handle, DMDReader_CHANNELTYPE_ALL_CHANNELS, channel_index, 1, &ch_handle, &status) != DMDReader_NoError) || (status != 1))
        return "invalid channel index";
    if (DMDReader_GetChannelInformation(ch_handle, &ch_info) != DMDReader_NoError)
        return "error while reading channel information";

    fprintf(csv_handle, "Time;%s (AVG);%s (RMS);%s (MIN);%s (MAX)\n", ch_info.name, ch_info.name, ch_info.name, ch_info.name);
    fprintf(csv_handle, "s;%s;%s;%s;%s\n", ch_info.unit, ch_info.unit, ch_info.unit, ch_info.unit);

    if (DMDReader_GetNumReducedSweeps(ch_handle, &num_sweeps) != DMDReader_NoError)
        return "error while reading sweep information";

    DMDReader_uint64 sweep_idx;
    for (sweep_idx = 0; sweep_idx < num_sweeps; ++sweep_idx)
    {
        DMDReader_SWEEP sweep_info;
        if ((DMDReader_GetReducedSweeps(ch_handle, sweep_idx, 1, &sweep_info, &status) != DMDReader_NoError) || (status != 1))
            return "error while reading sweep information";
        printf("Converting sweep %llu (%fs)", sweep_idx, sweep_info.endTime - sweep_info.startTime);
        {
            DMDReader_SAMPLEVALUE_REDUCED values[1000];
            DMDReader_TIMESTAMP_SECONDS times[1000];
            DMDReader_SampleIndex sample_idx = sweep_info.firstSample;
            DMDReader_SampleIndex valid_samples, next_sample = sample_idx;

            while (next_sample < sweep_info.lastSample)
            {
                if (DMDReader_GetSamplesAndTS_ReducedValue_Seconds(ch_handle, sample_idx, 1000, values, times, &valid_samples, &next_sample) != DMDReader_NoError)
                {
                    printf("\n");
                    return "error while reading samples";
                }
                DMDReader_uint64 i;
                for (i = 0; i < valid_samples; ++i)
                {
                    fprintf(csv_handle, "%f;%f;%f;%f;%f\n", times[i], values[i].avg, values[i].rms, values[i].min, values[i].max);
                }
                sample_idx = next_sample;
                printf(".");
            }
        }
        printf("\n");
    }
    return NULL;
}

const char* WriteDMDChannelsToCSV(DMDReader_FileHandle dmd_handle, FILE* csv_handle)
{
    DMDReader_ChannelHandle ch_handle;
    DMDReader_uint64 status, num_channels, ch_idx;
    DMDReader_CHANNELINFORMATION ch_info;

    DMDReader_GetNumChannels(dmd_handle, DMDReader_CHANNELTYPE_ALL_CHANNELS, &num_channels);

    fprintf(csv_handle, "Index;Name;SampleRate;Range;Unit\n");

    for (ch_idx = 0; ch_idx < num_channels; ++ch_idx)
    {
        if ((DMDReader_GetChannels(dmd_handle, DMDReader_CHANNELTYPE_ALL_CHANNELS, ch_idx, 1, &ch_handle, &status) != DMDReader_NoError) || (status != 1))
            return "invalid channel index";
        if (DMDReader_GetChannelInformation(ch_handle, &ch_info) != DMDReader_NoError)
            return "error while reading channel information";

        printf("%3llu: %s\n", ch_idx, ch_info.name);
        fprintf(csv_handle, "%llu; %s;%f;[%f,%f];%s\n", ch_idx, ch_info.name, ch_info.sampleRate, ch_info.rangeMin, ch_info.rangeMax, ch_info.unit);
    }

    return NULL;
}
