// "Bleh" -- a "potato-friendly" cmatrix clone.
// Compile this with gcc bleh.c -o bleh -static -O2. Or gcc bleh.c -o bleh -Bstatic if you are on MacOS.
// Screenshot: https://i.imgur.com/dt6RmU7.png
// Docker image (RISCV included): https://hub.docker.com/r/defnotgustavom/bleh

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#define DELAY 80000
#define RAND_RANGE 94
#define MIN_STREAM_LEN 5

typedef struct {
    short x;
    short y;
    short prev_y;
    unsigned char length;
    unsigned char active;
} Stream;

static inline void get_terminal_size(short* rows, short* cols) {
    struct winsize ws;
    ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
    *rows = ws.ws_row;
    *cols = ws.ws_col;
}

static inline int kbhit(void) {
    struct termios t = {0};
    tcgetattr(STDIN_FILENO, &t);
    t.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &t);
    int f = fcntl(STDIN_FILENO, F_GETFL, 0);
    fcntl(STDIN_FILENO, F_SETFL, f | O_NONBLOCK);
    int ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &t);
    fcntl(STDIN_FILENO, F_SETFL, f);
    if (ch != EOF) {
        ungetc(ch, stdin);
        return 1;
    }
    return 0;
}

int main() {
    short max_y, max_x;
    get_terminal_size(&max_y, &max_x);
    
    srand(time(NULL));
    Stream* streams = calloc(max_x, sizeof(Stream));
    if (!streams) return 1;
    
    for (short i = 0; i < max_x; i++) {
        streams[i].x = i;
        streams[i].y = rand() % max_y;
        streams[i].prev_y = streams[i].y;
        streams[i].length = (rand() % (max_y / 2)) + MIN_STREAM_LEN;
        streams[i].active = rand() & 1;
    }
    
    printf("\033[2J\033[?25l");
    
    while (!kbhit()) {
        for (short i = 0; i < max_x; i++) {
            if (streams[i].active) {
                short y = streams[i].y;
                short prev_y = streams[i].prev_y;
                unsigned char len = streams[i].length;
                
                
                if (y != prev_y) {
                    for (unsigned char j = 0; j < len; j++) {
                        short prev_pos = prev_y - j;
                        if (prev_pos >= 0 && prev_pos < max_y) {
                            printf("\033[%d;%dH ", prev_pos + 1, i + 1);
                        }
                    }
                }
                
                if (y < max_y + len) {
                    for (unsigned char j = 0; j < len; j++) {
                        short pos = y - j;
                        if (pos >= 0 && pos < max_y) {
                            printf("\033[%d;%dH%s%c", 
                                   pos + 1, i + 1,
                                   j == 0 ? "\033[97m" : "\033[32m",
                                   (rand() % RAND_RANGE) + 33);
                        }
                    }
                }
                
                streams[i].prev_y = y;
                if (++streams[i].y - len >= max_y) {
                    streams[i].y = 0;
                    streams[i].prev_y = 0;
                    streams[i].length = (rand() % (max_y / 2)) + MIN_STREAM_LEN;
                    streams[i].active = (rand() % 5) != 0;
                }
            } else if ((rand() % 50) == 0) {
                streams[i].active = 1;
                streams[i].y = 0;
                streams[i].prev_y = 0;
                streams[i].length = (rand() % (max_y / 2)) + MIN_STREAM_LEN;
            }
        }
        
        fflush(stdout);
        usleep(DELAY);
    }
    
    printf("\033[?25h\033[0m");
    fflush(stdout);
    
    free(streams);
    return 0;
}