// Roman C Compiler 2 code generator
// Copyright (C) 2025 t.me/pyproman
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.



typedef struct {
    U8** registers;
    U8 len;
} TargetConfig;

TargetConfig gc24 = {
    .registers = (U8*[6]){
        "%ax", "%bx", "%cx", "%dx", "%si", "%gi"
    },
    .len = 6
};

typedef struct {
    U8* strs[64];
    U8 strcnt;
    U8 ifcnt;
    U8 whilecnt;
} FnState;
typedef struct {
    FnState* fn;
    U8 next_reg;
} ExprState;

U8* reg_res(ExprState* es) {
    return gc24.registers[es->next_reg++];
}
ERR reg_add(ExprState* es, U32 val) {
    if(es->next_reg >= gc24.len) {
        error("Register overflow");
        return Error;
    }
    printf("  mov %s %d\n", gc24.registers[es->next_reg], val);
    es->next_reg++;
}
U8* reg_rem(ExprState* es) {
    return gc24.registers[--es->next_reg];
}
U0 reg_exc_bin(ExprState* es, U8* dat) {
    U8* firstr = gc24.registers[es->next_reg - 2];
    U8* secondr = gc24.registers[es->next_reg - 1];
    if (!strcmp(dat, "+")) {
        printf("  add %s %s\n", firstr, secondr);
    } else if (!strcmp(dat, "-")) {
        printf("  sub %s %s\n", firstr, secondr);
    } else if (!strcmp(dat, "*")) {
        printf("  mul %s %s\n", firstr, secondr);
    } else if (!strcmp(dat, "/")) {
        printf("  div %s %s\n", firstr, secondr);
    } else if (!strcmp(dat, "=")) {
        printf("  stob %s %s\n", firstr, secondr);
    }
    reg_rem(es);
}
// Initial (execution) syntax:
// a: b [+-] b
// b: c [*/] c
// c: num | ( a )
ERR parse_a(ExprState* es, Tokenizer* tok);
ERR parse_d(ExprState* es, Tokenizer* tok) {
    if (tok->cur.type == KeywordToken && !strcmp(tok->cur.data.str, "(")) {
        tokenizer_next(tok);
        if(parse_a(es, tok)!=Ok)return Error;
        if (tok->cur.type != KeywordToken || strcmp(tok->cur.data.str, ")")) {
            error("Ty debil");
            return Error;
        }
        tokenizer_next(tok);
    } else if (tok->cur.type == KeywordToken && !strcmp(tok->cur.data.str, "*")) {
        tokenizer_next(tok);
        if(parse_d(es, tok)!=Ok)return Error;
        U8* reg = gc24.registers[es->next_reg - 1];
        printf("  lodb %s %s\n", reg, reg);
        printf("  dex %s\n", reg, reg);
    } else if (tok->cur.type == NumberToken) {
        //printf("%d ", tok->cur.data.number);
        reg_add(es, tok->cur.data.number);
        tokenizer_next(tok);
    } else if (tok->cur.type == NameToken) {
        U8 kek[16];
        memcpy(kek, tok->cur.data.str, 16);
        tokenizer_next(tok);
        if (tok->cur.type == KeywordToken && !strcmp(tok->cur.data.str, "(")) {
            tokenizer_next(tok);
            U8 bup = es->next_reg;
            for(U8 i = 0; i < bup; i++) {
                printf("  push %s\n", gc24.registers[i]);
            }
            es->next_reg = 0;

            if(tok->cur.type == KeywordToken && !strcmp(tok->cur.data.str, ")")) {
                tokenizer_next(tok);
            } else while (1) {
                if(parse_a(es, tok)!=Ok)return Error;
                printf("  push %s\n", reg_rem(es));
                if (tok->cur.type == KeywordToken) {
                    if (!strcmp(tok->cur.data.str, ",")) {
                        tokenizer_next(tok);
                        continue;
                    }
                    if (!strcmp(tok->cur.data.str, ")")) {
                        tokenizer_next(tok);
                        break;
                    }
                }
                print_token(&(tok->cur));
                error(", or )");
                return Error;
            }
            printf("  call %s\n", kek);
            es->next_reg = bup;
            printf("  pop %s\n", reg_res(es));
            for(U8 i = 0; i < bup; i++) {
                printf("  pop %s\n", gc24.registers[i]);
            }
        } else {
            printf("  mov %s %s\n", reg_res(es), kek);
        }
    } else if (tok->cur.type == StrToken) {
        U8 sid = es->fn->strcnt++;
        printf("  mov %s .str%d\n", reg_res(es), sid);
        es->fn->strs[sid] = tok->cur.data.ptr;
        tokenizer_next(tok);
    }
    return Ok;
}

