/**
 * @file white_noise.c
 * @brief Example: generate white noise and visualise its power spectral density.
 *
 * This program demonstrates MD_white_noise() by:
 *   1. Generating Gaussian white noise (N=4096 samples, amplitude 1.0).
 *   2. Computing its PSD via MD_power_spectral_density().
 *   3. Writing the results to CSV and to an interactive HTML visualisation
 *      (Plotly.js, no server required).
 *
 * Build and run (from the repository root):
 *   make                          # build libminidsp.a first
 *   make -C examples plot         # build example, run it, generate HTML
 *   open examples/white_noise.html
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "minidsp.h"
#include "plot_html.h"

/* -----------------------------------------------------------------------
 * Write the interactive HTML visualisation.
 * -----------------------------------------------------------------------*/
static int write_html(const char *path,
                      const double *freqs, const double *psd,
                      unsigned num_bins,
                      double sample_rate, unsigned N)
{
    FILE *fp = fopen(path, "w");
    if (!fp) {
        fprintf(stderr, "cannot open %s for writing\n", path);
        return -1;
    }

    char subtitle[256];
    snprintf(subtitle, sizeof(subtitle),
        "Gaussian white noise &nbsp;|&nbsp;\n"
        "    sample rate %.0f Hz &nbsp;|&nbsp;\n"
        "    FFT size %u",
        sample_rate, N);

    plot_html_begin(fp, "White Noise PSD", subtitle, 0);

    fprintf(fp,
        "  <div id=\"plot\" class=\"plot-container\"></div>\n"
        "  <div class=\"info\">\n"
        "    <strong>What you are seeing:</strong><br>\n"
        "    Gaussian white noise has approximately equal power at all frequencies.\n"
        "    The PSD fluctuates around a constant level.  Hover for exact values.\n"
        "    Use the Plotly toolbar to zoom, pan, or export to PNG/SVG.\n"
        "  </div>\n\n");

    /* ---- Embed PSD data as JS arrays ---- */
    fprintf(fp, "  <script>\n");
    fprintf(fp, "    // PSD data generated by white_noise.c\n");
    plot_html_js_array(fp, "freqs", freqs, num_bins, "%.4f");
    plot_html_js_array(fp, "psd", psd, num_bins, "%.8f");
    fprintf(fp, "\n");

    /* ---- Plotly visualisation ---- */
    fprintf(fp,
        "    Plotly.newPlot('plot', [{\n"
        "      x: freqs, y: psd,\n"
        "      type: 'scatter', mode: 'lines',\n"
        "      line: { color: '#2563eb', width: 1.0 },\n"
        "      name: 'PSD',\n"
        "      hovertemplate: '%%{x:.1f} Hz<br>PSD: %%{y:.6f}<extra></extra>'\n"
        "    }], {\n"
        "      title: { text: 'Power Spectral Density of White Noise', font: { size: 15 } },\n"
        "      xaxis: { title: 'Frequency (Hz)', range: [0, %.1f] },\n"
        "      yaxis: { title: 'Power / bin' },\n"
        "      margin: { t: 55, r: 30, b: 55, l: 65 },\n"
        "      height: 380\n"
        "    }, { responsive: true });\n"
        "  </script>\n",
        sample_rate / 2.0);

    plot_html_end(fp);
    fclose(fp);
    return 0;
}

int main(void)
{
    /* ------------------------------------------------------------------
     * Signal parameters
     * ----------------------------------------------------------------*/
    const unsigned N           = 4096;       /* FFT size (samples)    */
    const double   sample_rate = 44100.0;    /* Hz                    */
    const double   amplitude   = 1.0;
    const unsigned seed        = 42;
    const unsigned num_bins    = N / 2 + 1;

    /* ------------------------------------------------------------------
     * Generate white noise using MD_white_noise
     * ----------------------------------------------------------------*/
    double *signal = malloc(N * sizeof(double));
    double *psd    = malloc(num_bins * sizeof(double));
    double *freqs  = malloc(num_bins * sizeof(double));

    if (!signal || !psd || !freqs) {
        fprintf(stderr, "allocation failed\n");
        return 1;
    }

    //! [generate-signal]
    MD_white_noise(signal, N, amplitude, seed);
    //! [generate-signal]

    /* ------------------------------------------------------------------
     * Compute the power spectral density
     * ----------------------------------------------------------------*/
    MD_power_spectral_density(signal, N, psd);

    for (unsigned k = 0; k < num_bins; k++)
        freqs[k] = (double)k * sample_rate / (double)N;

    /* ------------------------------------------------------------------
     * Write results to CSV
     * ----------------------------------------------------------------*/
    const char *csv_file = "white_noise.csv";
    FILE *fp = fopen(csv_file, "w");
    if (!fp) {
        fprintf(stderr, "cannot open %s for writing\n", csv_file);
        return 1;
    }

    fprintf(fp, "bin,frequency_hz,psd\n");
    for (unsigned k = 0; k < num_bins; k++)
        fprintf(fp, "%u,%.4f,%.8f\n", k, freqs[k], psd[k]);
    fclose(fp);

    printf("Wrote %u frequency bins to %s\n", num_bins, csv_file);

    /* ------------------------------------------------------------------
     * Write interactive HTML visualisation
     * ----------------------------------------------------------------*/
    const char *html_file = "white_noise.html";
    if (write_html(html_file, freqs, psd, num_bins,
                   sample_rate, N) == 0) {
        printf("Wrote interactive plot to %s\n", html_file);
    }

    printf("Signal: Gaussian white noise, amplitude %.1f, seed %u\n",
           amplitude, seed);
    printf("Sample rate: %.0f Hz, FFT size: %u\n", sample_rate, N);

    /* ------------------------------------------------------------------
     * Cleanup
     * ----------------------------------------------------------------*/
    free(freqs);
    free(psd);
    free(signal);
    MD_shutdown();

    return 0;
}
