// Header file for native generator interface

#pragma once
#include "blockmanager.h"
#include "oslib/host_context.h"
#include "../sh4_interpreter.h"

// When NO_RWX is enabled there's two address-spaces, one executable and
// one writtable. The emitter and most of the code in rec-* will work with
// the RW pointer. However the fpcb table and other pointers during execution
// (ie. exceptions) are RX pointers. These two macros convert between them by
// sub/add the pointer offset. CodeCache will point to the RW pointer for simplicity.
#ifdef FEAT_NO_RWX_PAGES
	extern ptrdiff_t cc_rx_offset;
	#define CC_RW2RX(ptr) (void*)(((uintptr_t)(ptr)) + cc_rx_offset)
	#define CC_RX2RW(ptr) (void*)(((uintptr_t)(ptr)) - cc_rx_offset)
#else
	#define CC_RW2RX(ptr) (ptr)
	#define CC_RX2RW(ptr) (ptr)
#endif

//Called from ngen_FailedToFindBlock
DynarecCodeEntryPtr DYNACALL rdv_FailedToFindBlock(u32 pc);
DynarecCodeEntryPtr DYNACALL rdv_FailedToFindBlock_pc();
//Called when a block check failed, and the block needs to be invalidated
DynarecCodeEntryPtr DYNACALL rdv_BlockCheckFail(u32 addr);
// Registers a custom FailedToFindBlock handler function
void rdv_SetFailedToFindBlockHandler(void (*handler)());

//code -> pointer to code of block, dpc -> if dynamic block, pc. if cond, 0 for next, 1 for branch
void* DYNACALL rdv_LinkBlock(u8* code,u32 dpc);

//Value to be returned when the block manager failed to find a block,
//should call rdv_FailedToFindBlock and then jump to the return value
// Set with rdv_SetFailedToFindBlockHandler(). Internal use only.
extern void (*ngen_FailedToFindBlock)();

//Canonical callback interface
enum CanonicalParamType
{
	CPT_u32,	// u32 param
	CPT_u32rv,	// u32 return value
	CPT_u64rvL,	// u64 return value lsb
	CPT_u64rvH,	// u64 return value msb
	CPT_f32,	// f32 param
	CPT_f32rv,	// f32 return value
	CPT_ptr,	// register pointer
	CPT_sh4ctx,	// Sh4Context pointer
};

bool rdv_readMemImmediate(u32 addr, int size, void*& ptr, bool& isRam, u32& physAddr, RuntimeBlockInfo* block = nullptr);
bool rdv_writeMemImmediate(u32 addr, int size, void*& ptr, bool& isRam, u32& physAddr, RuntimeBlockInfo* block = nullptr);

class Sh4CodeBuffer
{
public:
	// Return the current position of the code buffer, where new code should be emitted.
	void *get();
	// Advance the buffer position by 'size' bytes.
	void advance(u32 size);
	// Return the available free space in bytes.
	u32 getFreeSpace();
	// Return a pointer to the beginning of the code buffer.
	void *getBase();
	// Return the full size of the code buffer.
	// Note that the code buffer may be partitioned (long term blocks, short term blocks) so the full size
	// might be greater than what getFreeSpace() returns when the buffer is empty.
	u32 getSize();

	// Select the long term or temporary buffer (internal use)
	void useTempBuffer(bool enable) { tempBuffer = enable; }
	// Reset main or temp code buffer position to 0 (internal use)
	void reset(bool temporary);

private:
	u32 lastAddr = 0;
	u32 tempLastAddr = 0;
	bool tempBuffer = false;
};

class Sh4Dynarec
{
public:
	// Initialize the dynarec, which should keep a reference to the passed code buffer to generate code later.
	virtual void init(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) = 0;
	// Compile the given block.
	// If smc_checks is true, add self-modifying code detection.
	// If optimize is true, use fast memory accesses if possible, that will be rewritten if they fail.
	virtual void compile(RuntimeBlockInfo* block, bool smc_checks, bool optimise) = 0;
	// Signal the dynarec that the code buffer has been cleared. It should re-generate its main loop if needed.
	virtual void reset() {}
	// Run the code.
	// cntx points right after the Sh4RCB struct, which corresponds to the start of the 512 MB virtual address space
	// (if available).
	virtual void mainloop(void* cntx) = 0;
	// An SH4 exception has occurred. The dynarec should abort execution of the current block and longjmp to its main loop
	// to execute the exception handler.
	virtual void handleException(host_context_t& context) = 0;
	// Rewrite the memory access at host PC address 'faultAddress'. This fast memory access failed and should be rewritten
	// to use mem access handlers.
	virtual bool rewrite(host_context_t& context, void *faultAddress) = 0;
	// Allocate a new block information structure.
	virtual RuntimeBlockInfo *allocateBlock() {
		return new RuntimeBlockInfo();
	}

	// Dynarec canonical implementation callback methods.
	// Used to call default implementation of shil ops that the dynarec doesn't implement.
	// Start call
	virtual void canonStart(const shil_opcode *op) = 0;
	// Pass input parameter or retrieve return parameter
	virtual void canonParam(const shil_opcode *op, const shil_param *param, CanonicalParamType paramType) = 0;
	// Call the implementation
	virtual void canonCall(const shil_opcode *op, void *function) = 0;
	// Finish call
	virtual void canonFinish(const shil_opcode *op) = 0;

	virtual ~Sh4Dynarec() = default;
};

extern Sh4Dynarec *sh4Dynarec;

class Sh4Recompiler : public Sh4Interpreter
{
	using super = Sh4Interpreter;

public:
	Sh4Recompiler() {
		Instance = this;
	}
	~Sh4Recompiler() {
		Instance = nullptr;
	}
	void Run() override;
	void ResetCache() override;
	void Reset(bool hard) override;
	void Init() override;
	void Term() override;

	void clear_temp_cache(bool full);

	static Sh4Recompiler *Instance;
};
