#pragma once

#include "common/arch.h"
#include "lex.h"
#include "report.h"
#include <stdbool.h>
#include <stdint.h>

typedef struct PLabel PLabel;

struct PLabel {
    PLabel* next;
    char* ident;
    Loc loc;
    bool sub_label;
};

PLabel* plabel_new(PLabel* next, char* ident, bool sub_label, Loc loc);
void plabel_free(PLabel* label);

typedef enum {
    PoTy_Reg,
    PoTy_Imm,
    PoTy_Ident,
    PoTy_SubLabel,
    PoTy_Str,
    PoTy_Mem8,
    PoTy_Mem16,
    PoTy_Not,
    PoTy_Negate,
    PoTy_Or,
    PoTy_Xor,
    PoTy_And,
    PoTy_Shl,
    PoTy_Shr,
    PoTy_Add,
    PoTy_Sub,
    PoTy_Mul,
    PoTy_Div,
    PoTy_Mod,
} POperandTy;

typedef struct POperand POperand;

struct POperand {
    POperandTy ty;
    Loc loc;
    union {
        Reg reg;
        uint16_t imm;
        struct {
            char* str;
            size_t str_len;
        };
        POperand* operand;
        struct {
            POperand* left;
            POperand* right;
        };
    };
};

POperand* poperand_new_reg(Reg reg, Loc loc);
POperand* poperand_new_imm(uint16_t imm, Loc loc);
POperand* poperand_new_str(POperandTy ty, char* str, size_t str_len, Loc loc);
POperand* poperand_new_unary(POperandTy ty, POperand* inner, Loc loc);
POperand* poperand_new_binary(
    POperandTy ty, POperand* left, POperand* right, Loc loc);
void poperand_free(POperand* operand);

typedef struct {
    PLabel* labels;
    char* op;
    Loc loc;
    size_t ops_size;
    POperand* ops[];
} PLine;

PLine* pline_new(
    char* op, PLabel* labels, Loc loc, size_t ops_size, POperand** ops);
void pline_free(PLine* pline);

typedef enum {
    PStmtTy_Line,
    PStmtTy_Global,
    PStmtTy_Extern,
    PStmtTy_Const,
    PStmtTy_Include,
} PStmtTy;

typedef struct {
    PStmtTy ty;
    Loc loc;
    union {
        PLine* line;
        struct {
            char* ident;
            POperand* value;
        };
        char* str;
    };
} PStmt;

PStmt* pstmt_new_line(Loc loc, PLine* line);
PStmt* pstmt_new_ident(PStmtTy ty, Loc loc, char* ident);
PStmt* pstmt_new_const(PStmtTy ty, Loc loc, char* ident, POperand* value);
PStmt* pstmt_new_include(Loc loc, char* str);
void pstmt_free(PStmt* stmt);

typedef struct {
    Lexer lexer;
    Tok tok;
    Tok eaten;
    bool error_occured;
} Parser;

void parser_construct(Parser* parser, const char* filename, const char* text);
bool parser_done(const Parser* parser);
bool parser_error_occured(const Parser* parser);
PLine* parser_next_line(Parser* parser);
PStmt* parser_next_stmt(Parser* parser);