#pragma once
#ifndef _MACHINE_KI_H
#define _MACHINE_KI_H

#include "bigpcrt.h"

static const char *skpKIMachineName = "KI";

typedef void (*TKICallback)(const vmuptr_t userData);
//MIPS breakpoint handlers should return 1 to execute the underlying op, or 0 to skip it
typedef uint32_t (*TKIMIPSBreakpointCallback)(const uint32_t localAddr, const uint32_t opData);
//same return logic for ADSP handlers
typedef uint32_t (*TKIADSPBreakpointCallback)(const uint32_t opData);
typedef void (*TKIFinishScanOutCallback)(const uint32_t fbIndex, const vmuptr_t userData);

typedef enum
{
	kADSPReg_PC = 0,
	kADSPReg_CNTR,
	kADSPReg_PX,
	kADSPReg_ASTAT,
	kADSPReg_SSTAT,
	kADSPReg_MSTAT,
	kADSPReg_ICNTL,
	kADSPReg_IMASK,
	kADSPReg_IFC,
	kADSPReg_AX0,
	kADSPReg_AX1,
	kADSPReg_AY0,
	kADSPReg_AY1,
	kADSPReg_AR,
	kADSPReg_AF,
	kADSPReg_MX0,
	kADSPReg_MX1,
	kADSPReg_MY0,
	kADSPReg_MY1,
	kADSPReg_MR0,
	kADSPReg_MR1,
	kADSPReg_MR2,
	kADSPReg_MF,
	kADSPReg_SR0,
	kADSPReg_SR1,
	kADSPReg_SI,
	kADSPReg_SE,
	kADSPReg_SB,
	kADSPReg_PCSP,
	kADSPReg_LOOPSP,
	kADSPReg_CNTRSP,
	kADSPReg_STATSP,
	kADSPReg_Count
} EADSPReg;

typedef enum
{
	kADSPDAGReg_I = 0,
	kADSPDAGReg_L,
	kADSPDAGReg_M,
	kADSPDAGReg_Count
} EADSPDAGReg;

typedef enum
{
	kADSPStackType_PC = 0,
	kADSPStackType_LOOP,
	kADSPStackType_CNTR,
	kADSPStackType_STAT,
	kADSPStackType_Count,
} EADSPStackType;

#define BIGDCS_SPATIAL_MONOSURROUND (1 << 0)

