#pragma once
#include "udpl_dll_imp.h"
#include "ftd2xx.h"
#include "ftd2xx_func.h"


constexpr uint32_t ADPCM_SIZE = 16777216;	// ADPCM buffer size
constexpr uint32_t CBUS_WAIT = 8;			// cbus bitbang wait
const TCHAR ftdiName[] = _T("ftd2xx.dll");

//
// YM2610 + FT232H
//
class UdplChipYM2610_H : public UdplChipImp
{
public:
	UdplChipYM2610_H(UdplSocketIf *pif, UDPL_CHIP_INFO *pinfo) : UdplChipImp(pif, pinfo)
	{
		// ウェイト設定
		setWaits(pinfo->clock);
		// レジスタの初期化をする
		initRegs();

		for (unsigned int i = 0; i < 2; ++i) {
			pcmbuf[i] = new uint8_t[ADPCM_SIZE];
			pcmbuf_len[i] = 0;
		}
		
		ftvalid = false;
		openDevice();	// ft232h オープン
	};

	virtual ~UdplChipYM2610_H()
	{
		closeDevice();		// ft232h クローズ

		for (unsigned int i = 0; i < 2; ++i) {
			
			if (sendth[i].joinable()) {
				sendth[i].join();
			}
			if (pcmbuf[i]) {
				delete[] pcmbuf[i];
				pcmbuf[i] = nullptr;
			}
		}
	};

	bool __stdcall setRegister(uint32_t addr, uint32_t data);
	bool __stdcall writeBus(uint8_t abit, uint8_t data);
	uint32_t __stdcall getRegister(uint32_t addr);
	bool __stdcall setPcmData(void *pdat);

private:
	virtual void setWaits(uint32_t clk) override;		// アドレスウェイト、データウェイトの値の計算
	virtual void initRegs(void) override;				// レジスタの初期化

	void openDevice(void);
	void closeDevice(void);
	void sendPcmBuf(uint32_t ch, uint32_t offset);

	uint8_t wait0;
	uint8_t wait17;
	uint8_t wait83;
	uint8_t wait47;
	uint8_t wait_pcma_data;	// ADPCM-A のデータ書き込み後ウエイト
	uint8_t wait_pcmb_data;	// ADPCM-B のデータ書き込み後ウエイト
	uint8_t* pcmbuf[2];
	uint32_t pcmbuf_len[2];
	std::mutex pcmbuf_m[2];
	std::thread sendth[2];
	std::mutex ftdi_m;

	HMODULE hFtModule;			// DLL モジュールハンドル
	FT_HANDLE hFtd2;			// デバイスハンドル

	_FT_Close FT_Close;
	_FT_Write FT_Write;
	_FT_ResetDevice FT_ResetDevice;
	_FT_Purge FT_Purge;
	_FT_SetBitMode FT_SetBitMode;
	_FT_GetBitMode FT_GetBitMode;
	_FT_SetBaudRate FT_SetBaudRate;
	_FT_SetTimeouts FT_SetTimeouts;
	_FT_GetDeviceInfo FT_GetDeviceInfo;
	_FT_OpenEx FT_OpenEx;
	_FT_SetLatencyTimer FT_SetLatencyTimer;
	bool ftvalid;
};