ERR parse_c(ExprState* es, Tokenizer* tok) {
    if(parse_d(es, tok)!=Ok)return Error;
    while(
        (tok->cur.type == KeywordToken) &&
        (tok->cur.data.str[1] == 0) &&
        ((tok->cur.data.str[0] == '*') || (tok->cur.data.str[0] == '/'))
    ) {
        U8 op[2] = {tok->cur.data.str[0], 0};
        tokenizer_next(tok);
        if(parse_d(es, tok)!=Ok)return Error;
        reg_exc_bin(es, op);
    }
    return Ok;
}


ERR parse_b(ExprState* es, Tokenizer* tok) {
    if(parse_c(es, tok)!=Ok)return Error;
    while(
        (tok->cur.type == KeywordToken) &&
        (tok->cur.data.str[1] == 0) &&
        ((tok->cur.data.str[0] == '+') || (tok->cur.data.str[0] == '-'))
    ) {
        U8 op[2] = {tok->cur.data.str[0], 0};
        tokenizer_next(tok);
        if(parse_c(es, tok)!=Ok)return Error;
        reg_exc_bin(es, op);
    }
    return Ok;
}

ERR parse_a(ExprState* es, Tokenizer* tok) {
    if(parse_b(es, tok)!=Ok)return Error;
    while(
        (tok->cur.type == KeywordToken) &&
        (tok->cur.data.str[1] == 0) &&
        (tok->cur.data.str[0] == '=')
    ) {
        U8 op[2] = {tok->cur.data.str[0], 0};
        tokenizer_next(tok);
        if(parse_b(es, tok)!=Ok)return Error;
        reg_exc_bin(es, op);
    }
    return Ok;
}

ERR keyword(Tokenizer* tok, U8* kw) {
    if((tok->cur.type != KeywordToken) || strcmp(tok->cur.data.str, kw)) {
        tkerr(tok, "Expected keyword %s", kw);
        return Error;
    }
    tokenizer_next(tok);
    return Ok;
}

ERR parse_block(FnState* fn, Tokenizer* tok) {
    if (tok->cur.type == KeywordToken) {
        if (!strcmp(tok->cur.data.str, "{")) {
            tokenizer_next(tok);
            while ((tok->cur.type != KeywordToken) || strcmp(tok->cur.data.str, "}")) {
                if(parse_block(fn, tok)!=Ok)return Error;
            }
            tokenizer_next(tok);
            return Ok;
        }
        if (!strcmp(tok->cur.data.str, "if")) {
            tokenizer_next(tok);
            if(keyword(tok, "("))return Error;
            ExprState es;
            es.fn = fn;
            es.next_reg = 0;
            if(parse_a(&es, tok)!=Ok)return Error;
            if(keyword(tok, ")"))return Error;
            U8 ifid = fn->ifcnt++;
            printf("  cmp %s 0\n"
                   "  je .ifnt%d\n", reg_rem(&es), ifid);
            if(parse_block(fn, tok)!=Ok)return Error;
            if((tok->cur.type == KeywordToken) && !strcmp(tok->cur.data.str, "else")) {
                tokenizer_next(tok);
                printf("  jmp .ifend%d", ifid);
                printf(".ifnt%d:\n", ifid);
                if(parse_block(fn, tok)!=Ok)return Error;
                printf(".ifend%d:\n", ifid);
            } else {
                printf(".ifnt%d:\n", ifid);
            }
            return Ok;
        }
        if (!strcmp(tok->cur.data.str, "while")) {
            tokenizer_next(tok);
            U8 wid = fn->whilecnt++;
            printf(".loop%d:\n", wid);
            if(keyword(tok, "("))return Error;
            ExprState es;
            es.fn = fn;
            es.next_reg = 0;
            if(parse_a(&es, tok)!=Ok)return Error;
            if(keyword(tok, ")"))return Error;
            U8 ifid = fn->ifcnt++;
            printf("  cmp %s 0\n"
                   "  je .lend%d\n", reg_rem(&es), ifid);
            if(parse_block(fn, tok)!=Ok)return Error;
            printf("  jmp .loop%d\n", ifid);
            printf(".lend%d:\n", ifid);
            return Ok;
        }
    }
    ExprState es;
    es.fn = fn;
    es.next_reg = 0;
    if(parse_a(&es, tok)!=Ok)return Error;
    if(keyword(tok, ";")!=Ok)return Error;
    return Ok;
}

