#include "args.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define VERSION "0.1.0"

void print_main_help(void) {
    printf("Proof of Transclusion (PoTc) by Diego Cabello using Brombere-Shpilrain-Vdovina SL2 homomorphic hashing as implemented by Benjamin Weinstein-Raun\n\n");
    printf("\x1b[1m\x1b[4mUsage:\x1b[0m\x1b[1m potc <command> <subcommand>\x1b[0m\n\n");
    printf("\x1b[1m\x1b[4mCommands:\x1b[0m\n");
    printf("  \x1b[1msequinclusion\x1b[0m      sequinclusion proof commands (see sequinclusion --help)\n");
    printf("  \x1b[1mtransclusion\x1b[0m       transclusion proof commands  (see transclusion --help)\n");
    printf("  \x1b[1msinglehash\x1b[0m         hash input via stdin, --file, or --text\n\n");
    printf("\x1b[1m\x1b[4mSubcommands:\x1b[0m\n");
    printf("  \x1b[1mgenerate\x1b[0m           generate a proof\n");
    printf("  \x1b[1mverify\x1b[0m             verify proofs\n\n");
    printf("\x1b[1m\x1b[4mFlags and Variables:\x1b[0m\n");
    printf("  \x1b[1m-h\x1b[0m, \x1b[1m--help\x1b[0m         print help\n");
    printf("  \x1b[1m-V\x1b[0m, \x1b[1m--version\x1b[0m      print version\n");
    printf("  \x1b[1m-f\x1b[0m, \x1b[1m--file\x1b[0m         file to hash\n");
    printf("  \x1b[1m-t\x1b[0m, \x1b[1m--text\x1b[0m         text to hash directly\n");
    printf("  \x1b[1m-i\x1b[0m, \x1b[1m--index\x1b[0m        byte range: start-end\n");
    printf("      \x1b[1m--index-base\x1b[0m   base for byte indexing (0 or 1) [default: 0]\n\n");
    printf("  \x1b[1m$POTC_INDEX_BASE\x1b[0m   environment variable for default index base (0 or 1)\n");
}

void print_sequinclusion_help(void) {
    printf("Sequinclusion proof commands\n\n");
    printf("Input (generate): single source via -f <file> or -t <text>\n");
    printf("Input (verify):   256-char proof strings directly as arguments or stdin\n\n");
    printf("\x1b[1m\x1b[4mUsage:\x1b[0m\x1b[1m potc sequinclusion <subcommand>\x1b[0m\n\n");
    printf("\x1b[1m\x1b[4mSubcommands:\x1b[0m\n");
    printf("  \x1b[1mgenerate\x1b[0m       Generate a proof\n");
    printf("  \x1b[1mverify\x1b[0m         Verify proofs\n\n");
    printf("\x1b[1m\x1b[4mFlags and Variables:\x1b[0m\n");
    printf("  \x1b[1m-h\x1b[0m, \x1b[1m--help\x1b[0m         print help\n");
    printf("  \x1b[1m-V\x1b[0m, \x1b[1m--version\x1b[0m      print version\n");
    printf("  \x1b[1m-f\x1b[0m, \x1b[1m--file\x1b[0m         file to hash\n");
    printf("  \x1b[1m-t\x1b[0m, \x1b[1m--text\x1b[0m         text to hash directly\n");
    printf("  \x1b[1m-i\x1b[0m, \x1b[1m--index\x1b[0m        byte range: start-end\n");
    printf("      \x1b[1m--index-base\x1b[0m   base for byte indexing (0 or 1) [default: 0]\n\n");
    printf("  \x1b[1m$POTC_INDEX_BASE\x1b[0m   environment variable for default index base (0 or 1)\n");
}

void print_transclusion_help(void) {
    printf("Transclusion proof commands\n\n");
    printf("Input (generate): two sources via -f/-t pairs, each with corresponding -i range\n");
    printf("Input (verify):   512-char (only, no 256 pairs) proof strings directly as arguments or stdin, or sources as pairs with -f/-t and -i\n\n");
    printf("\x1b[1m\x1b[4mUsage:\x1b[0m\x1b[1m potc transclusion <subcommand>\x1b[0m\n\n");
    printf("\x1b[1m\x1b[4mSubcommands:\x1b[0m\n");
    printf("  \x1b[1mgenerate\x1b[0m       Generate a proof\n");
    printf("  \x1b[1mverify\x1b[0m         Verify proofs\n\n");
    printf("\x1b[1m\x1b[4mFlags and Variables:\x1b[0m\n");
    printf("  \x1b[1m-h\x1b[0m, \x1b[1m--help\x1b[0m         print help\n");
    printf("  \x1b[1m-V\x1b[0m, \x1b[1m--version\x1b[0m      print version\n");
    printf("  \x1b[1m-f\x1b[0m, \x1b[1m--file\x1b[0m         file to hash (use multiple times)\n");
    printf("  \x1b[1m-t\x1b[0m, \x1b[1m--text\x1b[0m         text to hash directly (use multiple times)\n");
    printf("  \x1b[1m-i\x1b[0m, \x1b[1m--index\x1b[0m        byte range: start-end (use multiple times)\n");
    printf("      \x1b[1m--index-base\x1b[0m   base for byte indexing (0 or 1) [default: 0]\n\n");
    printf("  \x1b[1m$POTC_INDEX_BASE\x1b[0m   environment variable for default index base (0 or 1)\n");
}

