#include "udpl_dll.h"
#include "udplscci.h"
#include "chipname.h"

//
// override
//
BOOL __stdcall SoundChip_udpl::init()
{
	bool ret = pchip->setClock(info.dClock);
	ret &= pchip->init();	// HW reset してレジスタ初期化
	return ret ? TRUE : FALSE;
}

BOOL __stdcall SoundChip_udpl::setRegister(DWORD dAddr, DWORD dData)
{
	bool ret = pchip->setRegister(dAddr, dData);
	return ret ? TRUE : FALSE;
}

DWORD __stdcall SoundChip_udpl::getRegister(DWORD dAddr)
{
	return pchip->getRegister(dAddr);
}

SCCI_SOUND_CHIP_INFO* __stdcall SoundChip_udpl::getSoundChipInfo()
{
	return &info;
}

int __stdcall SoundChip_udpl::getSoundChipType()
{
	return info.iSoundChip;
}

DWORD __stdcall SoundChip_udpl::getSoundChipClock()
{
	return info.dClock;
}

DWORD __stdcall SoundChip_udpl::getWrittenRegisterData(DWORD addr)
{
	return pchip->getRegister(addr);
}

BOOL __stdcall SoundChip_udpl::isBufferEmpty()
{
	return TRUE;
}


SoundChip_udpl::SoundChip_udpl(UdplChip *pc, UDPL_CHIP_INFO *pi, uint32_t no)
{
	pchip = pc;
	udplinfo = pi;	// 一応持っておく

	// info を作る
	memset(&info, 0, sizeof(info));
	info.bIsUsed = FALSE;
	strcpy_s(info.cSoundChipName, sizeof(info.cSoundChipName), pi->name);
	info.dBusID = no;
	info.dClock = pi->clock;
	if (strcmp(pi->name, CHIP_YM2608) == 0) {		// YM2608
		info.iSoundChip = SC_TYPE_YM2608;
		info.dSoundLocation = SC_LOCATION_STEREO;
	}
	else if (strcmp(pi->name, CHIP_YM2151) == 0) {	// YM2151
		info.iSoundChip = SC_TYPE_YM2151;
		info.dSoundLocation = SC_LOCATION_STEREO;
	}
	else if (strcmp(pi->name, CHIP_YM2612) == 0 || strcmp(pi->name, CHIP_YM3438) == 0) {	// YM2612, 3438
		info.iSoundChip = SC_TYPE_YM2612;
		info.dSoundLocation = SC_LOCATION_STEREO;
	}
	else if (strcmp(pi->name, CHIP_YM2203) == 0) {	// YM2203
		info.iSoundChip = SC_TYPE_YM2203;
	}
	else if (strcmp(pi->name, CHIP_YM3812) == 0) {	// YM3812
		info.iSoundChip = SC_TYPE_YM3812;
	}
	else if (strcmp(pi->name, CHIP_YM3526) == 0) {	// YM3526
		info.iSoundChip = SC_TYPE_YM3526;
	}
	else if (strcmp(pi->name, CHIP_Y8950) == 0) {	// Y8950
		info.iSoundChip = SC_TYPE_Y8950;
	}
	else if (strcmp(pi->name, CHIP_SN76489) == 0) {	// SN76489
		info.iSoundChip = SC_TYPE_SN76489;
	}
	else if (strcmp(pi->name, CHIP_SCC) == 0) {		// SCC
		info.iSoundChip = SC_TYPE_SCC;
	}
	else if (strcmp(pi->name, CHIP_SCCP) == 0) {	// SCC+
		info.iSoundChip = SC_TYPE_SCCS;
	}
	else if (strcmp(pi->name, CHIP_YM2610_H) == 0 || strcmp(pi->name, CHIP_YM2610B_H) == 0) {	// YM2610 + FT232H
		info.iSoundChip = SC_TYPE_YM2610;
		info.dSoundLocation = SC_LOCATION_STEREO;
	}
	else if (strcmp(pi->name, CHIP_AY8910) == 0 ) {	// AY8910
		info.iSoundChip = SC_TYPE_AY8910;
	}
	else if (strcmp(pi->name, CHIP_YM2149) == 0) {	// YM2149
		info.iSoundChip = SC_TYPE_YM2149;
	}
	else if (strcmp(pi->name, CHIP_YM2413) == 0) {	// YM2413
		info.iSoundChip = SC_TYPE_YM2413;
	}
	else if (strcmp(pi->name, CHIP_YMF262) == 0) {	// YMF262
		info.iSoundChip = SC_TYPE_YMF262;
		info.dSoundLocation = SC_LOCATION_STEREO;
	}
	else {
		info.iSoundChip = SC_TYPE_UNKNOWN;
	}

	isUsed = false;
}

