#include "pch.h"
#include "XYParserApi.h"

#include "XYEegParser8.h"
#include "XYEegParser64.h"

#include <algorithm>
#include <new>
#include <string>
#include <vector>

namespace {

struct ParserContext {
    std::uint8_t channel_count = 0;
    XYEegParser8 parser8;
    XYEegParser64 parser64;
    std::string last_error;
};

void FillSummary(const XYEegFrame8& frame, XYParserFrameSummary& summary)
{
    summary.frame_index = frame.index;
    summary.channel_count = frame.channel_count;
    summary.battery = frame.battery;
    summary.sample_count = static_cast<std::uint8_t>(frame.samples.size());
    for (std::size_t sample_index = 0; sample_index < XYPARSER_SAMPLES_PER_FRAME; ++sample_index) {
        summary.trigger_types[sample_index] = frame.samples[sample_index].trigger_type;
        summary.trigger_indices[sample_index] = frame.samples[sample_index].trigger_index;
        for (std::size_t channel_index = 0; channel_index < XYPARSER_MAX_CHANNELS; ++channel_index) {
            summary.channel_values_uv[sample_index][channel_index] =
                channel_index < frame.channel_count
                    ? frame.samples[sample_index].channel_values_uv[channel_index]
                    : 0.0;
        }
    }
}

void FillSummary(const XYEegFrame64& frame, XYParserFrameSummary& summary)
{
    summary.frame_index = frame.index;
    summary.channel_count = frame.channel_count;
    summary.battery = frame.battery;
    summary.sample_count = static_cast<std::uint8_t>(frame.samples.size());
    for (std::size_t sample_index = 0; sample_index < XYPARSER_SAMPLES_PER_FRAME; ++sample_index) {
        summary.trigger_types[sample_index] = frame.samples[sample_index].trigger_type;
        summary.trigger_indices[sample_index] = frame.samples[sample_index].trigger_index;
        for (std::size_t channel_index = 0; channel_index < XYPARSER_MAX_CHANNELS; ++channel_index) {
            summary.channel_values_uv[sample_index][channel_index] =
                channel_index < frame.channel_count
                    ? frame.samples[sample_index].channel_values_uv[channel_index]
                    : 0.0;
        }
    }
}

} // namespace

extern "C" {

XYParserHandle XYParser_CreateParser(std::uint8_t channel_count)
{
    if (channel_count != 8 && channel_count != 64) {
        return nullptr;
    }

    ParserContext* context = new (std::nothrow) ParserContext();
    if (context == nullptr) {
        return nullptr;
    }

    context->channel_count = channel_count;
    return context;
}

void XYParser_DestroyParser(XYParserHandle handle)
{
    ParserContext* context = static_cast<ParserContext*>(handle);
    delete context;
}

void XYParser_SetAdcParams(XYParserHandle handle, double vref, double gain)
{
    ParserContext* context = static_cast<ParserContext*>(handle);
    if (context == nullptr) {
        return;
    }

    if (context->channel_count == 8) {
        context->parser8.SetAdcParams(vref, gain);
    } else {
        context->parser64.SetAdcParams(vref, gain);
    }
}

void XYParser_SetBypassChecksum(XYParserHandle handle, int bypass)
{
    ParserContext* context = static_cast<ParserContext*>(handle);
    if (context == nullptr) {
        return;
    }

    const bool enabled = bypass != 0;
    if (context->channel_count == 8) {
        context->parser8.SetBypassChecksum(enabled);
    } else {
        context->parser64.SetBypassChecksum(enabled);
    }
}

int XYParser_Feed(XYParserHandle handle,
                  const std::uint8_t* data,
                  std::size_t size,
                  XYParserFrameSummary* out_summaries,
                  int max_summaries)
{
    ParserContext* context = static_cast<ParserContext*>(handle);
    if (context == nullptr || data == nullptr || size == 0) {
        return 0;
    }

    if (max_summaries < 0) {
        max_summaries = 0;
    }

    if (context->channel_count == 8) {
        const std::vector<XYEegFrame8> frames = context->parser8.Feed(data, size);
        context->last_error = context->parser8.LastError();
        const int write_count = std::min<int>(static_cast<int>(frames.size()), max_summaries);
        for (int i = 0; i < write_count; ++i) {
            FillSummary(frames[static_cast<std::size_t>(i)], out_summaries[i]);
        }
        return write_count;
    }

    const std::vector<XYEegFrame64> frames = context->parser64.Feed(data, size);
    context->last_error = context->parser64.LastError();
    const int write_count = std::min<int>(static_cast<int>(frames.size()), max_summaries);
    for (int i = 0; i < write_count; ++i) {
        FillSummary(frames[static_cast<std::size_t>(i)], out_summaries[i]);
    }
    return write_count;
}

const char* XYParser_GetLastError(XYParserHandle handle)
{
    ParserContext* context = static_cast<ParserContext*>(handle);
    if (context == nullptr) {
        return "invalid parser handle";
    }
    return context->last_error.c_str();
}

}
