#include <io.h>	// access()

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

extern std::tstring strDllPath;	// dll のパス

void SoundInterfaceManager_udpl::makeAllList(const std::vector<INI_ELEM> &list)
{
	// chip 情報構築
	chip_num = pudplManager->getChipCount();
	if (chip_num) {
		pchipInfo = new UDPL_CHIP_INFO[chip_num];
		pudplManager->getChipInfoList(pchipInfo, chip_num);
	}

	// board 情報構築
	memset(&ifInfo, 0, sizeof(ifInfo));
	strcpy_s(ifInfo.cInterfaceName, sizeof(ifInfo.cInterfaceName), "Virtual Udpl Interface");

	// udpl chip オブジェクト化
	UdplChip *pchip;
	UDPL_CHIP_INFO *pinfo;
	SoundChip_udpl *schip;
	
	std::vector<SoundChip_udpl *> realChipList;

	for (unsigned int i = 0; i < chip_num; ++i) {
		
		pchip = pudplManager->getChip(i, pchipInfo[i].clock);
		if (pchip != nullptr) {

			pinfo = pchipInfo + i;

			if (strcmp(pinfo->name, CHIP_SN76489) == 0) {
				schip = new SoundChip_udpl_sn76489(pchip, pinfo, i);
			}
			else if (strcmp(pinfo->name, CHIP_YM2610_H) == 0 || strcmp(pinfo->name, CHIP_YM2610B_H) == 0) {
				schip = new SoundChip_udpl_ym2610_h(pchip, pinfo, i);
			}
			else {
				schip = new SoundChip_udpl(pchip, pinfo, i);
			}
			realChipList.push_back(schip);
		}
	}

	scciChipList = realChipList;

	// 仮想チップを作成する
	unsigned int busid = chip_num;	// 仮想チップのスロットID
	std::vector<uint32_t> clocklist;
	std::vector<SoundChip_udpl*> same;

	for (auto el : realChipList) {
		
		same.clear();
		same.push_back(el);

		if (el->getVariableClock()) {	// 可変クロック対応なら
		
			clocklist.clear();

			// 作成する仮想サウンドチップのクロック情報を取り出す
			switch (el->getSoundChipType()) {
			case SC_TYPE_YM2608:
				get_clocks(CHIP_YM2608, list, clocklist);
				break;

			case SC_TYPE_YM2151:
				get_clocks(CHIP_YM2151, list, clocklist);
				break;

			case SC_TYPE_YM2612:
				get_clocks(CHIP_YM2612, list, clocklist);
				break;

			case SC_TYPE_YM2203:
				get_clocks(CHIP_YM2203, list, clocklist);
				break;

			case SC_TYPE_YM3812:
				get_clocks(CHIP_YM3812, list, clocklist);
				break;

			case SC_TYPE_YM3526:
				get_clocks(CHIP_YM3526, list, clocklist);
				break;

			case SC_TYPE_Y8950:
				get_clocks(CHIP_Y8950, list, clocklist);
				break;

			case SC_TYPE_SN76489:
				get_clocks(CHIP_SN76489, list, clocklist);
				break;

			case SC_TYPE_SCC:
				get_clocks(CHIP_SCC, list, clocklist);
				break;

			case SC_TYPE_SCCS:
				get_clocks(CHIP_SCCP, list, clocklist);
				break;

			case SC_TYPE_YM2610:
				get_clocks(CHIP_YM2610_H, list, clocklist);	// とりあえず YM2610+H
				break;

			case SC_TYPE_AY8910:
				get_clocks(CHIP_AY8910, list, clocklist);
				break;

			case SC_TYPE_YM2149:
				get_clocks(CHIP_YM2149, list, clocklist);
				break;

			case SC_TYPE_YM2413:
				get_clocks(CHIP_YM2413, list, clocklist);
				break;

			case SC_TYPE_YMF262:
				get_clocks(CHIP_YMF262, list, clocklist);
				break;

			default:
				break;
			}// switch

			// 仮想サウンドチップを作成する
			for (auto cl : clocklist) {
				if (el->getSoundChipClock() != cl) {
					if (el->getSoundChipType() == SC_TYPE_SN76489) {
						schip = new SoundChip_udpl_sn76489(el, cl, busid++);
					}
					else if (el->getSoundChipType() == SC_TYPE_YM2610) {
						schip = new SoundChip_udpl_ym2610_h(el, cl, busid++);
					}
					else {
						schip = new SoundChip_udpl(el, cl, busid++);
					}
					scciChipList.push_back(schip);
					same.push_back(schip);
				}
			}
		}

		// 同一ハードの情報をもたせる
		for (auto s : same) {
			s->setSameList(same);
		}

	}// for

	pscciIf = new SoundInterface_udpl(scciChipList);
	ifInfo.iSoundChipCount = scciChipList.size();
}

