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

#define STRING_PARTS len

int STRING_PARTS;
double *Y;
int t_next, t_prev, t_cur;

/*
 * Macros used to manipulate the allocated memory for Y.
 */
#define GET_Y(i, t) \
    Y[i + t * STRING_PARTS]

#define SET_Y(i, t, val) \
    Y[i + t * STRING_PARTS] = val

/*
 * Fill the allocated memory for Y with a sinus shape.
 */
static void init_sinus(int phases) {
    double c = (double) (STRING_PARTS * phases) * M_PI ;

    for( int i = 0; i < STRING_PARTS; i++ ) {
        SET_Y(i, 0, sin(((double) i) / c));
    }
}

/*
 * Fill the allocated memory for Y with a pluck shape.
 */
static void init_pluck(double pluck, double length, double dx) {
    int ipos = ((int) dx) + 1, i;
    fprintf(stderr, "ipos: %d\n", ipos);

    for( i = 0; i < ipos; i++ ) {
        SET_Y(i, 0, i * dx / pluck);
    }

    for( ; i < STRING_PARTS; i++ ) {
        SET_Y(i, 0, ((length - i) * dx) / (length - ipos));
    }
}

static void connect_string_ends() {
    const int end = STRING_PARTS - 1;
    Y[STRING_PARTS * 2] = Y[STRING_PARTS * 1] = Y[STRING_PARTS];
    Y[STRING_PARTS * 2 + end] = Y[STRING_PARTS * 1 + end] = Y[STRING_PARTS + end];
}

/*
 * Calculate position over the entire string at time 't_cur' (= dt).
 */
static void iterate(double C, int t_prev, int t_cur, int t_next) {
    for( int i = 1, max = STRING_PARTS - 1; i < max; i++ ) {
        SET_Y(i, t_next, \
            C * (GET_Y(i-1, t_cur) - 2 * GET_Y(i, t_cur) + GET_Y(i+1, t_cur))
            - GET_Y(i, t_prev)
            + 2 * GET_Y(i, t_cur));
    }
}

/*
 * Integer XOR swapping macro.
 */
#define SWAP(a, b) \
    a ^= b; \
    b ^= a; \
    a ^= b;

static void plot(int col) {
    printf("%e", GET_Y(0, col));

    for( int i = 1; i < STRING_PARTS; i++ )
        printf(",%e", GET_Y(i, col));

    printf("\n");
}

int main(int argc, char **argv) {

    if(argc < 7) {
        fprintf(stderr, "Usage: %s <p|s> ITERATIONS LENGTH STEP_SIZE"\
                        " TAU <PLUCK|PHASES>\n",
                argv[0]);
        return 1;
    }

    // Initialise time indices, iterations and string length.
    int t_prev = 0, t_cur = 1, t_next = 2,
        iterations = atoi(argv[2]);

    double length = atof(argv[3]),
        dx = atof(argv[4]),
        tau = atof(argv[5]);

    STRING_PARTS = ceil(length / dx) + 1;

    if( !(Y = (double *) malloc(3 * STRING_PARTS * sizeof(double))) )
        return 2;

    // Initialise the requested shape in the allocated memory.
    switch(*argv[1]) {
    case 'p': init_pluck(atof(argv[6]), length, dx); break;
    case 's': init_sinus(atoi(argv[6])); break;
    default:
        fprintf(stderr, "Argument 2 '%s' does not look like a 'p' or 's'.\n",
                argv[1]);
        return 1;
    }

    // The string is connected at both ends.
    connect_string_ends();

    plot(t_prev);

    for( int i = 0; i < iterations; i++ ) {
        iterate(tau, t_prev, t_cur, t_next);
        SWAP(t_cur, t_next);
        SWAP(t_prev, t_next);
        //plot(t_cur);
    }

    return 0;
}
