#pragma once
#include "types.h"
#include <deque>

namespace aica
{

#define SCIEB_addr 0x289C
#define SCIPD_addr (0x289C+4)
#define SCIRE_addr (0x289C+8)

#define MCIEB_addr 0x28B4
#define MCIPD_addr (0x28B4+4)
#define MCIRE_addr (0x28B4+8)

#define TIMER_A 0x2890 
#define TIMER_B (0x2890+4) 
#define TIMER_C (0x2890+8)
#define REG_L (0x2D00)
#define REG_M (0x2D04)

#define SCILV0_addr 0x28A8
#define SCILV1_addr 0x28AC
#define SCILV2_addr 0x28B0

struct CommonData_struct
{
	//+0
	u32 MVOL:4;
	u32 VER:4;
	u32 DAC18B:1;
	u32 MEM8MB:1;
	u32 :5;
	u32 Mono:1;
	
	u32 :16;
	//+4
	u32 RBP:12;
	u32 :1;
	u32 RBL:2;
	u32 TESTB0:1;

	u32 :16;
	//+8
	u32 MIBUF:8;
	u32 MIEMP:1;
	u32 MIFUL :1;
	u32 MIOVF :1;
	u32 MOEMP :1;
	u32 MOFUL :1;
	u32 :3;

	u32 :16;
	//+C
	u32 MOBUF:8;
	u32 MSLC:6;
	u32 AFSEL:1;
	u32 :1;

	u32 :16;
	//+10
	u32 EG:13;
	u32 SGC:2;
	u32 LP:1;
	
	u32 :16;
	//+14
	u32 CA:16;

	u32 :16;
	
	//quite a bit padding here :)
	u8 pad_med_0[0x6C-4];

	//+80
	u32 MRWINH:4;
	u32 $T:1;
	u32 $TSCD:3;
	u32 :1;
	u32 DMEA_hi:7;

	u32 :16;
	//+84
	u32 :2;
	u32 DMEA_lo:14;

	u32 :16;
	//+88
	u32 :2;
	u32 DRGA:13;
	u32 DGATE:1;

	u32 :16;
	//+8C
	u32 DEXE:1;
	u32 :1;
	u32 DLG:13;
	u32 DDIR:1;

	u32 :16;
	//+90
	u32 TIMA:8;
	u32 TACTL:3;
	u32 :5;

	u32 :16;
	//+94
	u32 TIMB:8;
	u32 TBCTL:3;
	u32 :5;

	u32 :16;
	//+98
	u32 TIMC:8;
	u32 TCCTL:3;
	u32 :5;

	u32 :16;

	//+9C
	u32 SCIEB:11;
	u32 :5;

	u32 :16;

	//+A0
	u32 SCIPD:11;
	u32 :5;

	u32 :16;

	//+A4
	u32 SCIRE:11;
	u32 :5;

	u32 :16;

	//+A8
	u32 SCILV0:8;
	u32 :8;

	u32 :16;

	//+AC
	u32 SCILV1:8;
	u32 :8;

	u32 :16;

	//+B0
	u32 SCILV2:8;
	u32 :8;

	u32 :16;

	//+B4
	u32 MCIEB:11;
	u32 :5;

	u32 :16;

	//+B8
	u32 MCIPD:11;
	u32 :5;

	u32 :16;

	//+BC
	u32 MCIRE:11;
	u32 :5;

	u32 :16;
	
	//some other misc shit FAR away is here :p
	u8 pad_lot_0[0x344-4];

	//+400 , hopefully :p
	u32 AR:1;
	u32 :7;
	u32 VREG:2;
	u32 :6;

	u32 :16;

	//Even more
	u8 pad_lot_1[0x100-4];

	//+500 , hopefully :p
	u32 L0_r:1;
	u32 L1_r:1;
	u32 L2_r:1;
	u32 L3_r:1;
	u32 L4_r:1;
	u32 L5_r:1;
	u32 L6_r:1;
	u32 L7_r:1;
	
	u32 :8;

	u32 :16;

	//+504

	u32 M0_r:1;
	u32 M1_r:1;
	u32 M2_r:1;
	u32 M3_r:1;
	u32 M4_r:1;
	u32 M5_r:1;
	u32 M6_r:1;
	u32 M7_r:1;
	u32 RP:1;
	
	u32 :7;

	u32 :16;
};
static_assert(sizeof(CommonData_struct) == 0x508, "Wrong CommonData size");

struct DSPData_struct
{
	//+0x000
	u32 COEF[128];		//15:3

	//+0x200
	u32 MADRS[64];		//15:0
	