void print_version(void) {
    printf("potc %s\n", VERSION);
}

int parse_range(const char* s, Index* index) {
    char* dash = strchr(s, '-');
    if (!dash) {
        fprintf(stderr, "error: expected format: start-end\n");
        return -1;
    }

    *dash = '\0';
    index->start = atoi(s);
    index->end = atoi(dash + 1);
    *dash = '-';

    if (index->end < index->start) {
        fprintf(stderr, "error: end must be >= start\n");
        return -1;
    }

    return 0;
}

Args parse_args(int argc, char** argv) {
    Args args = {0};
    args.index_base = -1;  // Not set

    if (argc < 2) {
        print_main_help();
        exit(0);
    }

    int i = 1;

    // Check for help/version at top level
    if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
        print_main_help();
        exit(0);
    }
    if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {
        print_version();
        exit(0);
    }

    // Parse command
    if (strcmp(argv[i], "sequinclusion") == 0) {
        i++;
        if (i >= argc) {
            print_sequinclusion_help();
            exit(0);
        }

        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            print_sequinclusion_help();
            exit(0);
        }

        if (strcmp(argv[i], "generate") == 0) {
            args.command = CMD_SEQUINCLUSION_GENERATE;
        } else if (strcmp(argv[i], "verify") == 0) {
            args.command = CMD_SEQUINCLUSION_VERIFY;
        } else {
            fprintf(stderr, "error: unknown subcommand '%s'\n", argv[i]);
            exit(1);
        }
        i++;
    } else if (strcmp(argv[i], "transclusion") == 0) {
        i++;
        if (i >= argc) {
            print_transclusion_help();
            exit(0);
        }

        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            print_transclusion_help();
            exit(0);
        }

        if (strcmp(argv[i], "generate") == 0) {
            args.command = CMD_TRANSCLUSION_GENERATE;
        } else if (strcmp(argv[i], "verify") == 0) {
            args.command = CMD_TRANSCLUSION_VERIFY;
        } else {
            fprintf(stderr, "error: unknown subcommand '%s'\n", argv[i]);
            exit(1);
        }
        i++;
    } else if (strcmp(argv[i], "singlehash") == 0) {
        args.command = CMD_SINGLEHASH;
        i++;
    } else {
        fprintf(stderr, "error: unknown command '%s'\n", argv[i]);
        exit(1);
    }

    // Parse flags
    while (i < argc) {
        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            args.help = true;
            i++;
        } else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {
            print_version();
            exit(0);
        } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--file") == 0) {
            i++;
            if (i >= argc) {
                fprintf(stderr, "error: -f requires an argument\n");
                exit(1);
            }
            if (args.file_count >= MAX_FILES) {
                fprintf(stderr, "error: too many files\n");
                exit(1);
            }
            args.files[args.file_count++] = argv[i];
            i++;
        } else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--text") == 0) {
            i++;
            if (i >= argc) {
                fprintf(stderr, "error: -t requires an argument\n");
                exit(1);
            }
            if (args.text_count >= MAX_TEXTS) {
                fprintf(stderr, "error: too many texts\n");
                exit(1);
            }
            args.texts[args.text_count++] = argv[i];
            i++;
        } else if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--index") == 0) {
            i++;
            if (i >= argc) {
                fprintf(stderr, "error: -i requires an argument\n");
                exit(1);
            }
            if (args.index_count >= MAX_INDICES) {
                fprintf(stderr, "error: too many indices\n");
                exit(1);
            }
            if (parse_range(argv[i], &args.indices[args.index_count]) != 0) {
                exit(1);
            }
            args.index_count++;
            i++;
        } else if (strcmp(argv[i], "--index-base") == 0) {
            i++;
            if (i >= argc) {
                fprintf(stderr, "error: --index-base requires an argument\n");
                exit(1);
            }
            int base = atoi(argv[i]);
            if (base != 0 && base != 1) {
                fprintf(stderr, "error: --index-base must be 0 or 1\n");
                exit(1);
            }
            args.index_base = base;
            i++;
        } else {
            // Treat as proof
            if (args.proof_count >= MAX_PROOFS) {
                fprintf(stderr, "error: too many proofs\n");
                exit(1);
            }
            args.proofs[args.proof_count++] = argv[i];
            i++;
        }
    }

    return args;
}
