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

/**
 * @file read_all_data.c
 * @author DEWETRON GmbH
 * @date 8 Oct 2014
 * @brief DMD Reader lib example usage.
 *
 * Here all the available data of a DEMO dmd file is read and the result is written on the console.
 */

#define _INITDMD
#include "dmd_reader_api_load.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#define CHECK(f) if (!(f)) { printf("Error @ line %d (%s)\n", __LINE__, #f); goto ERROR_HANDLER; }
#define CHECK_ERROR(f) if (dmdResult = (f), dmdResult < 0) { printf("Error %d @ line %d (%s)\n", dmdResult, __LINE__, #f); goto ERROR_HANDLER; }
#define CHECK_NULL(f) if (!f) { dmdResult = DMDReader_OutOfMemory; goto ERROR_HANDLER; }

/**
 * @brief Reads the sample values for a specified channel and writes the result on the console
 *
 * @param channel - valid channel handle
 * @param numSampleValues - sample cnt to read for the channel
 * @return error code
 */
DMDReader_ErrorCode ReadSampleValuesForChannel(DMDReader_ChannelHandle channel, DMDReader_SampleType sampleType, DMDReader_SampleIndex firstSample, DMDReader_SampleIndex lastSample, DMDReader_SampleDimension maxSampleDimension, const char* unit)
{
    DMDReader_SampleCount validSamplesCount = 0;
    DMDReader_P_SCALEDSAMPLE_TIMESTAMP sampleValuesTSScaled = NULL;
    DMDReader_P_REDUCEDSAMPLE_TIMESTAMP sampleValuesTSReduced = NULL;
    DMDReader_P_DIGITALSAMPLE_TIMESTAMP sampleValuesTSDigital = NULL;
    DMDReader_P_SAMPLEVALUE_SCALED sampleValuesScaled = NULL;
    DMDReader_P_SAMPLEVALUE_COMPLEX sampleValuesComplex = NULL;
    DMDReader_P_TIMESTAMP_SECONDS sampleTimestampsSeconds = NULL;
    DMDReader_P_SampleDimension sampleDimensions = NULL;
    DMDReader_ErrorCode dmdResult = DMDReader_ErrorEnd;
    unsigned int sample_idx = 0;
    DMDReader_SampleCount numSamples = lastSample - firstSample + 1;
    DMDReader_SampleCount maxSamplesToPrint = 15;
    DMDReader_SampleDimension maxDimensionsToPrint = 20;
    DMDReader_SampleIndex nextSample = 0;

    switch (sampleType)
    {
        case DMDReader_SAMPLETYPE_DOUBLE:
            {
                // READ SCALED CHANNEL DATA eg. ANALOG, COUNTER
                // real applications should not allocate space for all samples in a sweep but read step by step

                CHECK(maxSampleDimension == 1);

                //variant 1:
                sampleValuesTSScaled = (DMDReader_SCALEDSAMPLE_TIMESTAMP*) calloc((size_t)numSamples, sizeof(DMDReader_SCALEDSAMPLE_TIMESTAMP));
                CHECK_NULL(sampleValuesTSScaled);
                CHECK_ERROR(DMDReader_GetSamplesWithTS_ScaledValue_Seconds(channel, firstSample, numSamples, sampleValuesTSScaled, &validSamplesCount, &nextSample));

                //variant 2:
//                 sampleValuesScaled = (DMDReader_P_SAMPLEVALUE_SCALED)calloc((size_t)numSamples, sizeof(DMDReader_SAMPLEVALUE_SCALED));
//                 CHECK_NULL(sampleValuesScaled);
//                 sampleTimestampsSeconds = (DMDReader_P_TIMESTAMP_SECONDS)calloc((size_t)numSamples, sizeof(DMDReader_TIMESTAMP_SECONDS));
//                 CHECK_NULL(sampleTimestampsSeconds);
//                 CHECK_ERROR(DMDReader_GetSamplesAndTS_ScaledValue_Seconds(channel, firstSample, numSamples, sampleValuesScaled, sampleTimestampsSeconds, &validSamplesCount, &nextSample));

                for (sample_idx=0; sample_idx< ((maxSamplesToPrint<validSamplesCount)?maxSamplesToPrint:validSamplesCount); ++sample_idx)
                {
                    if (sampleValuesTSScaled)
                        printf("%4.2f [%s] @ %4.5f [s]\n", sampleValuesTSScaled[sample_idx].value, unit, sampleValuesTSScaled[sample_idx].timestamp);

                    if (sampleValuesScaled && sampleTimestampsSeconds)
                        printf("%4.2f [%s] @ %4.5f [s]\n", sampleValuesScaled[sample_idx], unit, sampleTimestampsSeconds[sample_idx]);
                }

                free(sampleValuesTSScaled);
                free(sampleValuesScaled);
                free(sampleTimestampsSeconds);

                break;
            }
        case DMDReader_SAMPLETYPE_REDUCED:
             {
                // READ SCALED REDUCED DATA

                CHECK(maxSampleDimension == 1);

                sampleValuesTSReduced = (DMDReader_REDUCEDSAMPLE_TIMESTAMP*) calloc((size_t)numSamples, sizeof(DMDReader_REDUCEDSAMPLE_TIMESTAMP));
                CHECK_NULL(sampleValuesTSReduced);

                CHECK_ERROR(DMDReader_GetSamplesWithTS_ReducedValue_Seconds(channel, firstSample, numSamples, sampleValuesTSReduced, &validSamplesCount, &nextSample));
                for (sample_idx=0; sample_idx< ((maxSamplesToPrint<validSamplesCount)?maxSamplesToPrint:validSamplesCount); ++sample_idx)
                {
                    printf("<min/max/avg/rms>=<%4.2f/%4.2f/%4.2f/%4.2f> @ %4.5f [s] \n", sampleValuesTSReduced[sample_idx].value.min, sampleValuesTSReduced[sample_idx].value.max,
                                                                                                           sampleValuesTSReduced[sample_idx].value.avg, sampleValuesTSReduced[sample_idx].value.rms,
                                                                                                           sampleValuesTSReduced[sample_idx].timestamp);
                }

                free(sampleValuesTSReduced);

                break;
            }
        case DMDReader_SAMPLETYPE_SINT32:
            {
                // READ DIGITAL DATA

                CHECK(maxSampleDimension == 1);

                sampleValuesTSDigital = (DMDReader_DIGITALSAMPLE_TIMESTAMP*) calloc((size_t)numSamples, sizeof(DMDReader_DIGITALSAMPLE_TIMESTAMP));
                CHECK_NULL(sampleValuesTSDigital);

                CHECK_ERROR(DMDReader_GetSamplesWithTS_DigitalValue_Seconds(channel, firstSample, numSamples, sampleValuesTSDigital, &validSamplesCount, &nextSample));
                for (sample_idx=0; sample_idx< ((maxSamplesToPrint<validSamplesCount)?maxSamplesToPrint:validSamplesCount); ++sample_idx)
                {
                    printf("%d @ %4.5f [s] \n",  sampleValuesTSDigital[sample_idx].value, sampleValuesTSDigital[sample_idx].timestamp);
                }

                free(sampleValuesTSDigital);

                break;
            }
        case DMDReader_SAMPLETYPE_DOUBLE_VECTOR:
            {
                CHECK(maxSampleDimension > 0);

                // READ SCALAR SAMPLE VECTOR DATA
                sampleValuesScaled = (DMDReader_P_SAMPLEVALUE_SCALED)calloc((size_t)numSamples, sizeof(DMDReader_SAMPLEVALUE_SCALED) * maxSampleDimension);
                CHECK_NULL(sampleValuesScaled);
                sampleTimestampsSeconds = (DMDReader_P_TIMESTAMP_SECONDS)calloc((size_t)numSamples, sizeof(DMDReader_TIMESTAMP_SECONDS));
                CHECK_NULL(sampleTimestampsSeconds);
                sampleDimensions = (DMDReader_P_SampleDimension)calloc((size_t)numSamples, sizeof(DMDReader_SampleDimension));
                CHECK_NULL(sampleDimensions);

                CHECK_ERROR(DMDReader_GetSamplesAndTS_ScalarVector_Seconds(
                    channel, firstSample, numSamples, maxSampleDimension, sampleValuesScaled,
                    sampleTimestampsSeconds, sampleDimensions, &validSamplesCount, &nextSample));

                for (sample_idx = 0; sample_idx< ((maxSamplesToPrint<validSamplesCount) ? maxSamplesToPrint : validSamplesCount); ++sample_idx)
                {
                    DMDReader_SampleDimension d;
                    DMDReader_SampleDimension max_d = maxDimensionsToPrint < sampleDimensions[sample_idx] ? maxDimensionsToPrint : sampleDimensions[sample_idx];
                    for (d = 0; d < max_d; d++)
                    {
                        printf("%4.2f ", sampleValuesScaled[sample_idx * maxSampleDimension + d]);
                    }
                    if (max_d < sampleDimensions[sample_idx])
                    {
                        printf("... ");
                    }
                    printf("@ %4.5f [s] \n", sampleTimestampsSeconds[sample_idx]);
                }

                free(sampleDimensions);
                free(sampleTimestampsSeconds);
                free(sampleValuesScaled);

                break;
            }
        case DMDReader_SAMPLETYPE_COMPLEX_VECTOR:
            {
                // READ COMPLEX VECTOR DATA
                sampleValuesComplex = (DMDReader_P_SAMPLEVALUE_COMPLEX)calloc((size_t)numSamples, sizeof(DMDReader_SAMPLEVALUE_COMPLEX) * maxSampleDimension);
                CHECK_NULL(sampleValuesComplex);
                sampleTimestampsSeconds = (DMDReader_P_TIMESTAMP_SECONDS)calloc((size_t)numSamples, sizeof(DMDReader_TIMESTAMP_SECONDS));
                CHECK_NULL(sampleTimestampsSeconds);
                sampleDimensions = (DMDReader_P_SampleDimension)calloc((size_t)numSamples, sizeof(DMDReader_SampleDimension));
                CHECK_NULL(sampleDimensions);

                CHECK_ERROR(DMDReader_GetSamplesAndTS_ComplexVector_Seconds(
                    channel, firstSample, numSamples, maxSampleDimension, sampleValuesComplex,
                    sampleTimestampsSeconds, sampleDimensions, &validSamplesCount, &nextSample));

                for (sample_idx = 0; sample_idx < ((maxSamplesToPrint < validSamplesCount) ? maxSamplesToPrint : validSamplesCount); ++sample_idx)
                {
                    DMDReader_SampleDimension d;
                    DMDReader_SampleDimension max_d = maxDimensionsToPrint < sampleDimensions[sample_idx] ? maxDimensionsToPrint : sampleDimensions[sample_idx];
                    for (d = 0; d < max_d; d++)
                    {
                        printf("%4.2f + %4.2fi ",
                            sampleValuesComplex[sample_idx * maxSampleDimension + d].real,
                            sampleValuesComplex[sample_idx * maxSampleDimension + d].imag);
                    }
                    if (max_d < sampleDimensions[sample_idx])
                    {
                        printf("... ");
                    }
                    printf("@ %4.5f [s] \n", sampleTimestampsSeconds[sample_idx]);
                }

                free(sampleDimensions);
                free(sampleTimestampsSeconds);
                free(sampleValuesComplex);

                break;
            }
        default:
            {
                printf("UNKNOWN TYPE");
            }
    }

    return DMDReader_NoError;

ERROR_HANDLER:
    free(sampleValuesTSScaled);
    free(sampleValuesTSReduced);
    free(sampleValuesTSDigital);
    free(sampleValuesScaled);
    free(sampleValuesComplex);
    free(sampleTimestampsSeconds);
    free(sampleDimensions);
    return dmdResult;
}

