/* test_apcf.cpp */

#include <cstdio>
#include <cstdlib>
#include <ctime>

#include "ics.h"

int
main()
{
    // shape of input data in Airy detectors and t dimensions
    ssize_t shape[3] = {1, 32, 10000000};
    ssize_t nbins = 256;
    double threshold = 0.0;
    double filter = 0.7;
    int nthreads = 0;
    int autocorr = ICS_TRUE;

    int status;
    clock_t t;
    double duration;

    printf(
        "Data shape=(%zd, %zd, %zd) dtype=uint16\n",
        shape[0],
        shape[1],
        shape[2]);
    size_t data_size =
        static_cast<size_t>(shape[0]) * shape[1] * shape[2] * sizeof(uint16_t);
    constexpr ssize_t su = static_cast<ssize_t>(sizeof(uint16_t));
    ssize_t strides[3] = {
        /* y */ su * shape[2] * shape[1],
        /* x */ su * shape[2],
        /* t */ su,
    };

    // allocate buffer to contain the whole file
    uint16_t *data = static_cast<uint16_t *>(malloc(data_size));
    if (data == NULL) {
        fputs("Memory error: data", stderr);
        return 1;
    }

    // open input file
    FILE *pfile;
    errno_t err = fopen_s(&pfile, "Airy_Detectors.bin", "rb");
    if (err != 0) {
        fputs("File open error", stderr);
        free(data);
        return 2;
    }
    // read file into data array
    size_t result = fread(data, 1, data_size, pfile);
    if (result != data_size) {
        fputs("File read error", stderr);
        fclose(pfile);
        free(data);
        return 3;
    }
    // close file
    fclose(pfile);

    if (1) {
        // correct for bleaching
        size_t mean_size =
            static_cast<size_t>(shape[0]) * shape[1] * sizeof(double);
        double *mean = static_cast<double *>(malloc(mean_size));
        if (mean == NULL) {
            fputs("Memory error: mean", stderr);
            free(data);
            return 4;
        }
        constexpr ssize_t sd = static_cast<ssize_t>(sizeof(double));
        ssize_t meanstrides[2] = {sd * shape[1], sd};

        t = clock();
        status = yxt_correct_bleaching_H(
            data, shape, strides, mean, meanstrides, 0.99, nthreads);

        if (status != ICS_OK) {
            fputs("yxt_correct_bleaching_H failed", stderr);
            free(mean);
            free(data);
            return 9;
        }
        t = clock() - t;
        duration = (static_cast<double>(t)) / CLOCKS_PER_SEC;
        printf("Correct bleaching: %f s\n", duration);

        // save filtered
        err = fopen_s(&pfile, "Airy_Detectors.filtered.bin", "wb");
        if (err != 0) {
            fputs("File open error", stderr);
            free(mean);
            free(data);
            return 10;
        }
        result = fwrite(data, 1, data_size, pfile);
        if (result != data_size) {
            fputs("File write error", stderr);
            fclose(pfile);
            free(mean);
            free(data);
            return 11;
        }
        fclose(pfile);

        // save mean
        err = fopen_s(&pfile, "Airy_Detectors.mean.bin", "wb");
        if (err != 0) {
            fputs("File open error", stderr);
            free(mean);
            free(data);
            return 12;
        }
        result = fwrite(mean, 1, mean_size, pfile);
        if (result != mean_size) {
            fputs("File write error", stderr);
            fclose(pfile);
            free(mean);
            free(data);
            return 13;
        }
        fclose(pfile);

        free(mean);
    }

    // open yxt handle
    yxt_handle handle = yxt_new(shape);
    if (handle == NULL) {
        fputs("Memory error: yxt_handle", stderr);
        free(data);
        return 5;
    }

    // get handle shape; tsize is cropped to power of two
    yxt_get_buffer(handle, shape, NULL);
    printf(
        "Buffer shape=(%zd, %zd, %zd) dtype=float64\n",
        shape[0],
        shape[1],
        shape[2]);

    // log-bins array
    ssize_t *bins = static_cast<ssize_t *>(malloc(nbins * sizeof(ssize_t)));
    if (bins == NULL) {
        fputs("Memory error: bins", stderr);
        yxt_del(handle);
        free(data);
        return 6;
    }
    nbins = logbins(shape[2] / 2, nbins, bins);
    printf("Bins");
    for (ssize_t i = 0; i < nbins; i++) {
        printf(" %zd", bins[i]);
    }
    printf("\n");

    // output array
    ssize_t shapeout[3] = {
        shape[1], shape[1] - ((autocorr == ICS_FALSE) ? 1 : 0), nbins};
    printf(
        "Output shape=(%zd, %zd, %zd) dtype=float32\n",
        shapeout[0],
        shapeout[1],
        shapeout[2]);
    ssize_t out_count = shapeout[0] * shapeout[1] * shapeout[2];
    float *out = static_cast<float *>(malloc(out_count * sizeof(float)));
    if (out == NULL) {
        fputs("Memory error: out", stderr);
        yxt_del(handle);
        free(data);
        free(bins);
        return 8;
    }
    constexpr ssize_t sf = static_cast<ssize_t>(sizeof(float));
    ssize_t stridesout[3] = {
        sf * shapeout[2] * shapeout[1],
        sf * shapeout[2],
        sf,
    };
    // initialize output
    for (ssize_t i = 0; i < out_count; i++) {
        out[i] = 0.0f;
    }

    // start timer
    t = clock();

    // cross correlate
    status = yxt_apcf_Hf(
        handle,
        data,
        &strides[1],
        out,
        stridesout,
        bins,
        nbins,
        autocorr,
        filter,
        nthreads);

    if (status != ICS_OK) {
        fputs("yxt_apcf_Hf failed", stderr);
        yxt_del(handle);
        free(data);
        free(bins);
        free(out);
        return 14;
    }

    // print elapsed time
    t = clock() - t;
    duration = (static_cast<double>(t)) / CLOCKS_PER_SEC;
    printf("Duration: %f s\n", duration);

    // open output file
    err = fopen_s(&pfile, "Airy_Detectors.apcf.bin", "wb");
    if (err != 0) {
        fputs("File open error", stderr);
        yxt_del(handle);
        free(data);
        free(bins);
        free(out);
        return 15;
    }
    // write out array to file
    size_t out_bytes = static_cast<size_t>(out_count) * sizeof(float);
    result = fwrite(out, 1, out_bytes, pfile);
    if (result != out_bytes) {
        fputs("File write error", stderr);
        fclose(pfile);
        yxt_del(handle);
        free(data);
        free(bins);
        free(out);
        return 16;
    }
    // close file
    fclose(pfile);

    // close handle and free memory
    yxt_del(handle);
    free(data);
    free(bins);
    free(out);

    return 0;
}
