// @BAKE gcc -o $*.out $@ -ggdb
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#if 1
#define N_SPECIAL_BATCHES 3
struct {
    int color;
    const char * chars;
} specials[N_SPECIAL_BATCHES] = {
    {36, "./"},          // path
    {34, "0123456789"},  // numbers
    {31, ":(|&)[]{}-"},  // everything else
};

#define N_KEYWORD_BATCHES 6
struct {
    int color;
    const char * const * const keywords;
} keywords[N_KEYWORD_BATCHES] = {
    // Control
    {33, (const char * const[]) {"done", "do", "for", "in", "while", "select", "break", "continue", "exit", NULL}},
    // Input
    {35, (const char * const[]) {"read", "readarray", "compgen", "complete", "compopt", "bind", NULL}},
    // Bool
    {35, (const char * const[]) {"false", "true", NULL}},
    // Job Management
    {33, (const char * const[]) {"fg", "bg", "disown", "jobs", "kill", NULL}},
    // Directory manipulation
    {31, (const char * const[]) {"cd", "pwd", "popd", "pushd", "dirs", NULL}},
    // Misc.
    {33, (const char * const[]) {"alias", "builtin", "caller", "command", "declare", "echo", "enable", "export", "eval", "exec", "fc", "getopts", "hash", "help", "history", "let", "local", "logout", "mapfile", "printf", "readonly", "return", "set", "shift", "shopt", "source", "suspend", "test", "times", "trap", "type", "typeset", "ulimit", "umask", "unalias", "unset", "wait", NULL}},
};

#define N_REGIONS 4
struct {
    int color;
    const char * start;
    const char * end;
} regions[N_REGIONS] = {
    {32, "\"", "\""},
    {32, "'", "'"},
    {34, "${", "}"},
    {34, "#", "\n"},
};
#else
#define N_SPECIAL_BATCHES 1
struct {
    int color;
    const char * chars;
} specials[N_SPECIAL_BATCHES] = {
    {36, ".,:;<=>+-*%!&~^?|()[]{}"},
};

#define N_KEYWORD_BATCHES 1
struct {
    int color;
    const char * const * const keywords;
} keywords[N_KEYWORD_BATCHES] = {
    // Control
    {33, (const char * const[]) {
        "register",     "volatile",     "auto",         "const",        "static",       "extern",       "if",           "else",
		"do",           "while",        "for",          "continue",     "switch",       "case",         "default",      "break",
		"enum",         "union",        "struct",       "typedef",      "goto",         "void",         "return",       "sizeof",
		"char",         "short",        "int",          "long",         "signed",       "unsigned",     "float",        "double",
        NULL
    }},
};

#define N_REGIONS 4
struct {
    int color;
    const char * start;
    const char * end;
} regions[N_REGIONS] = {
    {32, "\"", "\""},
    {32, "'", "'"},
    {34, "//", "\n"},
    {34, "/*", "*/"},
};
#endif

void highlight(const char * s, int * pos, int * len, int * color) {
    const char * s_base = s;
    for (; *s != '\0'; s++) {
        for (int i = 0; i < N_SPECIAL_BATCHES; i++) {
            for (const char * ss = specials[i].chars; *ss != '\0'; ss++) {
                if (*ss == *s) {
                    *pos = s - s_base;
                    *len = 1;
                    *color = specials[i].color;
                    return;
                }
            }
        }

        for (int i = 0; i < N_KEYWORD_BATCHES; i++) {
            for (const char * const * ss = keywords[i].keywords; *ss != NULL; ss++) {
                if (!strncmp(s, *ss, strlen(*ss))) {
                    *pos = s - s_base;
                    *len = strlen(*ss);
                    *color = keywords[i].color;
                    return;
                }
            }
        }

        for (int i = 0; i < N_REGIONS; i++) {
            if (!strncmp(s, regions[i].start, strlen(regions[i].start))) {
                *pos = s - s_base;
                while (1) {
                    ++s;
                    if (*s == '\0') {
                        goto end;
                    }
                    if (*s == '\\') {
                        if (*(s+1) == '\\') {
                            ++s;
                            continue;
                        }
                        if (!strncmp(s+1, regions[i].end, strlen(regions[i].end))) {
                            s += strlen(regions[i].end);
                            continue;
                        }
                    }
                    if (!strncmp(s, regions[i].end, strlen(regions[i].end))) {
                        goto end;
                    }

                    continue;
                  end:
                    *len = ((s - s_base) - *pos) + 1;
                    *color = regions[i].color;
                    return;
                }
            }
        }
    }

    *pos = s - s_base;
    *len = 0;
    *color = 0;
    return;
}

signed main(int argc, char * argv[]) {
    //const char * input = "for i in 1 2 3 4 5; do echo \"test\\\"${i}\"; done; A=${i}\n";
	FILE* f = fopen(argv[1], "r"); // XXX segv warning

	fseek(f, 0, SEEK_END);
	int flen = ftell(f);
	rewind(f);
	char fstr[flen+1];
	fstr[flen] = '\00';
	fread(fstr, flen, sizeof(char), f);

	fclose(f);

    const char * input = fstr;
    int pos, len, color;

    for (const char * s = input; *s != '\0';) {
        highlight(s, &pos, &len, &color);
        printf("%.*s\033[%dm%.*s\033[0m", pos, s, color, len, s + pos);
        s += pos + len;
    }
}