// px_ir.h

#pragma once
#include "px_internal.h"

enum PXInstructionType {
	PXIT_NOP = 0,
	PXIT_FATAL,

	PXIT_LABEL,
	PXIT_CALL,
	PXIT_LEAVE,
	PXIT_RET,
	
	PXIT_MOV,

	PXIT_PUSH,
	PXIT_POP,
	PXIT_POP_NULL,

	PXIT_JMP,

	PXIT_CMP_ZERO,
	PXIT_JE,  // same as JZ
	PXIT_JNE, // same as JNZ

	PXIT_EQUAL,       // These pseudo-opcodes pop 2 and (DONT) push 1. For the x86nasm
	PXIT_NOTEQUAL,    //  backend, they are often implemented as multiple ops
	PXIT_GREATERTHAN, 
	PXIT_LESSTHAN,

	PXIT_ADD_REG_IMM,
	PXIT_ADD_REG_REG,
	PXIT_SUB_REG_IMM,
	PXIT_SUB_REG_REG,
	PXIT_MUL_REG_IMM,
	PXIT_MUL_REG_REG,

	PXIT_CLEAR_REGISTER,

	PXIT_IMPORT_EXTERN,

	//PXIT_JG, // Unused - binary comparators push 0/1 to stack
	//PXIT_JGE,
	//PXIT_JL,
	//PXIT_JLE
};

struct PXInstruction {
	PXInstruction(enum PXInstructionType it)
		: it(it) {};
	PXInstruction(enum PXInstructionType it, std::string arg1)
		: it(it), arg1(arg1) {};
	PXInstruction(enum PXInstructionType it, int i1)
		: it(it), i1(i1) {};
	PXInstruction(enum PXInstructionType it, std::string arg1, std::string arg2)
		: it(it), arg1(arg1), arg2(arg2) {};
	PXInstruction(enum PXInstructionType it, std::string arg1, int i2)
		: it(it), arg1(arg1), i2(i2) {};
	
	enum PXInstructionType it;
	std::string arg1, arg2;
	int i1, i2;

	std::string comment;
};

struct PXProgram {
	
	std::string register_string_literal(std::string& sz);

	void optimise();

	std::string render_x86nasm();

	void op(PXInstruction pxi);

	bool should_optimise;

	/*
	void op(PXProgram& pxp);

	int uniqid();
	*/

	// Helper overloads

	void op(enum PXInstructionType it) {
		op(PXInstruction(it));
	}
	void op(enum PXInstructionType it, std::string a1) {
		op(PXInstruction(it, a1));
	}
	void op(enum PXInstructionType it, int i1) {
		op(PXInstruction(it, i1));
	}
	void op(enum PXInstructionType it, std::string a1, std::string a2) {
		op(PXInstruction(it, a1, a2));
	}
	void op(enum PXInstructionType it, std::string a1, int i2) {
		op(PXInstruction(it, a1, i2));
	}

private:
	std::vector<PXInstruction> ops;
	std::vector<std::string> stringTable;
	std::string commentBuffer;
};

// Breaking encapsulation
std::string x86nasm_stack_address(int stack_address);