/******************************************************************************!
 * \file fft.c
 * \author Sebastien Beaugrand
 * \sa http://beaugrand.chez.com/
 * \copyright CeCILL 2.1 Free Software license
 * \note http://www.fftw.org/benchfft/ffts.html
 ******************************************************************************/
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <linux/tcp.h>
#include <complex.h>
#include <fftw3.h>
#include <math.h>
#include "debug.h"

void sockInit(int argc, char* argv[]);
ssize_t sockRead(unsigned char* buff, size_t count);
void sockQuit();

void plotInit(unsigned int samplesSize,
              const char* min,
              const char* max);
void plotShowSamples(fftw_complex* samples,
                     unsigned int min,
                     unsigned int max);
void plotShowFFT(fftw_complex* samples,
                 fftw_complex* output);
void plotQuit();

static fftw_complex* gSamples1 = NULL;
static fftw_complex* gSamples2 = NULL;
static fftw_complex* gSamples = NULL;
static fftw_complex* gOutput = NULL;
static fftw_plan gPlan1;
static fftw_plan gPlan2;

/******************************************************************************!
 * \fn controlC
 ******************************************************************************/
void controlC(int sig)
{
    if (sig == SIGINT) {
        fftw_destroy_plan(gPlan1);
        fftw_destroy_plan(gPlan2);
        fftw_free(gOutput);
        fftw_free(gSamples1);
        fftw_free(gSamples2);
        sockQuit();
        plotQuit();
        exit(EXIT_SUCCESS);
    }
}

/******************************************************************************!
 * \fn initFFT
 ******************************************************************************/
void initFFT(unsigned int size)
{
    unsigned int i;

    gSamples1 = fftw_alloc_complex(size);
    if (! gSamples1) {
        ERROR("fftw_alloc_complex");
        exit(EXIT_FAILURE);
    }
    gSamples2 = fftw_alloc_complex(size);
    if (! gSamples2) {
        ERROR("fftw_alloc_complex");
        exit(EXIT_FAILURE);
    }
    gSamples = gSamples1;
    gOutput = fftw_alloc_complex(size);
    if (! gOutput) {
        ERROR("fftw_alloc_complex");
        exit(EXIT_FAILURE);
    }
    gPlan1 = fftw_plan_dft_1d(size,
                              gSamples1, gOutput, FFTW_FORWARD, FFTW_ESTIMATE);
    if (! gPlan1) {
        ERROR("fftw_plan_dft_1d");
        exit(EXIT_FAILURE);
    }
    gPlan2 = fftw_plan_dft_1d(size,
                              gSamples2, gOutput, FFTW_FORWARD, FFTW_ESTIMATE);
    if (! gPlan2) {
        ERROR("fftw_plan_dft_1d");
        exit(EXIT_FAILURE);
    }
    for (i = 0; i < size; ++i) {
        gSamples1[i] = 0;
        gSamples2[i] = 0;
        gOutput[i] = 0;
    }
}

/******************************************************************************!
 * \fn main
 ******************************************************************************/
int main(int argc, char* argv[])
{
    const unsigned int HEADER_SIZE = sizeof(uint32_t) << 1;
    const unsigned int SAMPLE_SIZE = 1 << (int) log2(TCP_MSS_DEFAULT -
                                                     HEADER_SIZE);
    const unsigned int PACKET_SIZE = HEADER_SIZE + SAMPLE_SIZE;
    const unsigned int SAMPLES_SIZE = SAMPLE_SIZE;  // ariettaG25: build/adc 18
    //const unsigned int SAMPLES_SIZE = SAMPLE_SIZE << 3;  // G25: build/adc 13

    uint16_t buff[SAMPLES_SIZE >> 1];
    int pos;
    uint16_t uint2;
    uint32_t uint4;
    double t;
    ssize_t len;
    unsigned int samplesPos = 0;
    unsigned int savePos = 0;
    uint32_t count = 0;

    sockInit(argc, argv);

    DEBUG("TCP_MSS_DEFAULT = %d", TCP_MSS_DEFAULT);
    DEBUG("SAMPLE_SIZE = %d", SAMPLE_SIZE);
    DEBUG("PACKET_SIZE = %d", PACKET_SIZE);
    DEBUG("SAMPLES_SIZE = %d", SAMPLES_SIZE);

    plotInit(SAMPLES_SIZE, argv[1], argv[2]);
    initFFT(SAMPLES_SIZE);

    for (;;) {
        len = sockRead((unsigned char*) buff, PACKET_SIZE) >> 1;
        uint4 =
            (buff[0] << 16) +
            (buff[1]);
        pos = 2;
        if (count != 0 && uint4 != count + 1) {
            ERROR("paquet voulu: %d, paquet recu:%d\n", count + 1, uint4);
        }
        count = uint4;
        uint4 =
            (buff[pos] << 16) +
            (buff[pos + 1]);
        pos += 2;
        if (uint4 != SAMPLE_SIZE) {
            ERROR("taille voulue: %d, taille recue:%d\n", SAMPLE_SIZE, uint4);
        }
        while (pos < len) {
            uint2 = buff[pos++];
            uint4 =
                (buff[pos] << 16) +
                (buff[pos + 1]);
            pos += 2;
            t = uint2 + (double) uint4 / 1000000;
            uint2 = buff[pos++];
            gSamples[samplesPos++] = t + I * uint2;
            if (pos >= len) {
                plotShowSamples(gSamples, savePos, samplesPos);
                savePos = samplesPos;
            }
            if (samplesPos == SAMPLES_SIZE) {
                if (gSamples == gSamples1) {
                    fftw_execute(gPlan1);
                } else {
                    fftw_execute(gPlan2);
                }
                plotShowFFT(gSamples, gOutput);
                if (gSamples != gSamples1) {
                    gSamples = gSamples1;
                } else {
                    gSamples = gSamples2;
                }
                samplesPos = 0;
                savePos = 0;
            }
        }
    }
}