#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdbool.h>

#include "common/wave.h"
#include "common/debug.h"
#include "ft8/pack.h"
#include "ft8/encode.h"
#include "ft8/constants.h"

#define LOG_LEVEL LOG_INFO

void gfsk_pulse(int n_spsym, float b, float *pulse)
{
    const float c = M_PI * sqrtf(2 / logf(2));

    for (int i = 0; i < 3 * n_spsym; ++i)
    {
        float t = i / (float)n_spsym - 1.5f;
        pulse[i] = (erff(c * b * (t + 0.5f)) - erff(c * b * (t - 0.5f))) / 2;
    }
}

// Same as synth_fsk, but uses GFSK phase shaping
void synth_gfsk(const uint8_t *symbols, int n_sym, float f0, int n_spsym, int signal_rate, float *signal)
{
    LOG(LOG_DEBUG, "n_spsym = %d\n", n_spsym);
    int n_wave = n_sym * n_spsym;
    float hmod = 1.0f;

    // Compute the smoothed frequency waveform.
    // Length = (nsym+2)*nsps samples, first and last symbols extended
    float dphi_peak = 2 * M_PI * hmod / n_spsym;
    float dphi[n_wave + 2 * n_spsym];

    // Shift frequency up by f0
    for (int i = 0; i < n_wave + 2 * n_spsym; ++i)
    {
        dphi[i] = 2 * M_PI * f0 / signal_rate;
    }

    float pulse[3 * n_spsym];
    gfsk_pulse(n_spsym, 2.0f, pulse);

    for (int i = 0; i < n_sym; ++i)
    {
        int ib = i * n_spsym;
        for (int j = 0; j < 3 * n_spsym; ++j)
        {
            dphi[j + ib] += dphi_peak * symbols[i] * pulse[j];
        }
    }

    // Add dummy symbols at beginning and end with tone values equal to 1st and last symbol, respectively
    for (int j = 0; j < 2 * n_spsym; ++j)
    {
        dphi[j] += dphi_peak * pulse[j + n_spsym] * symbols[0];
        dphi[j + n_sym * n_spsym] += dphi_peak * pulse[j] * symbols[n_sym - 1];
    }

    // Calculate and insert the audio waveform
    float phi = 0;
    for (int k = 0; k < n_wave; ++k)
    { // Don't include dummy symbols
        signal[k] = sinf(phi);
        phi = fmodf(phi + dphi[k + n_spsym], 2 * M_PI);
    }

    // Apply envelope shaping to the first and last symbols
    int n_ramp = n_spsym / 8;
    for (int i = 0; i < n_ramp; ++i)
    {
        float env = (1 - cosf(2 * M_PI * i / (2 * n_ramp))) / 2;
        signal[i] *= env;
        signal[n_wave - 1 - i] *= env;
    }
}

// Convert a sequence of symbols (tones) into a sinewave of continuous phase (FSK).
// Symbol 0 gets encoded as a sine of frequency f0, the others are spaced in increasing
// fashion.
void synth_fsk(const uint8_t *symbols, int num_symbols, float f0, float spacing,
               float symbol_rate, float signal_rate, float *signal)
{
    float phase = 0;
    float dt = 1 / signal_rate;
    float dt_sym = 1 / symbol_rate;
    float t = 0;
    int j = 0;
    int i = 0;
    while (j < num_symbols)
    {
        float f = f0 + symbols[j] * spacing;
        phase = fmodf(phase + 2 * M_PI * f / signal_rate, 2 * M_PI);
        signal[i] = sinf(phase);
        t += dt;
        if (t >= dt_sym)
        {
            // Move to the next symbol
            t -= dt_sym;
            ++j;
        }
        ++i;
    }
}

void usage()
{
    printf("Generate a 15-second WAV file encoding a given message.\n");
    printf("Usage:\n");
    printf("\n");
    printf("gen_ft8 MESSAGE WAV_FILE [FREQUENCY]\n");
    printf("\n");
    printf("(Note that you might have to enclose your message in quote marks if it contains spaces)\n");
}

int main(int argc, char **argv)
{
    // Expect two command-line arguments
    if (argc < 3)
    {
        usage();
        return -1;
    }

    const char *message = argv[1];
    const char *wav_path = argv[2];
    float frequency = 1000.0;
    if (argc > 3)
    {
        frequency = atof(argv[3]);
    }

    // First, pack the text data into binary message
    uint8_t packed[FT8_K_BYTES];
    //int rc = packmsg(message, packed);
    int rc = pack77(message, packed);
    if (rc < 0)
    {
        printf("Cannot parse message!\n");
        printf("RC = %d\n", rc);
        return -2;
    }

    printf("Packed data: ");
    for (int j = 0; j < 10; ++j)
    {
        printf("%02x ", packed[j]);
    }
    printf("\n");

    // Second, encode the binary message as a sequence of FSK tones
    uint8_t tones[FT8_NN]; // FT8_NN = 79, lack of better name at the moment
    //genft8(packed, 0, tones);
    genft8(packed, tones);

    printf("FSK tones: ");
    for (int j = 0; j < FT8_NN; ++j)
    {
        printf("%d", tones[j]);
    }
    printf("\n");

    // Third, convert the FSK tones into an audio signal
    const int sample_rate = 12000;
    const float symbol_rate = 6.25f;
    const int num_samples = (int)(0.5f + FT8_NN / symbol_rate * sample_rate);
    const int num_silence = (15 * sample_rate - num_samples) / 2;
    float signal[num_silence + num_samples + num_silence];
    for (int i = 0; i < num_silence + num_samples + num_silence; i++)
    {
        signal[i] = 0;
    }

    // synth_fsk(tones, FT8_NN, frequency, symbol_rate, symbol_rate, sample_rate, signal + num_silence);
    synth_gfsk(tones, FT8_NN, frequency, sample_rate / symbol_rate, sample_rate, signal + num_silence);
    save_wav(signal, num_silence + num_samples + num_silence, sample_rate, wav_path);

    return 0;
}
