/******************************************************************************!
 * \file fftAlarm.c
 * \author Sebastien Beaugrand
 * \sa http://beaugrand.chez.com/
 * \copyright CeCILL 2.1 Free Software license
 * \note echo | awk '{ print 8000000 / 64 }'  # 125000 < 200 kHz
 *       echo | awk '{ print 8000000 / 64 / 13 }'  # 9615.38 = Sample rate
 ******************************************************************************/
#include <math.h>
#include "common.h"
#define FPS_O 4
#define FPS 16  // 2 ^ FPS_O

#ifdef tinyX5
# include <avr/io.h>
# include "wiring.h"
#else
# include <stdio.h>
# include <stdint.h>
# include <unistd.h>
# include <stdlib.h>
# include <string.h>
const ssize_t BUFF_MEMSIZE = N * sizeof(int16_t);
const ssize_t OUT_MEMSIZE = N >> 1;
#endif

int16_t fix_fft(int8_t fr[], int8_t fi[], int16_t m, int16_t inverse);

int16_t buff[N];
int8_t data[N];
int8_t im[N];
int gSkipCount;
int gAlarmPulse;
int gAlarmSpace;
int gAlarm;
int gAlarmCount;

/******************************************************************************!
 * \fn setup
 ******************************************************************************/
void setup()
{
#   ifdef tinyX5
    analogInit();
    digitalInit(PINB4, OUTPUT);
    digitalInit(PINB2, OUTPUT);
    digitalInit(PINB1, OUTPUT);
    digitalInit(PINB0, OUTPUT);
    digitalWrite(PINB4, 0);
    digitalWrite(PINB2, ADCSRA & 1);
    digitalWrite(PINB1, ADCSRA & 2);
    digitalWrite(PINB0, ADCSRA & 4);
#   endif
    gSkipCount = 0;
    gAlarmPulse = 0;
    gAlarmSpace = 0;
    gAlarm = 0;
    gAlarmCount = 0;
}

/******************************************************************************!
 * \fn loop
 ******************************************************************************/