SoundChip_udpl::SoundChip_udpl(SoundChip_udpl *pc, uint32_t clock, uint32_t no)
{
	pchip = pc->pchip;
	udplinfo = pc->udplinfo;	// 一応持っておく

	memcpy(&info, &pc->info, sizeof(info));
	info.dBusID = no;
	info.dClock = clock;

	isUsed = false;
}

void SoundChip_udpl::setSameList(const std::vector<SoundChip_udpl*> &in)
{
	samelist = in;
}

void SoundChip_udpl::setUsed(bool val, bool recursive)
{
	if (recursive) {
		for (auto el : samelist) {
			el->setUsed(val);
		}
	}
	else {
		isUsed = val;
	}
}

bool SoundChip_udpl::getUsed(void)
{
	return isUsed;
}

bool SoundChip_udpl::getVariableClock(void)
{
	return udplinfo->variable_clock;
}

//
// DCSG
//
BOOL __stdcall SoundChip_udpl_sn76489::setRegister(DWORD dAddr, DWORD dData)
{
	bool ret = pchip->writeBus(0x00, (uint8_t)dData);
	return ret ? TRUE : FALSE;
}

//
// YM2610 + FT232H
//
BOOL __stdcall SoundChip_udpl_ym2610_h::setRegister(DWORD dAddr, DWORD dData)
{
	bool ret = true;
	if (!filling) {
		if ((dAddr & 0x0600) == 0) {	// 0110 0000 ... 0x0200 未満かつバッファリング中でない
			ret = pchip->setRegister(dAddr, dData);
		}
		else {
			switch (dAddr) {
			case 0x0200:
				offset = (offset & 0xFFFF00) | (dData & 0xFF);
				break;

			case 0x0201:
				offset = (offset & 0xFF00FF) | ((dData & 0xFF) << 8);
				break;

			case 0x0202:
				offset = (offset & 0x00FFFF) | ((dData & 0xFF) << 16);
				break;

			case 0x0203:	// adpcm ch 指定
				if (dData < 2) {
					cur_ch = dData;	// バッファを指定
					pcmbuf_len[cur_ch] = offset;
					filling = true;
				}
				break;
			default:
				break;
			}
		}
	}
	else {	// バッファリング中 filling == true
		switch (dAddr) {
		case 0x0200:
			offset = (offset & 0xFFFF00) | (dData & 0xFF);
			break;

		case 0x0201:
			offset = (offset & 0xFF00FF) | ((dData & 0xFF) << 8);
			break;

		case 0x0202:
			offset = (offset & 0x00FFFF) | ((dData & 0xFF) << 16);
			break;

		case 0x0204:	// adpcm data
			pcmbuf[cur_ch][pcmbuf_len[cur_ch]++] = (uint8_t)dData;
			break;

		case 0x0203:	// adpcm ch 指定
			if (dData == cur_ch) {				// 同じチャンネルが指定された
				pcmbuf_len[cur_ch] = offset;	// オフセットだけ更新
			} else {
				// cur_ch の溜め込みが終わった
				pcminfo.ch = cur_ch;
				pcminfo.pdata = pcmbuf[cur_ch];
				pcminfo.size = pcmbuf_len[cur_ch];
				pcminfo.offset = 0;
				pchip->setPcmData(&pcminfo);	// PCM data セット
				if (dData < 2) {
					cur_ch = dData;	// バッファを変えてバッファリング続行
					pcmbuf_len[cur_ch] = offset;
				}
			}
			break;

		default:
			// cur_ch の溜め込みが終わった
			pcminfo.ch = cur_ch;
			pcminfo.pdata = pcmbuf[cur_ch];
			pcminfo.size = pcmbuf_len[cur_ch];
			pcminfo.offset = 0;
			pchip->setPcmData(&pcminfo);	// PCM data セット

			filling = false;
			ret = pchip->setRegister(dAddr, dData);
			break;
		}
	}

	return ret ? TRUE : FALSE;
}

void SoundChip_udpl_ym2610_h::allocPcmBuf(void)
{
	for (unsigned int i = 0; i < 2; ++i) {
		pcmbuf[i] = new uint8_t[ADPCM_SIZE];
		pcmbuf_len[i] = 0;
	}

	cur_ch = ADPCM_A_CH;
	filling = false;
	memset(&pcminfo, 0, sizeof(pcminfo));
	offset = 0;
}
void SoundChip_udpl_ym2610_h::freePcmBuf(void)
{
	for (unsigned int i = 0; i < 2; ++i) {
		if (pcmbuf[i]) {
			delete[] pcmbuf[i];
			pcmbuf[i] = nullptr;
		}
	}
}