void SoundInterfaceManager_udpl::delAllList(void)
{
	for (auto el : scciChipList) {
		if (el->getSoundChipType() == SC_TYPE_SN76489) {
			delete dynamic_cast<SoundChip_udpl_sn76489*>(el);
		}
		else if (el->getSoundChipType() == SC_TYPE_YM2610) {
			delete dynamic_cast<SoundChip_udpl_ym2610_h*>(el);
		}
		else {
			delete el;
		}
	}

	if (pchipInfo) {
		delete[] pchipInfo;
		pchipInfo = nullptr;
	}

	delete pscciIf;
	pscciIf = nullptr;
}

void SoundInterfaceManager_udpl::split_null(const TCHAR *c, uint32_t size, std::vector<std::tstring> &list)
{
	uint32_t cur = 0;
	size_t len;

	while (cur < size) {
		len = _tcslen(c + cur);
		if (len != 0) {
			list.push_back(std::tstring(c + cur));
			cur += len + 1;
		}
		else {	// \0\0 が来た
			break;
		}
	}
}

void SoundInterfaceManager_udpl::get_appparam(const std::vector<std::tstring> &in, std::vector<PARAM_ELEM> &out)
{
	PARAM_ELEM element;
	unsigned int pos;

	for (auto el : in) {
		pos = el.find("=");
		if (pos != std::string::npos && pos != 0) {	// 見つけた
			element.appname = el.substr(0, pos);
			element.param = el.substr(pos + 1);
			out.push_back(element);
		}
	}
}

void SoundInterfaceManager_udpl::make_clocklist(const std::vector<PARAM_ELEM> &in, std::vector<uint32_t> &out)
{
	for (auto el : in) {
		if (el.appname == _T("CLOCK")) {
			out.push_back(std::stoi(el.param));
		}
	}
}

void SoundInterfaceManager_udpl::get_clocks(const char *chipname, const std::vector<INI_ELEM> &list, std::vector<uint32_t> &out)
{
	char name[1024];

	for (auto el : list) {
		memset(name, '\0', sizeof(name));
		tchar2char(name, el.section.c_str(), _countof(name));
		if (strcmp(name, chipname) == 0) {
			make_clocklist(el.paramlist, out);
		}
	}
}

void SoundInterfaceManager_udpl::readSetting(std::vector<INI_ELEM> &list)
{
	TCHAR str[1024] = { _T('\0') };
	DWORD ret;
	std::vector<std::tstring> seclist;
	std::vector<std::tstring> para_combo_list;
	std::vector<PARAM_ELEM> para_split_list;

	INI_ELEM tmpelem;

	std::tstring iniPath = strDllPath + iniName;
	
	if (_taccess(iniPath.c_str(), 0) == 0) {	// ファイルが存在する
		
		ret = GetPrivateProfileSectionNames(str, _countof(str), iniPath.c_str());
		split_null(str, ret, seclist);	// 全セクション名をリストで持つ
		
		for (auto el : seclist) {	// 各セクションについて…
			// 初期化
			memset(str, '\0', sizeof(str));
			para_combo_list.clear();
			para_split_list.clear();

			ret = GetPrivateProfileSection(el.c_str(), str, _countof(str), iniPath.c_str());
			split_null(str, ret, para_combo_list);
			get_appparam(para_combo_list, para_split_list);	// アプリネームとパラメータをリストで持つ

			tmpelem.section = el;
			tmpelem.paramlist = para_split_list;
			list.push_back(tmpelem);	// セクションとパラメータの組み合わせを作ってリスト化する
		}
	}
	else {	// ファイルが存在しない

	}
}



// *********************************************************************************************************
// override

//
// マネージャーの初期化
//
BOOL __stdcall SoundInterfaceManager_udpl::initializeInstance()
{
	BOOL ret = FALSE;

	// 最初にBASE_DLL のライブラリを初期化する
	pudplManager->initialize();

	std::vector<INI_ELEM> all_params;

	// ini から設定を読む
	readSetting(all_params);

	// soundchip、soundinterface を生成する
	makeAllList(all_params);

	return TRUE;
}

//
// マネージャーの削除
// hoot は scci が認識してるチップが 0 だと この関数を実行しないのでメモリリークする
//
BOOL __stdcall SoundInterfaceManager_udpl::releaseInstance()
{
	delAllList();

	if (pudplManager) {
		pudplManager->suicide();	// udpl を開放
		pudplManager = nullptr;
	}

	if (hDll) {
		::FreeLibrary(hDll);		// udpl DLL をアンロード
		hDll = nullptr;
	}

	delete this;		// 本体を削除
	return TRUE;
}