typedef struct
{
	uint64_t (*GetAudioHash)();
	uint64_t (*GetHDDHash)();

	//this reset callback may also be called after state loads or other operations which flush breakpoint data.
	//this makes it more suitable than the normal software load callback for setting up initial breakpoint hooks.
	void (*RegisterOnReset)(const TKICallback pCallback, const vmuptr_t userData);
	//adsp boot callbacks will occur 1 emulated millisecond after boot time.
	//note that multiple adsp boot events may occur during startup, and adsp breakpoints are cleared with each boot.
	void (*RegisterOnADSPBoot)(const TKICallback pCallback, const vmuptr_t userData);

	void (*RegisterOnFinishScanOut)(const TKIFinishScanOutCallback pCallback, const vmuptr_t userData);
	
	//CAUTION: unless otherwise stated, Emu_* functions should only be called from the emulator thread
	
	uint32_t (*Emu_MIPSToLocalAddress)(const uint32_t addr);
	uint32_t (*Emu_MIPSToPhysicalAddress)(const uint32_t addr);
	
	//be mindful that MIPS breakpoint addresses are in local space; translate via Emu_MIPSToLocalAddress if necessary.
	//don't set breakpoints on delay slots; this may happen to work in some cases, but will usually break horribly.
	//only one breakpoint per address is supported.
	void (*Emu_MIPS_SetBreakpoint)(const uint32_t localAddr, const TKIMIPSBreakpointCallback pCallback);
	void (*Emu_MIPS_ClearBreakpoint)(const uint32_t localAddr);
	
	uint8_t (*Emu_ReadMemory8)(const uint32_t localAddr);
	uint16_t (*Emu_ReadMemory16)(const uint32_t localAddr);
	uint32_t (*Emu_ReadMemory32)(const uint32_t localAddr);
	uint64_t (*Emu_ReadMemory64)(const uint32_t localAddr);
	void (*Emu_ReadMemoryBuffer)(uint8_t *pDst, const uint32_t localAddr, const uint32_t dstSize);
	void (*Emu_WriteMemory8)(const uint32_t localAddr, const uint8_t val);
	void (*Emu_WriteMemory16)(const uint32_t localAddr, const uint16_t val);
	void (*Emu_WriteMemory32)(const uint32_t localAddr, const uint32_t val);
	void (*Emu_WriteMemory64)(const uint32_t localAddr, const uint64_t val);
	void (*Emu_WriteMemoryBuffer)(const uint8_t *pSrc, const uint32_t localAddr, const uint32_t srcSize);

	//Emu_MIPS_Read*/Write* are just provided for convenience and do nothing but call the corresponding function with
	//the Emu_MIPSToLocalAddress-mapped address
	uint8_t (*Emu_MIPS_ReadMemory8)(const uint32_t addr);
	uint16_t (*Emu_MIPS_ReadMemory16)(const uint32_t addr);
	uint32_t (*Emu_MIPS_ReadMemory32)(const uint32_t addr);
	uint64_t (*Emu_MIPS_ReadMemory64)(const uint32_t addr);
	void (*Emu_MIPS_ReadMemoryBuffer)(uint8_t *pDst, const uint32_t addr, const uint32_t dstSize);
	void (*Emu_MIPS_WriteMemory8)(const uint32_t addr, const uint8_t val);
	void (*Emu_MIPS_WriteMemory16)(const uint32_t addr, const uint16_t val);
	void (*Emu_MIPS_WriteMemory32)(const uint32_t addr, const uint32_t val);
	void (*Emu_MIPS_WriteMemory64)(const uint32_t addr, const uint64_t val);
	void (*Emu_MIPS_WriteMemoryBuffer)(const uint8_t *pSrc, const uint32_t addr, const uint32_t srcSize);
	
	uint64_t (*Emu_GetFrameCount)();
	
	uint32_t (*Emu_MIPS_GetPC)();
	void (*Emu_MIPS_SetPC)(const uint32_t val);
	void (*Emu_MIPS_SetBranch)(const uint32_t val);

	uint64_t (*Emu_MIPS_GetGPR)(const uint32_t regIndex);
	void (*Emu_MIPS_SetGPR)(const uint32_t regIndex, const uint64_t val);
	
	uint64_t (*Emu_MIPS_GetLo)();
	void (*Emu_MIPS_SetLo)(const uint64_t val);
	uint64_t (*Emu_MIPS_GetHi)();
	void (*Emu_MIPS_SetHi)(const uint64_t val);
	uint32_t (*Emu_MIPS_GetFPUCBits)();
	void (*Emu_MIPS_SetFPUCBits)(const uint32_t val);
	
	uint64_t (*Emu_MIPS_GetFPR)(const uint32_t regIndex);
	void (*Emu_MIPS_SetFPR)(const uint32_t regIndex, const uint64_t val);
	float (*Emu_MIPS_GetFPR_Float)(const uint32_t regIndex, const uint32_t hi);
	void (*Emu_MIPS_SetFPR_Float)(const uint32_t regIndex, const uint32_t hi, const float val);

	uint64_t (*Emu_MIPS_GetCOP0Reg)(const uint32_t regIndex);
	void (*Emu_MIPS_SetCOP0Reg)(const uint32_t regIndex, const uint64_t val);
	
	void (*Emu_MIPS_ConsumeCycles)(const int32_t cycles); //pass -1 to consume all cycles in timeslice
	void (*Emu_MIPS_HatlTimeSlice)();
	
	//if expectedOp is non-0, it must match the contents of program memory at the given address, or the breakpoint will not be applied.
	//returns > 0 on success, 0 on failure.
	uint32_t (*Emu_ADSP_SetBreakpoint)(const uint32_t progAddr, const uint32_t expectedOp, const TKIADSPBreakpointCallback pCallback);
	uint32_t (*Emu_ADSP_ClearBreakpoint)(const uint32_t progAddr);	
	
	uint32_t (*Emu_ADSP_GetReg)(const EADSPReg reg);
	void (*Emu_ADSP_SetReg)(const EADSPReg reg, const uint32_t val);
	uint32_t (*Emu_ADSP_GetRegAlt)(const EADSPReg reg);
	void (*Emu_ADSP_SetRegAlt)(const EADSPReg reg, const uint32_t val);
	uint32_t (*Emu_ADSP_GetDAGReg)(const uint32_t addrIndex, const EADSPDAGReg reg);
	void (*Emu_ADSP_SetDAGReg)(const uint32_t addrIndex, const EADSPDAGReg reg, const uint32_t val);
	
	uint32_t (*Emu_ADSP_ReadData)(const uint32_t addr);
	uint32_t (*Emu_ADSP_ReadProgram)(const uint32_t addr);
	void (*Emu_ADSP_WriteData)(const uint32_t addr, const uint32_t val);
	void (*Emu_ADSP_WriteProgram)(const uint32_t addr, const uint32_t val);
	//direct read/write functions provide direct access to underlying memory and don't map, handle with care
	uint32_t (*Emu_ADSP_DirectReadInt)(const uint32_t addr);
	uint32_t (*Emu_ADSP_DirectReadExt)(const uint32_t addr);
	uint32_t (*Emu_ADSP_DirectReadIntData)(const uint32_t addr);
	void (*Emu_ADSP_DirectWriteInt)(const uint32_t addr, const uint32_t val);
	void (*Emu_ADSP_DirectWriteExt)(const uint32_t addr, const uint32_t val);
	void (*Emu_ADSP_DirectWriteIntData)(const uint32_t addr, const uint32_t val);
	
	uint64_t (*Emu_ADSP_GetStackValue)(const EADSPStackType stackType, const uint32_t index);
	void (*Emu_ADSP_SetStackValue)(const EADSPStackType stackType, const uint32_t index, const uint64_t val);

	void (*Emu_ADSP_ConsumeCycles)(const int32_t cycles); //pass -1 to consume all cycles in timeslice
	void (*Emu_ADSP_HatlTimeSlice)();
	
	void (*Emu_DCS_CreateExtraChannels)(const uint32_t chanCount);
	uint32_t (*Emu_DCS_IsSoundPannable)(const uint32_t soundId); //returns non-0 for pannable sounds (determined by analyzing track data)
	uint32_t (*Emu_DCS_GetSoundChannel)(const uint32_t soundId);
	uint32_t (*Emu_DCS_SetSoundChannel)(const uint32_t soundId, const uint32_t channel);
	void (*Emu_DCS_SetSpatialListenerData)(const float *pPos, const float *pFwd, const float *pRight, const float *pUp, const uint32_t flags);
	//attnDist 0 means no attenuation, otherwise it specifies the distance at which the sound is fully attenuated. returns non-0 on success.
	uint32_t (*Emu_DCS_PlaySpatial)(const uint32_t soundId, const float *pSrcPos, const float attnDist);
	
	double (*Emu_KI_SubFrameUSec)();
	
	void (*Emu_KI_SetStereoDisplay)(const uint32_t en, const uint32_t flags);
	uint32_t (*Emu_KI_GetStereoEye)(); //0 = center, 1 = left, 2 = right
	uint32_t (*Emu_KI_GetStereoEyeFlag)();
	
	uint64_t (*Emu_KI_TextToTag)(const char *pString);
	
	uint32_t (*Emu_KI_LoadMachineInstanceData)(void *pData, const uint32_t dataSize, const uint64_t tag); //returns >0 if the data exists under the specified tag
	void (*Emu_KI_StoreMachineInstanceData)(const void *pData, const uint32_t dataSize, const uint64_t tag);
	
	void (*Emu_KI_TransformPoints)(float *pPointsOut, const uint32_t pointsOutStride, const int32_t *pWorldPoints, const int32_t *pCamData, const float vBias, const uint32_t pointCount);

	uint64_t (*Emu_KI_Buttons)(const uint32_t inputIndex);
	uint32_t (*Emu_KI_ExtraButtons)(const uint32_t inputIndex);
} SKIMachineAPI;

