#pragma once

#include <string>
#include <functional>
#include "Types.h"
#include "Iop_SpuBase.h"

namespace Iop
{
	namespace Spu2
	{
		class CCore
		{
		public:
			enum
			{
				DEFAULT_BASE_SAMPLING_RATE = 48000,
			};

			CCore(unsigned int, CSpuBase&);
			virtual ~CCore() = default;

			CCore(const CCore&) = delete;
			CCore& operator=(const CCore&) = delete;

			void Reset();
			void SetBaseSamplingRate(uint32);

			CSpuBase& GetSpuBase() const;

			uint32 ReadRegister(uint32, uint32);
			uint32 WriteRegister(uint32, uint32);

			enum REGISTERS
			{
				VP_VOLL = 0x1F900000,
				VP_VOLR = 0x1F900002,
				VP_PITCH = 0x1F900004,
				VP_ADSR1 = 0x1F900006,
				VP_ADSR2 = 0x1F900008,
				VP_ENVX = 0x1F90000A,
				VP_VOLXL = 0x1F90000C,
				VP_VOLXR = 0x1F90000E,
				S_REG_BASE = 0x1F900180,
				S_PMON_HI = 0x1F900180,
				S_PMON_LO = 0x1F900182,
				S_NON_HI = 0x1F900184,
				S_NON_LO = 0x1F900186,
				S_VMIXL_HI = 0x1F900188,
				S_VMIXL_LO = 0x1F90018A,
				S_VMIXEL_HI = 0x1F90018C,
				S_VMIXEL_LO = 0x1F90018E,
				S_VMIXR_HI = 0x1F900190,
				S_VMIXR_LO = 0x1F900192,
				S_VMIXER_HI = 0x1F900194,
				S_VMIXER_LO = 0x1F900196,
				P_MMIX = 0x1F900198,
				CORE_ATTR = 0x1F90019A,
				A_IRQA_HI = 0x1F90019C,
				A_IRQA_LO = 0x1F90019E,
				A_KON_HI = 0x1F9001A0,
				A_KON_LO = 0x1F9001A2,
				A_KOFF_HI = 0x1F9001A4,
				A_KOFF_LO = 0x1F9001A6,
				A_TSA_HI = 0x1F9001A8,
				A_TSA_LO = 0x1F9001AA,
				A_STD = 0x1F9001AC,
				A_TS_MODE = 0x1F9001B0,
				VA_REG_BASE = 0x1F9001C0,
				VA_SSA_HI = 0x1F9001C0,
				VA_SSA_LO = 0x1F9001C2,
				VA_LSAX_HI = 0x1F9001C4,
				VA_LSAX_LO = 0x1F9001C6,
				VA_NAX_HI = 0x1F9001C8,
				VA_NAX_LO = 0x1F9001CA,
				R_REG_BASE = 0x1F9002E0,
				RVB_A_REG_BASE = 0x1F9002E4, //Reverb Base
				RVB_A_REG_END = 0x1F90033C,
				A_ESA_HI = 0x1F9002E0,
				A_ESA_LO = 0x1F9002E2,
				A_EEA_HI = 0x1F90033C,
				A_EEA_LO = 0x1F90033E,
				S_ENDX_HI = 0x1F900340,
				S_ENDX_LO = 0x1F900342,
				STATX = 0x1F900344,
				P_MVOLL = 0x1F900760, //Master volume
				P_MVOLR = 0x1F900762,
				P_EVOLL = 0x1F900764, //Effect volume
				P_EVOLR = 0x1F900766,
				P_AVOLL = 0x1F900768, //External Input Volume (Core 1)
				P_AVOLR = 0x1F90076A,
				P_BVOLL = 0x1F90076C, //Audio input volume
				P_BVOLR = 0x1F90076E,
				RVB_C_REG_BASE = 0x1F900774,
				RVB_C_REG_END = 0x1F900788,
			};

		private:
			typedef uint32 (CCore::*RegisterAccessFunction)(unsigned int, uint32, uint32);

			enum
			{
				MAX_CHANNEL = 24,
			};

			struct REGISTER_DISPATCH_INFO
			{
				RegisterAccessFunction core;
				RegisterAccessFunction channel;
			};

			uint32 ProcessRegisterAccess(const REGISTER_DISPATCH_INFO&, uint32, uint32);

			uint32 ReadRegisterCore(unsigned int, uint32, uint32);
			uint32 WriteRegisterCore(unsigned int, uint32, uint32);

			uint32 ReadRegisterChannel(unsigned int, uint32, uint32);
			uint32 WriteRegisterChannel(unsigned int, uint32, uint32);

			void LogRead(uint32, uint32);
			void LogWrite(uint32, uint32);
			void LogChannelRead(unsigned int, uint32, uint32);
			void LogChannelWrite(unsigned int, uint32, uint32);

			uint16 GetAddressLo(uint32);
			uint16 GetAddressHi(uint32);
			uint32 SetAddressLo(uint32, uint16);
			uint32 SetAddressHi(uint32, uint16);

			REGISTER_DISPATCH_INFO m_readDispatch;
			REGISTER_DISPATCH_INFO m_writeDispatch;
			unsigned int m_coreId;
			std::string m_logName;
			uint32 m_baseSamplingRate = 0;
			CSpuBase& m_spuBase;
		};
	};
};
