#define HMMLIB_IMPLEMENTATION
#include "hmmlib.h"
#include "stb_sb.h"
#include <stdbool.h>
#include <getopt.h>

typedef struct {
    char* text;
    int*  lines;
} Index;

static Index* index_find(Index* base, const char* text)
{
    for(size_t i = 0; i < sb_count(base); ++i){
        if(strcmp(base[i].text, text) == 0){
            return base + i;
        }
    }
    return NULL;
}

void hmml_dump(HMML_Output* hmml, bool extra)
{
    if(!hmml){
        puts("(null)");
        return;
    }

    if(!hmml->well_formed){
        printf("Error:%d:%d %s\n", hmml->error.line, hmml->error.col, hmml->error.message);
        return;
    }

    if(extra) {
        puts("Metadata:");
        static const char* meta[] = { "stream_platform", "project", "title", "vod_platform", "id", "output", "template", "medium" };
        for(size_t i = 0; i < countof(meta); ++i) {
            const char* value = ((char**)&hmml->metadata)[i];
            printf("  %s = %s\n", meta[i], value);
        }

        puts("  Credits:");
        for(size_t i = 0; i < hmml->metadata.credit_count; ++i) {
            HMML_Credit* c = hmml->metadata.credits + i;
            printf("    %s [%s]\n", c->name, c->role);
        }

        puts("  Custom:");
        for(size_t i = 0; i < hmml->metadata.custom_count; ++i) {
            HMML_VideoCustomMetaData* m = hmml->metadata.custom + i;
            printf("    %s = %s\n", m->key, m->value);
        }
    }

    puts("Annotations:");
    for(size_t i = 0; i < hmml->annotation_count; ++i){
        HMML_Annotation* a = hmml->annotations + i;

        char time_buf[256];
        char* tp = time_buf;

        if(a->h) {
            *tp++ = (a->h%10) + '0';
            sprintf(tp, ":%02d:%02d", a->m, a->s);
        } else {
            sprintf(tp, "  %2d:%02d", a->m, a->s);
        }

        printf("\t%3d [%s] [%s]\n", a->line, time_buf, a->text);
    }

    Index* authors = NULL;
    Index* markers[HMML_MARKER_COUNT] = {};
    int max_text_len = 0;

    for(size_t i = 0; i < hmml->annotation_count; ++i){
        HMML_Annotation* a = hmml->annotations + i;

        if(a->author){
            int len = strlen(a->author);
            if(len > max_text_len){
                max_text_len = len;
            }

            Index* idx;
            if(!(idx = index_find(authors, a->author))){
                Index x = { .text = a->author };
                sb_push(authors, x);
                idx = &sb_last(authors);
            }

            sb_push(idx->lines, a->line);
        }
    }

    for(size_t i = 0; i < hmml->annotation_count; ++i){
        HMML_Annotation* a = hmml->annotations + i;

        for(size_t j = 0; j < a->marker_count; ++j){
            int   type = a->markers[j].type;
            char* text = a->markers[j].marker;

            int len = strlen(text);
            if(len > max_text_len){
                max_text_len = len;
            }

            Index* idx;
            if(!(idx = index_find(markers[type], text))){
                Index x = { .text = text };
                sb_push(markers[type], x);
                idx = &sb_last(markers[type]);
            }

            sb_push(idx->lines, a->line);
        }
    }

    puts("Authors:");
    for(size_t i = 0; i < sb_count(authors); ++i){
        printf("\t %*s: ", max_text_len, authors[i].text);
        for(size_t j = 0; j < sb_count(authors[i].lines); ++j){
            printf("%3d ", authors[i].lines[j]);
        }
        puts("");
    }


    static const char* m_tags[HMML_MARKER_COUNT] = { "Categories", "Members", "Projects" };

    for(size_t i = 0; i < HMML_MARKER_COUNT; ++i){
        printf("%s:\n", m_tags[i]);
        for(size_t j = 0; j < sb_count(markers[i]); ++j){
            printf("\t %*s: ", max_text_len, markers[i][j].text);
            for(size_t k = 0; k < sb_count(markers[i][j].lines); ++k){
                printf("%3d ", markers[i][j].lines[k]);
            }
            puts("");
        }
    }

    static const char* r_tags[] = { "Site", "Page", "URL", "Title", "Article", "Author", "Editor", "Publisher", "ISBN" };
    puts("References:");
    for(size_t i = 0; i < hmml->annotation_count; ++i){
        HMML_Annotation* a = hmml->annotations + i;
        for(size_t j = 0; j < a->reference_count; ++j){
            printf("\t%3d ", a->line);
            HMML_Reference* r = a->references + j;
            for(size_t k = 0; k < countof(r_tags); ++k){
                char* item = ((char**)r)[k];
                if(item){
                    printf("[%s = %s] ", r_tags[k], item);
                }
            }
            puts("");
        }
    }

    puts("Quotes:");
    for(size_t i = 0; i < hmml->annotation_count; ++i){
        HMML_Annotation* a = hmml->annotations + i;
        if(a->quote.present){
            if(a->quote.author){
                printf("\t%3d [Quote #%d, by %s]", a->line, a->quote.id, a->quote.author);
            } else {
                printf("\t%3d [Quote #%d]", a->line, a->quote.id);
            }
            puts("");
        }
    }

    for(size_t i = 0; i < sb_count(authors); ++i){
        sb_free(authors[i].lines);
    }
    sb_free(authors);

    for(size_t i = 0; i < HMML_MARKER_COUNT; ++i){
        for(size_t j = 0; j < sb_count(markers[i]); ++j){
            sb_free(markers[i][j].lines);
        }
        sb_free(markers[i]);
    }
}

void usage(char* argv0, FILE* out)
{
    fprintf(out, "Usage: %s [-bx] [file]\n", argv0);
}

int main(int argc, char** argv)
{
    bool dump_extra = false;
    bool breakpoint = false;
    int opt;

    while((opt = getopt(argc, argv, "bx")) != -1) {
        if(opt == 'x') {
            dump_extra = true;
        } else if(opt == 'b') {
            breakpoint = true;
        } else {
            usage(argv[0], stderr);
            return 1;
        }
    }

    if(optind >= argc){
        usage(argv[0], stderr);
        return 1;
    }

    argc -= (optind-1);
    argv += (optind-1);

    FILE* f = fopen(argv[1], "r");
    if(!f) {
        perror(argv[1]);
        return 1;
    }

    fseek(f, 0, SEEK_END);
    long size = ftell(f);
    rewind(f);

    char* mem = malloc(size+1);
    mem[size] = 0;

    fread(mem, 1, size, f);
    fclose(f);

    HMML_Output out = hmml_parse(mem);
    free(mem);

    if(breakpoint) {
        asm("int3");
    }

    hmml_dump(&out, dump_extra);

    hmml_free(&out);
}