	//+0x300
	u8 _PAD0[0x100];

	//+0x400
	u32 MPRO[128*4];	//15:0
	
	//+0xC00
	u8 _PAD1[0x400];

	//+0x1000
	struct 
	{ 
		u32 l;			//7:0
		u32 h;			//15:0 (23:8)
	} 
	TEMP[128];

	//+0x1400
	struct 
	{ 
		u32 l;			//7:0
		u32 h;			//15:0 (23:8)
	} 
	MEMS[32];
	
	//+0x1500
	struct 
	{ 
		u32 l;			//3:0
		u32 h;			//15:0 (19:4)
	} 
	MIXS[16];

	//+0x1580
	u32 EFREG[16];		//15:0
	
	//+0x15C0
	u32 EXTS[2];		//15:0
};
static_assert(sizeof(DSPData_struct) == 0x15C8, "Wrong DSPData size");

union InterruptInfo
{
	struct
	{
		//Bit 0 (R): Requests interrupt to external interrupt input pin "INTON". (SCSI) 
		u32 INTON:1;
		//Bit 1 (R): Reserved. 
		u32 :1;
		//Bit 2 (R): Reserved. 
		u32 :1;
		//Bit 3 (R): MIDI input interrupt. 
		//(Interrupt request generated when input FIFO has fetched valid data. Hence, if the CPU reads FIFO data, it must read the lot once and leave the FIFO empty. When the FIFO has changed to empty status, the interrupt request is canceled automatically.) 
		u32 MIDI_IN:1;
		//Bit 4 (R): DMA end interrupt 
		u32 DMA_END:1;
		//Bit 5 (R/W): SCPU interrupt caused by data being written to the CPU, so only "1" can be written. (Writing "0" has no effect.) This flag can be set from either the MCPU or the SCPU. 
		u32 SCPU:1;
		//Bit 6 (R): Timer A interrupt 
		u32 TimerA:1;
		//Bit 7 (R): Timer B interrupt 
		u32 TimerB:1;
		//Bit 8 (R): Timer C interrupt 
		u32 TimerC:1;
		//Bit 9 (R): MIDI output interrupt. 
		//(If the output FIFO changes to empty status, an interrupt request is generated.)
		//(If the status is no longer empty because data is written to the output FIFO, the interrupt request is canceled automatically.) 
		u32 MIDI_OUT:1;
		//Bit 10 (R): Interrupt of one sample interval 
		u32 SAMPLE_DONE:1;
	};
	u32 full;
};

struct DSP_OUT_VOL_REG
{
	//--	EFSDL[3:0]	--	EFPAN[4:0]

	u32 EFPAN:5;
	u32 :3;

	u32 EFSDL:4;
	u32 :4;

	u32 :16;
};

extern InterruptInfo * const MCIEB;
extern InterruptInfo * const MCIPD;
extern InterruptInfo * const MCIRE;
extern InterruptInfo * const SCIEB;
extern InterruptInfo * const SCIPD;
extern InterruptInfo * const SCIRE;
extern std::deque<u8> midiSendBuffer;

extern CommonData_struct * const CommonData;
extern DSPData_struct * const DSPData;
extern const DSP_OUT_VOL_REG * const dsp_out_vol;

template<typename T>
void writeTimerAndIntReg(u32 reg, T data);

class AicaTimer
{
	struct AicaTimerData
	{
		union
		{
			struct
			{
				u32 count:8;
				u32 md:3;
				u32 :21;
			};
			u32 data;
		};
	};
	AicaTimerData* data;
	u32 id;

public:
	s32 c_step;
	u32 m_step;

	void Init(u8* regbase,u32 timer)
	{
		data=(AicaTimerData*)&regbase[0x2890 + timer*4];
		id=timer;
		m_step=1<<(data->md);
		c_step=m_step;
	}

	void StepTimer(u32 samples)
	{
		do
		{
			c_step--;
			if (c_step==0)
			{
				c_step=m_step;
				data->count++;
				if (data->count==0)
				{
					if (id==0)
					{
						SCIPD->TimerA=1;
						MCIPD->TimerA=1;
					}
					else if (id==1)
					{
						SCIPD->TimerB=1;
						MCIPD->TimerB=1;
					}
					else
					{
						SCIPD->TimerC=1;
						MCIPD->TimerC=1;
					}
				}
			}
		} while(--samples);
	}

	void RegisterWrite()
	{
		u32 n_step=1<<(data->md);
		if (n_step!=m_step)
		{
			m_step=n_step;
			c_step=m_step;
		}
	}
};

extern AicaTimer timers[3];

} // namespace aica