ERR parse_fn(Tokenizer* tok) {
    // Very simple ;(
    if (keyword(tok, "U0")) return Error;

    if (tok->cur.type != NameToken) {
        error("Ty debil");
        return Error;
    }
    U8 fname[16];
    memcpy(fname, tok->cur.data.str, 16);
    printf("%s:\n", fname);
    tokenizer_next(tok);

    if (keyword(tok, "(")) return Error;

    if (keyword(tok, ")")) return Error;

    FnState fn = {0};
    if(parse_block(&fn, tok)!=Ok)return Error;

    printf("  pop %%ax push 0 push %%ax\n");
    printf("  ret\n");
    for(U8 i = 0; i < fn.strcnt; i++) {
        printf(".str%d: bytes ", i);
        govnasm_embed(fn.strs[i]);
        free(fn.strs[i]);
        printf("\n");
    }
    return Ok;
}

ERR parse_var(Tokenizer* tok) {
    // Stupid
    if (keyword(tok, "U8")) return Error;

    if (tok->cur.type != NameToken) {
        error("Ty debil");
        return Error;
    }
    U8 fname[16];
    memcpy(fname, tok->cur.data.str, 16);
    printf("%s: bytes", fname);
    tokenizer_next(tok);

    if (keyword(tok, "[")) return Error;
    if((tok->cur.type != NumberToken) || (tok->cur.data.number >= 256)) {
        error("Unsupported!!!!");
        return Error;
    }
    U8 len = tok->cur.data.number;
    tokenizer_next(tok);

    if (keyword(tok, "]")) return Error;
    if (keyword(tok, "=")) return Error;
    if (keyword(tok, "{")) return Error;

    while (1) {
        if((tok->cur.type != NumberToken) || (tok->cur.data.number >= 256)) {
            error("Unsupported!!!!");
            return Error;
        }
        printf(" $%02x", tok->cur.data.number);
        tokenizer_next(tok);
        if(tok->cur.type != KeywordToken) {
            error("Bro");
            return Error;
        }
        if(!strcmp(tok->cur.data.str, ",")) {
            tokenizer_next(tok);
            continue;
        }
        if(!strcmp(tok->cur.data.str, "}")) {
            tokenizer_next(tok);
            break;
        }
        error("Im tired of writing errors");
        return Error;
    }
    if (keyword(tok, ";")) return Error;

    printf("\n");
    return Ok;
}

ERR parse_decl(Tokenizer* tok) {
    if (tok->cur.type != KeywordToken) {
        error("Meh");
        return Error;
    }
    if (!strcmp(tok->cur.data.str, "U0")) {
        return parse_fn(tok);
    } else if (!strcmp(tok->cur.data.str, "U8")) {
        return parse_var(tok);
    } else {
        print_token(&(tok->cur));
        error("Meh.");
        return Error;
    }
}
