#ifndef system_h
#define system_h

#pragma once

class _System {

public:
	enum Region { USA = 0, JAPAN = 1, EUROPE = 2 };
	enum Unit { SMS1 = 0, SMS2 = 1 };

	Bus bus;
	Cpu cpu;
	Vdp vdp;
	SN76489 sn76489;
	DVideo dvideo;
	DSound dsound;
	DInput dinput;
	Inputmanager inputmanager;
	Color color;

	void render();

	void setHwnd(HWND _hwnd) { hwnd = _hwnd; }
	HWND getHwnd() { return hwnd; }

private:
	Region region;
	Unit unit;
	HWND hwnd;
	u8* _sram;

public:
	bool _region() { return region; }
	bool _isNTSC() { return region == Region::USA || region == Region::JAPAN; }
	bool _Unit() { return unit; }
	void setUnit(Unit _unit) { unit = _unit; }
	void setRegion(Region _region) { region = _region; }

	cothread_t thread_cpu;		//CPU
	cothread_t thread_vdp;		//VDP
	cothread_t thread_apu;		//APU
	cothread_t thread_app;	
	cothread_t thread_active;	//active thread

	struct {
		i64 cpuvdp;
		i64 cpuapu;
	} clock;

	enum sync_type { SyncNormal, SyncCpu, SyncAll } sync;

	//CPU <> APU
	void sync_cpuapu() {
		if(clock.cpuapu < 0) {
			thread_active = thread_apu;
			co_switch(thread_apu);
		}
	}

	void sync_apucpu() {
		if(clock.cpuapu >= 0) {
			if (sync == SyncAll) {
				sync = SyncNormal; //savestate critical
				//exit();
			}
			thread_active = thread_cpu;
			co_switch(thread_cpu);
		}
	}

	//CPU <> VDP
	void sync_cpuvdp() {
		if(clock.cpuvdp < 0) {
			thread_active = thread_vdp;
			co_switch(thread_vdp);
		}
	}

	void sync_vdpcpu() {
		if(clock.cpuvdp >= 0) {
			if (sync == SyncAll) {
				sync = SyncNormal; //savestate critical
				//exit();
			}
			thread_active = thread_cpu;
			co_switch(thread_cpu);
		}
	}

	//add clocks
	void addclocks_cpu(unsigned clocks) {
		clock.cpuvdp -= clocks * 3;
		clock.cpuapu -= clocks * 3;
	}

	void addclocks_apu(unsigned clocks) {
		clock.cpuapu += clocks * 3;
	}
	
	void addclocks_vdp(unsigned clocks) {
		clock.cpuvdp += clocks * 2;
	}
	
	void enter();
	void exit();
	void init();

	_System()
	:
	cpu(this, bus, vdp, sn76489, inputmanager),
	bus(this, cpu, vdp),
	vdp(this, bus, cpu),
	dvideo(this),
	dsound(this),
	dinput(this),
	sn76489(this, dsound),
	inputmanager(dinput),
	color()
	{
		thread_app  = 0;
		thread_cpu  = 0;	  
		thread_vdp  = 0;
		thread_apu  = 0;
		thread_active = 0;
		region = Region::USA;
		unit = Unit::SMS2;
	}
};

#endif
