#pragma once
#include "common.hpp"
#include <vector>


typedef enum {
    REG_A, REG_B, REG_C, REG_D, //used by idiv so will be our tmp-register
    REG_SI, REG_DI, REG_BP, REG_SP, //won't use SP as well
    REG_8, REG_9, REG_10, REG_11, REG_12, REG_13, REG_14, REG_15,
    REG_NONE=REG_15+1
} reg_id_t;


class Reg {
    public:
        typedef struct {
            reg_id_t reg_id;
            const char* names[4]; // 8, 16, 32, 64
            bool needs_push;
            bool auto_assign; // must be explicitly requested
        } reg_info_t;

        typedef struct {
            unsigned var_id; // whether its used
            bool is_ptr;
            bool pushed;
            width_t width; // actual declared size
            width_t init_width; // zero-initialized size (>= width, as for this the user should be responsible)
        } reg_val_t;

    private:
        const reg_info_t reg_info;
        reg_val_t val;

    public:
        Reg(const reg_info_t);

        const char* get_str(width_t) const;
        const char* get_str() const;
        static const char* get_c_str(width_t, bool);

        bool used() const;
        const reg_val_t& get() const;
        const reg_info_t& info() const;

        void set();
        void set(unsigned, width_t, bool, const char*&);
        void set(width_t);
};


class Regs {
    private:
        Reg regs[REG_NONE];
        std::vector<Reg*> bufs;

    public:
        Regs();
        ~Regs();

        bool push(unsigned, width_t, bool);
        bool push(unsigned, width_t, bool, const char*&);
        bool push(unsigned, width_t, reg_id_t, bool); // for rv and args
        bool push(unsigned, width_t, reg_id_t, bool, const char*&);
        bool push_lbl(unsigned, width_t, bool, const char*);

        void pop(unsigned);

        const Reg& get(reg_id_t) const;
        const Reg& get(unsigned) const;

        void inc_init(unsigned, width_t);
};