//
// インターフェイス情報を返す
//
SCCI_INTERFACE_INFO* __stdcall SoundInterfaceManager_udpl::getInterfaceInfo(int iInterfaceNo)
{
	SCCI_INTERFACE_INFO *ret = nullptr;

	if (iInterfaceNo == 0) {
		ret = &ifInfo;
	}

	return ret;
}

//
// インターフェイスを返す
//
SoundInterface* __stdcall SoundInterfaceManager_udpl::getInterface(int iInterfaceNo)
{
	SoundInterface *ret = nullptr;

	if (iInterfaceNo == 0) {
		ret = pscciIf;
	}

	return ret;
}

//
// インターフェイスを開放する
//
BOOL __stdcall SoundInterfaceManager_udpl::releaseInterface(SoundInterface* pSoundInterface)
{
	BOOL ret = TRUE;
	return ret;
}

//
// すべてのインターフェイスを開放する
//
BOOL __stdcall SoundInterfaceManager_udpl::releaseAllInterface()
{
	BOOL ret = TRUE;
	return ret;
}

//
// サウンドチップを返す
//
SoundChip* __stdcall SoundInterfaceManager_udpl::getSoundChip(int iSoundChipType, DWORD dClock)
{
	SoundChip *ret = nullptr;
	for (auto el : scciChipList) {
		if (!el->getUsed() && el->getSoundChipType() == iSoundChipType && ((dClock == 0) || (el->getSoundChipClock() == dClock))) {
			el->setUsed(true, true);	// 同一ハードはすべて used
			ret = el;
			break;
		}
	}

	return ret;
}

//
// サウンドチップを開放する
//
BOOL __stdcall SoundInterfaceManager_udpl::releaseSoundChip(SoundChip* pSoundChip)
{
	BOOL ret = FALSE;
	for (auto el : scciChipList) {
		if (el == pSoundChip) {
			el->setUsed(false, true);
			ret = TRUE;
			break;
		}
	}

	return ret;
}

//
// すべてのサウンドチップを開放する
//
BOOL __stdcall SoundInterfaceManager_udpl::releaseAllSoundChip()
{
	BOOL ret = TRUE;
	for (auto el : scciChipList) {
		el->setUsed(false);
	}

	return ret;
}

int __stdcall SoundInterfaceManager_udpl::getInterfaceCount()
{
	return interfaceNum;
}



//
// 
//

void __stdcall SoundInterfaceManager_udpl::setAcquisitionMode(int iMode)
{
}

void __stdcall SoundInterfaceManager_udpl::setAcquisitionModeClockRenge(DWORD dClock)
{
}

BOOL __stdcall SoundInterfaceManager_udpl::setDelay(DWORD dMSec)
{
	BOOL ret = TRUE;
	return ret;
}

DWORD __stdcall SoundInterfaceManager_udpl::getDelay()
{
	DWORD ret = 0;
	return ret;
}

BOOL __stdcall SoundInterfaceManager_udpl::reset()
{
	bool uret = true;
	uret = pudplManager->reset();

	return uret ? TRUE : FALSE;
}

BOOL __stdcall SoundInterfaceManager_udpl::init()
{
	bool uret = true;
	uret = pudplManager->init();

	return uret ? TRUE : FALSE;
}

BOOL __stdcall SoundInterfaceManager_udpl::config()
{
	BOOL ret = TRUE;
	return ret;
}

DWORD __stdcall SoundInterfaceManager_udpl::getVersion(DWORD *pMVersion)
{
	DWORD ret = 0x01;
	return ret;
}

BOOL __stdcall SoundInterfaceManager_udpl::isValidLevelDisp()
{
	BOOL ret = TRUE;
	return ret;
}

BOOL __stdcall SoundInterfaceManager_udpl::isLevelDisp()
{
	BOOL ret = TRUE;
	return ret;
}

void __stdcall SoundInterfaceManager_udpl::setLevelDisp(BOOL bDisp)
{
}

void __stdcall SoundInterfaceManager_udpl::setMode(int iMode)
{
}

void __stdcall SoundInterfaceManager_udpl::sendData()
{
}

void __stdcall SoundInterfaceManager_udpl::clearBuff()
{
}

BOOL __stdcall SoundInterfaceManager_udpl::setCommandBuffetSize(DWORD dBuffSize)
{
	BOOL ret = TRUE;
	return ret;
}

BOOL __stdcall SoundInterfaceManager_udpl::isBufferEmpty()
{
	BOOL ret = FALSE;
	return ret;
}
