/**
 * @file LTTB.h
 * @brief Largest Triangle Three Buckets (LTTB) time-series decimation.
 *
 * Direct C++ translation of the Go lttbDecimate function in wshub/hub.go.
 * Preserves visual fidelity when reducing a large time-series to a smaller
 * number of representative points.
 *
 * No external dependencies (uses only <math.h> for fabs).
 */

#ifndef STREAMHUB_LTTB_H_
#define STREAMHUB_LTTB_H_

#include "CompilerTypes.h"
#include <math.h>

namespace StreamHub {

using MARTe::uint32;
using MARTe::float64;

/**
 * @brief Decimate time-series (tIn, vIn) of length nIn to at most threshold points.
 *
 * @param tIn      Input time array (float64, monotonically increasing).
 * @param vIn      Input value array (float64), same length as tIn.
 * @param nIn      Number of input points.
 * @param tOut     Output time array (pre-allocated, capacity >= threshold).
 * @param vOut     Output value array (pre-allocated, capacity >= threshold).
 * @param threshold  Target number of output points (must be >= 3 for LTTB to apply;
 *                   otherwise all points are copied).
 * @return Number of output points written (always <= threshold and <= nIn).
 */
inline uint32 LTTBDecimate(const float64 *tIn, const float64 *vIn, uint32 nIn,
                            float64 *tOut, float64 *vOut, uint32 threshold)
{
    if (nIn == 0u) {
        return 0u;
    }
    if (nIn <= threshold || threshold < 3u) {
        /* Fast path: no decimation needed */
        for (uint32 i = 0u; i < nIn; i++) {
            tOut[i] = tIn[i];
            vOut[i] = vIn[i];
        }
        return nIn;
    }

    uint32 nOut = 0u;

    /* Always include the first point */
    tOut[nOut] = tIn[0u];
    vOut[nOut] = vIn[0u];
    nOut++;

    float64 bucketSize = static_cast<float64>(nIn - 2u) /
                         static_cast<float64>(threshold - 2u);

    uint32 a = 0u; /* index of last selected point */

    for (uint32 i = 0u; i < threshold - 2u; i++) {

        /* Compute average point in the NEXT bucket (for area calculation) */
        uint32 avgStart = static_cast<uint32>((static_cast<float64>(i + 1u) * bucketSize)) + 1u;
        uint32 avgEnd   = static_cast<uint32>((static_cast<float64>(i + 2u) * bucketSize)) + 1u;
        if (avgEnd > nIn) { avgEnd = nIn; }

        float64 avgT = 0.0;
        float64 avgV = 0.0;
        uint32 avgCount = avgEnd - avgStart;
        if (avgCount > 0u) {
            for (uint32 j = avgStart; j < avgEnd; j++) {
                avgT += tIn[j];
                avgV += vIn[j];
            }
            avgT /= static_cast<float64>(avgCount);
            avgV /= static_cast<float64>(avgCount);
        }

        /* Find point in current bucket with maximum triangle area */
        uint32 bStart = static_cast<uint32>((static_cast<float64>(i) * bucketSize)) + 1u;
        uint32 bEnd   = static_cast<uint32>((static_cast<float64>(i + 1u) * bucketSize)) + 1u;
        if (bEnd > nIn) { bEnd = nIn; }

        float64 maxArea = -1.0;
        uint32 nextA = bStart;

        float64 aT = tIn[a];
        float64 aV = vIn[a];

        for (uint32 j = bStart; j < bEnd; j++) {
            float64 area = fabs((aT - avgT) * (vIn[j] - aV) -
                               (aT - tIn[j]) * (avgV - aV)) * 0.5;
            if (area > maxArea) {
                maxArea = area;
                nextA = j;
            }
        }

        tOut[nOut] = tIn[nextA];
        vOut[nOut] = vIn[nextA];
        nOut++;
        a = nextA;
    }

    /* Always include the last point */
    tOut[nOut] = tIn[nIn - 1u];
    vOut[nOut] = vIn[nIn - 1u];
    nOut++;

    return nOut;
}

} /* namespace StreamHub */

#endif /* STREAMHUB_LTTB_H_ */