void loop()
{
    int i;
    int j;
    int k;
    int32_t adc;
    int min;

#   ifdef tinyX5
    while ((adc = analogRead(PINB3)) < 0) {
        ;
    }
    buff[0] = adc;
    min = adc;
    for (i = 1; i < N; ++i) {
        while ((adc = analogRead(PINB3)) < 0) {
            ;
        }
        buff[i] = adc;
        if (min > adc) {
            min = adc;
        }
    }
#   else
    ssize_t t = 0;
    ssize_t s;
    do {
        s = read(STDIN_FILENO, buff + t, BUFF_MEMSIZE - t);
        if (s <= 0) {
            fprintf(stderr, "fftAlarm: EOF\n");
            exit(EXIT_SUCCESS);
        }
        t += s;
    } while (t < BUFF_MEMSIZE);
    min = buff[0];
    for (i = 1; i < N; ++i) {
        if (min > buff[i]) {
            min = buff[i];
        }
    }
#   endif

    if (++gSkipCount < (RATE >> (M + FPS_O))) {
        return;
    }
    gSkipCount = 0;
#   ifndef tinyX5
    fprintf(stderr, ".");
#   endif

    k = 15;
    for (i = 0; i < N; ++i) {
        buff[i] -= min;
        adc = buff[i];
        for (j = k; (adc << j) > 32767 && j > 0; --j) {
            ;
        }
        if (k > j) {
            k = j;
        }
    }

    int32_t sum = 0;
    for (i = 0; i < N; ++i) {
        if (k & 8) {  // k >= 8
            data[i] = buff[i];
        } else {
            data[i] = buff[i] >> (8 - k);
        }
        sum += data[i];
    }
    int avg = sum / N;
    for (i = 0; i < N; i++) {
        data[i] -= avg;
        im[i] = 0;
    }

    fix_fft(data, im, M, 0);

    int32_t max = 0;
    j = -1;
    for (i = 0; i < (N >> 1); ++i) {
        sum = (data[i] < 0) ? -data[i] : data[i];
        sum += (im[i] < 0) ? -im[i] : im[i];
        if (max < sum) {
            max = sum;
            j = i;
        }
#       ifndef tinyX5
        data[i] = sqrt(data[i] * data[i] + im[i] * im[i]);
#       endif
    }

#  ifndef tinyX5
#   ifndef NDEBUG
    if (j >= 0) {
        fprintf(stderr, "j=%d, freq=%d-%d, level=%d\n",
                j, (j * RATE) >> M, ((j + 1) * RATE) >> M, avg);
    }
#   endif
#  endif
    if (j >= 0) {
        j *= RATE >> M;
    }

#   ifdef tinyX5
    if (gAlarmCount) {
    } else if (j < 0) {
        digitalWrite(PINB2, 0);
        digitalWrite(PINB1, 0);
        digitalWrite(PINB0, 0);
    } else {
        if (j <= 1000) {
            digitalWrite(PINB2, 1);
            digitalWrite(PINB1, 0);
            digitalWrite(PINB0, 0);
        } else if (j <= 2000) {
            digitalWrite(PINB2, 0);
            digitalWrite(PINB1, 1);
            digitalWrite(PINB0, 0);
        } else if (j <= 3000) {
            digitalWrite(PINB2, 1);
            digitalWrite(PINB1, 1);
            digitalWrite(PINB0, 0);
        } else if (j <= 4000) {
            digitalWrite(PINB2, 0);
            digitalWrite(PINB1, 0);
            digitalWrite(PINB0, 1);
        } else if (j <= 5000) {
            digitalWrite(PINB2, 1);
            digitalWrite(PINB1, 0);
            digitalWrite(PINB0, 1);
        } else if (j <= 6000) {
            digitalWrite(PINB2, 0);
            digitalWrite(PINB1, 1);
            digitalWrite(PINB0, 1);
        } else {
            digitalWrite(PINB2, 1);
            digitalWrite(PINB1, 1);
            digitalWrite(PINB0, 1);
        }
    }
#   endif

    if (j > 3000 && j <= 4000) {
        if (gAlarm == 0 && ++gAlarmPulse >= FPS) {
            // [3000Hz, 4000Hz[ pendant au moins 1 seconde
            // avec espaces ne depassant pas 1.5 seconde
            // __________------______------______------__________
            //            0.5s  0.5s  0.5s  0.5s  0.5s
            ++gAlarmCount;
#           ifdef tinyX5
            if (gAlarmCount == 1 || gAlarmCount == 24) {  // 24 * 2.5s = 60s
                digitalWrite(PINB4, 1);
            }
            digitalWrite(PINB2, gAlarmCount & 1);
            digitalWrite(PINB1, gAlarmCount & 2);
            digitalWrite(PINB0, gAlarmCount & 4);
#           else
            fprintf(stderr, "Alarme = %d\n", 1);
#           endif
            gAlarm = FPS >> 2;
            gAlarmPulse = 0;
            gAlarmSpace = 0;
        }
    } else if (gAlarmPulse > 0) {
        if (gAlarm == 0 && ++gAlarmSpace >= FPS + (FPS >> 1)) {
#           ifndef tinyX5
            fprintf(stderr, "Pulse = %d\n", gAlarmPulse);
#           endif
            gAlarmPulse = 0;
            gAlarmSpace = 0;
        }
    }

#   ifndef tinyX5
    if (write(STDOUT_FILENO, data, OUT_MEMSIZE) < OUT_MEMSIZE) {
        fprintf(stderr, "error: write\n");
        exit(EXIT_FAILURE);
    }
#   endif

    if (gAlarm == 1) {
#       ifdef tinyX5
        digitalWrite(PINB4, 0);
#       else
        fprintf(stderr, "Alarme = %d\n", 0);
#       endif
        gAlarm = 0;
    } else if (gAlarm > 1) {
        --gAlarm;
    }
}

/******************************************************************************!
 * \fn main
 ******************************************************************************/
int main()
{
    setup();
    for (;;) {
        loop();
    }
}