#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>

// Here I use a lcg RNG with a specificaly crafted period to enumerate all pixels
// and color them by cycling a colormap in a randomish order
// Same approch as in DOOM (1993)
// see: Knuth - The Art of Computer Programming, Volume 2: Seminumerical Algorithms

uint32_t hash32(uint32_t d) {
    // DJB Hash
    uint32_t h = 0x811c9dc5;
    h = (h * 16777619) ^ ((d >>  0) & 0xff);
    h = (h * 16777619) ^ ((d >>  8) & 0xff);
    h = (h * 16777619) ^ ((d >> 16) & 0xff);
    h = (h * 16777619) ^ ((d >> 24) & 0xff);
    return h;
}

// must be the next prime after NUM_TOTAL_LEDS
#define IDLE_PRIME 3203 
// must be a primitive element of the prime residue field |N/IDLE_PRIM
#define IDLE_PRIM_ELEM 777

int IDLE_rand(int *seed){
    return *seed = (*seed * IDLE_PRIM_ELEM ) % IDLE_PRIME;
}

int IDLE_last_pixel   = 34;
int IDLE_color_offset = 2;

uint32_t IDLE_colors[5] ={
    0xd31719,
    0x0f6851,
    0xd4772e,
    0xd84b23,
    0x3d89a7
};


int Nx, Ny;
uint8_t *buf;

void SetPixel(int x, int y, uint8_t r, uint8_t g, uint8_t b){
    buf[3*(x+Nx*y)+0] = r;
    buf[3*(x+Nx*y)+1] = g;
    buf[3*(x+Nx*y)+2] = b;
}

void IDLE_set_pixel(){
    int i = IDLE_rand(&IDLE_last_pixel)-1;
    int x = i%Nx;
    int y = i/Nx;
    if (i == IDLE_color_offset){
        IDLE_color_offset++;
        if (IDLE_color_offset > 100)
            IDLE_color_offset=2;
    } 
    if (i >= Nx*Ny) return;
    int color_count = sizeof(IDLE_colors)/sizeof(IDLE_colors[0]);

    int color = IDLE_colors[hash32(i+IDLE_color_offset)%color_count];
    SetPixel(x, y, (color>>16)&0xff, (color>>8)&0xff, (color>>0)&0xff);
}

int main(int argc, char **argv) {
    Nx = atoi(argv[1]);
    Ny = atoi(argv[2]);
    buf = malloc(Nx*Ny*3);
    int sleepms = atoi(argv[3]);
    if (sleepms == 0) sleepms = 30;

    while(1) {
        IDLE_set_pixel();
        write(1, buf, Nx*Ny*3);
        usleep(1000*sleepms);

    }
}