static SKIMachineAPI *spMachAPI = NULL;

#define KI_MACHINE_INIT() \
	spMachAPI = (SKIMachineAPI *)bigpemu_get_platform_api(skpKIMachineName); \
	if (!spMachAPI) \
	{ \
		printf("Skipping script load, machine API unavailable.\n"); \
		return; \
	}

#define KI_MACHINE_INIT_RVAL(modName, rVal) \
	spMachAPI = (SKIMachineAPI *)bigpemu_get_platform_api(skpKIMachineName); \
	if (!spMachAPI) \
	{ \
		printf("Skipping module '%s', machine API unavailable.\n", modName); \
		return rVal; \
	}

#define KI_MIPSREG_ZERO			0
#define KI_MIPSREG_AT			1
#define KI_MIPSREG_V0			2
#define KI_MIPSREG_V1			3
#define KI_MIPSREG_A0			4
#define KI_MIPSREG_A1			5
#define KI_MIPSREG_A2			6
#define KI_MIPSREG_A3			7
#define KI_MIPSREG_T0			8
#define KI_MIPSREG_T1			9
#define KI_MIPSREG_T2			10
#define KI_MIPSREG_T3			11
#define KI_MIPSREG_T4			12
#define KI_MIPSREG_T5			13
#define KI_MIPSREG_T6			14
#define KI_MIPSREG_T7			15
#define KI_MIPSREG_S0			16
#define KI_MIPSREG_S1			17
#define KI_MIPSREG_S2			18
#define KI_MIPSREG_S3			19
#define KI_MIPSREG_S4			20
#define KI_MIPSREG_S5			21
#define KI_MIPSREG_S6			22
#define KI_MIPSREG_S7			23
#define KI_MIPSREG_T8			24
#define KI_MIPSREG_T9			25
#define KI_MIPSREG_K0			26
#define KI_MIPSREG_K1			27
#define KI_MIPSREG_GP			28
#define KI_MIPSREG_SP			29
#define KI_MIPSREG_FP			30
#define KI_MIPSREG_RA			31
	
#endif //_MACHINE_KI_H