void printTimestamp(const char* title, const DMDReader_TIMESTAMP_UTC* t)
{
    if (t->valid)
    {
        int hour = ((int)t->timeOfDay) / (60 * 60);
        int min = (((int)t->timeOfDay) / 60) % 60;
        double sec = ((int)t->timeOfDay) % 60 + (t->timeOfDay - (int)t->timeOfDay);
        printf ("%s day %d of %d; %d:%d:%.9f (UTC+%1.1fh)\n", title, t->yearDay, t->year, hour, min, sec, t->timeOffset/60.0);
    }
    else
    {
        printf ("%s invalid\n", title);
    }
}

/**
 * Main
 */
int main(int argc, const char* argv[])
{
    const char* fileName = DMD_READER_DEMO_FILE;
    DMDReader_FileHandle file = 0;
    DMDReader_TIMESTAMP_UTC measurementStartTime = {0};
    DMDReader_TIMESTAMP_UTC measurementStartTime2 = {0};
    DMDReader_uint64 numHeaderFields = 0;
    DMDReader_uint64 numChannels = 0;
    DMDReader_uint64 numMarkers = 0;
    DMDReader_P_ChannelHandle channelList = NULL;
    DMDReader_ErrorCode dmdResult = DMDReader_ErrorEnd;
    DMDReader_ChannelHandle channel = 0;
    unsigned int i, j;
    DMDReader_uint64 num;
    DMDReader_SampleCount numSampleValues = 0;
    DMDReader_CHANNELINFORMATION channelInformation = {0};
    DMDReader_P_MARKEREVENT markerList = NULL;

    CHECK_ERROR( DMDReader_LoadLibrary(DMD_READER_DLL_NAME) );

    if (argc >= 2)
    {
        fileName = argv[1];
    }

    printf("Reading file '%s'\n", fileName);

    CHECK_ERROR( DMDReader_OpenFile(fileName, &file) );

    printf("\nFILE HEADER INFORMATION\n");

    CHECK_ERROR(DMDReader_GetNumHeaderFields(file, &numHeaderFields));
    for (i = 0; i < numHeaderFields; ++i)
    {
        DMDReader_HEADERFIELD field;
        CHECK_ERROR(DMDReader_GetHeaderFields(file, i, 1, &field, &num));
        if (num > 0)
        {
            printf("  %s: %s\n", field.name, field.value);
        }
    }

    CHECK_ERROR(DMDReader_GetMeasurementStartTime(file, &measurementStartTime, DMDReader_True));
    CHECK_ERROR(DMDReader_GetMeasurementStartTime(file, &measurementStartTime2, DMDReader_False));
    printTimestamp("Measurement start time (UTC): ", &measurementStartTime);
    printTimestamp("Measurement start time (local): ", &measurementStartTime2);

    // READ ALL THE CHANNEL DATA
    printf("\nCHANNEL DATA");
    CHECK_ERROR( DMDReader_GetNumChannels(file, DMDReader_CHANNELTYPE_ALL_CHANNELS, &numChannels) );

    channelList = (DMDReader_P_ChannelHandle) calloc((size_t)numChannels, sizeof(DMDReader_ChannelHandle));
    CHECK_NULL(channelList);

    CHECK_ERROR( DMDReader_GetChannels(file, DMDReader_CHANNELTYPE_ALL_CHANNELS, 0, numChannels, channelList, &numChannels) );

    for (i=0; i < numChannels; ++i)
    {
        DMDReader_uint64 numDataSweeps = 0;
        DMDReader_SampleType dataSampleType = DMDReader_SAMPLETYPE_INVALID;
        DMDReader_SampleType reducedSampleType = DMDReader_SAMPLETYPE_INVALID;
        DMDReader_SampleDimension maxSampleDimension = 0;
        // READ THE DATA FOR A SPECIFIC CHANNEL
        channel = channelList[i];

        CHECK_ERROR( DMDReader_GetChannelInformation(channel,
                                                     &channelInformation) );

        printf("Channel: %s , Sample rate [Hz]: %6.0lf, Unit: %s, Measurement Duration [s]: %3.2lf, Description: %s , Range Min/Max [%s] %3.0lf/%3.0lf \n",
                                                                                                                        channelInformation.name,
                                                                                                                        channelInformation.sampleRate,
                                                                                                                        channelInformation.unit,
                                                                                                                        channelInformation.measurementDuration,
                                                                                                                        channelInformation.description,
                                                                                                                        channelInformation.unit,
                                                                                                                        channelInformation.rangeMin,
                                                                                                                        channelInformation.rangeMax);

        CHECK_ERROR( DMDReader_GetVectorSampleType(channel, &dataSampleType, &reducedSampleType, &maxSampleDimension));

        if (dataSampleType!=DMDReader_SAMPLETYPE_INVALID)
        {
            CHECK_ERROR( DMDReader_GetNumDataSweeps(channel, &numDataSweeps) );
            for (j=0; j<numDataSweeps; ++j)
            {
                DMDReader_uint64 num = 0;
                DMDReader_SWEEP sweep;
                CHECK_ERROR( DMDReader_GetDataSweeps(channel, j, 1, &sweep, &num) );
                printf("  Data Sweep %u: sample range [%llu - %llu]; time %f - %f seconds\n", j+1, sweep.firstSample, sweep.lastSample, sweep.startTime, sweep.endTime);
                DMDReader_ErrorCode read_result = ReadSampleValuesForChannel(channel, dataSampleType, sweep.firstSample, sweep.lastSample, maxSampleDimension, channelInformation.unit);
                if (read_result == DMDReader_OutOfMemory)
                {
                    //Split request into smaller parts
                    DMDReader_SampleIndex sweep_part = (sweep.lastSample - sweep.firstSample) / 2;
                    CHECK_ERROR(ReadSampleValuesForChannel(channel, dataSampleType, sweep.firstSample, sweep.firstSample + sweep_part, maxSampleDimension, channelInformation.unit));
                    CHECK_ERROR(ReadSampleValuesForChannel(channel, dataSampleType, sweep.firstSample + sweep_part + 1, sweep.lastSample , maxSampleDimension, channelInformation.unit));
                }
            }
        }

        if (reducedSampleType!=DMDReader_SAMPLETYPE_INVALID)
        {
            CHECK_ERROR( DMDReader_GetNumReducedSweeps(channel, &numDataSweeps) );
            for (j=0; j<numDataSweeps; ++j)
            {
                DMDReader_uint64 num = 0;
                DMDReader_SWEEP sweep;
                CHECK_ERROR( DMDReader_GetReducedSweeps(channel, j, 1, &sweep, &num) );
                printf("  Reduced Data Sweep %u: sample range [%llu - %llu]; time %f - %f seconds\n", j+1, sweep.firstSample, sweep.lastSample, sweep.startTime, sweep.endTime);
                CHECK_ERROR( ReadSampleValuesForChannel(channel, reducedSampleType, sweep.firstSample, sweep.lastSample, maxSampleDimension, channelInformation.unit));
            }
        }

        printf("\n");

    }

    // READ ALL MARKERS FROM THE STORED FILE
    CHECK_ERROR( DMDReader_GetNumMarkers(file, &numMarkers) );

    markerList = (DMDReader_MARKEREVENT*) calloc((size_t)numMarkers, sizeof(DMDReader_MARKEREVENT));
    CHECK_NULL(markerList);

    CHECK_ERROR( DMDReader_GetMarkers(file, 0, numMarkers, markerList, &numMarkers) );

    printf("\nFILE MARKER\n");
    for (i=0; i<numMarkers; ++i)
    {
        printf("MARKER TYPE: %d, MARKER SOURCE: %d, MARKER TIME [s]: %3.1f, DESCRIPTION: %s \n",
                                                                                            markerList[i].type,
                                                                                            markerList[i].source,
                                                                                            markerList[i].time,
                                                                                            markerList[i].text);
    }

    free(channelList);
    channelList = NULL;

    free(markerList);
    markerList = NULL;

    CHECK_ERROR( DMDReader_CloseFile(&file) );
    CHECK_ERROR( DMDReader_CloseLibrary() );

    return 0;

ERROR_HANDLER:
    printf("ERROR HANDLER: Closing Library \n");

    free(channelList);
    free(markerList);

    DMDReader_CloseLibrary();

    return -1;
}

