#pragma once

#include <stdint.h>

// チップの情報
typedef struct {
	bool		variable_clock;		// PLL クロックをつかってるか true=使ってる
	bool		hardware_reset;		// ハードウェアリセットかけるか true=使う
	uint32_t	clock;				// クロック
	char		name[16];			// チップ名 chipname.h で定義してある
	char		extinfo[32];		// 追加情報
	uint32_t	slotNo;				// SLOT 番号
	char		board_address_ipv6[65];	// 刺さってるボードの ipv6	// INET6_ADDRSTRLEN (65) 以上
	char		board_address_ipv4[22];	// 刺さってるボードの ipv4	// INET_ADDRSTRLEN (22) 以上
} UDPL_CHIP_INFO;

// ボードの情報
typedef struct {
	uint32_t	version;		// ファームバージョン
	uint16_t	listen_port;	// 受信ポート番号
	uint16_t	send_port;		// 送信ポート番号
	uint16_t	wait;			// コマンド送信遅延[ms]
	uint32_t	bufsize;		// ボードのバッファサイズ[byte]
	char		name[32];		// ボードのニックネーム
	uint32_t	protocol;		// プロトコル
	char		host_address_ipv6[65];	// 接続している PC の ipv6	// INET6_ADDRSTRLEN 以上
	char		host_address_ipv4[22];	// 接続している PC の ipv4	// INET_ADDRSTRLEN 以上
	char		board_address_ipv6[65];	// ボードの ipv6	// INET6_ADDRSTRLEN 以上
	char		board_address_ipv4[22];	// ボードの ipv4	// INET_ADDRSTRLEN (22) 以上
	uint32_t	slotnum;		// 総スロット数
} UDPL_BOARD_INFO;

enum INTERFACE_PROTOCOL : uint32_t {	// プロトコル
	IPV6UDP = 0,	// IPv6 UDP
	IPV4UDP,		// IPv4 UDP
	IPV6TCP,		// IPv6 TCP
	IPV4TCP			// IPv4 TCP
};

typedef struct {	// YM2610/B+H 用 ADPCM データ情報
	uint8_t *pdata;	// ADPCM データへのポインタ
	uint32_t ch;	// ADPCM-A か ADPCM-B か
	uint32_t size;	// データサイズ
	uint32_t offset;	// オフセット
} YM2610_PCMDATA;

enum : uint32_t {	// ADPCM-A と ADPCM-B の指定
	ADPCM_A_CH = 1,	// ADPCM-A チャンネル
	ADPCM_B_CH = 0	// ADPCM-B チャンネル
};

enum : unsigned int {	// 電子ボリュームで制御するアナログ入出力
	BOARD_OUTPUT = 0,	// 出力
	BOARD_INPUT			// 入力
};

enum : unsigned int {	// 電子ボリュームで制御するステレオチャンネル
	LEFT_CH = 0,		// 左
	RIGHT_CH,			// 右
	STEREO_CH			// 左右両方
};

// プロトタイプ
class UdplManager;
class UdplChip;

class UdplManager	// DLL 制御用インターフェイス
{
public:
	/*
	* 初期化 最初に必ず実行する
	*/
	virtual void __stdcall initialize(void) = 0;

	/*
	* ファイナライズ 最後、DLL を開放する前にかならず実行する
	*/
	virtual void __stdcall suicide(void) = 0;

	/*
	* 認識しているチップの総数を返す
	*/
	virtual uint32_t __stdcall getChipCount(void) = 0;

	/*
	* 認識しているボードの総数を返す
	*/
	virtual uint32_t __stdcall getBoardCount(void) = 0;

	/*
	* 認識しているチップの情報を返す
	* plist = 情報を受け取るバッファへのポインタ。バッファはコール側が管理する sizeof(UDPL_CHIP_INFO) * num バイト用意する
	* num = 情報を受け取る対象のチップ数
	*/
	virtual void __stdcall getChipInfoList(UDPL_CHIP_INFO *plist, uint32_t num) = 0;

	/*
	* 認識しているボードの情報を返す
	* plist = 情報を受け取るバッファへのポインタ。バッファはコール側が管理する sizeof(UDPL_BOARD_INFO) * num バイト用意する
	* num = 情報を受け取る対象のボード数
	*/
	virtual void __stdcall getBoardInfoList(UDPL_BOARD_INFO *plist, uint32_t num) = 0;

	/*
	* チップ制御用インターフェイスを返す。引数の条件に合うインターフェイスをマネージャーが自動で選ぶ
	* chipname = チップ名
	* clock = クロック
	*/
	virtual UdplChip * __stdcall getChip(const char *chipname, uint32_t clock) = 0;

	/*
	* チップ制御用インターフェイスを返す
	* board_address = チップが刺さっているボードの ip アドレス
	* slotno = チップが刺さっているスロットの番号
	* clock = クロック
	*/
	virtual UdplChip * __stdcall getChip(const char *board_address, uint32_t slotno, uint32_t clock) = 0;

	/*
	* チップ制御用インターフェイスを返す
	* index = DLL が認識しているチップの順番。getChipInfoList() で取得するチップ情報の順番。
	* clock = クロック
	*/
	virtual UdplChip * __stdcall getChip(uint32_t index, uint32_t clock) = 0;

	/*
	* チップ制御用インターフェイスを開放する
	* pchip = getChip() で取得したポインタを与える
	*/
	virtual bool __stdcall releseChip(UdplChip *pchip) = 0;

	/*
	* チップ制御用インターフェイスをすべて開放する
	*/
	virtual void __stdcall releseChipAll(void) = 0;
	
	/*
	* すべてのチップにハードウェアリセットをかける(IC# = 0 を実行する
	*/
	virtual bool __stdcall reset(void) = 0;

	/*
	* すべてのチップを初期化する
	*/
	virtual bool __stdcall init(void) = 0;

	/*
	* 指定の I2C デバイスを読む
	* baddress = I2C デバイスが接続されているボードの ip アドレス
	* slave_addr = I2C デバイスのアドレス（7 bit）
	* pr_buf = 読んだ内容を受け取るバッファへのポインタ
	* r_size = pr_buf が示すバッファのサイズ
	* r_len = I2C デバイスから読み出すバイト数
	* pw_buf = 読み出し実行前に書き込む内容を示すバッファへのポインタ。null を与えると書き込みを行わない
	* w_len = 読み出し実行前に書き込むバイト数。0 を与えると書き込みを行わない
	* repeatedstart = true のとき、書き込み後に読み込みを行う場合にrepeated start を使う。現在未使用で、常に stop する。
	*/
	virtual uint32_t __stdcall readI2C(const char *baddress, uint8_t slave_addr, uint8_t *pr_buf, uint8_t r_size, uint8_t r_len, const uint8_t *pw_buf = nullptr, uint8_t w_len = 0, bool repeatedstart = false) = 0;

	/*
	* 指定の I2C デバイスに書き込む
	* baddress = I2C デバイスが接続されているボードの ip アドレス
	* slave_addr = I2C デバイスのアドレス（7 bit）
	* pw_buf = 書き込む内容を示すバッファへのポインタ
	* len = 書き込むバイト数
	*/
	virtual bool __stdcall writeI2C(const char *baddress, uint8_t slave_addr, const uint8_t *pw_buf, uint8_t len) = 0;

	/*
	* 指定のアドレスの I2C デバイスが存在するか調べる
	* baddress = I2C デバイスが接続されているボードの ip アドレス
	* addr = I2C デバイスのアドレス（7 bit）
	* 返り値 true = 存在する. false = 存在しない
	*/
	virtual bool __stdcall checkI2CAddr(const char* baddress, uint8_t addr) = 0;

	/*
	* ボードのボリュームを設定する
	* board_address = 対象のボードの ip アドレス
	* vol = ボリューム値。DS1881 の設定値で、0〜33
	* ch = 音声のチャンネル LEFT_CH/RIGHT_CH/STEREO_CH
	* inout = 出力、入力の指定 BOARD_INPUT/BOARD_OUTPUT 現在は BOARD_OUTPUT のみ制御可能
	* 返り値 true = ボリューム設定成功. false = ボリュームの制御が出来ない
	*/
	virtual bool __stdcall setVol(const char* board_address, uint32_t vol, unsigned int ch = STEREO_CH, unsigned int inout = BOARD_OUTPUT) = 0;
	
	/*
	* ボードのボリュームの現在値を取得する
	* board_address = 対象のボードの ip アドレス
	* vol = 取得したボリューム値を受け取る変数へのポインタ
	* ch = 音声のチャンネル LEFT_CH/RIGHT_CH
	* inout = 出力、入力の指定 BOARD_INPUT/BOARD_OUTPUT 現在は BOARD_OUTPUT のみ制御可能
	* 返り値 true = ボリューム取得成功. false = ボリューム値を読めない
	*/
	virtual bool __stdcall getVol(const char* board_address, uint32_t* vol, unsigned int ch, unsigned int inout = BOARD_OUTPUT) = 0;
};

class UdplChip	// チップ制御用インターフェイス
{
public:
	/*
	*	指定アドレスのレジスタに値を書き込む
	*	addr = チップのレジスタのアドレス
	*	A1 = 1 の時のアドレスは、16進数で 1xx となる
	*	data = 書き込む値
	*/
	virtual bool __stdcall setRegister(uint32_t addr, uint32_t data) = 0;

	/*
	*	バスに書き込む
	*	abit = A0〜A3 ビットを指定する [X][X][X][X]_[A3][A2][A1][A0]
	*	data = データバスの値 [D7][D6][D5][D4]_[D3][D2][D1][D0]
	*/
	virtual bool __stdcall writeBus(uint8_t abit, uint8_t data) = 0;

	/*
	*	指定アドレスのレジスタを読み込む
	*	addr = チップのレジスタのアドレス
	*	A1 = 1 の時のアドレスは、16進数で 1xx となる
	*	返り値 = 読み込んだ値
	*/
	virtual uint32_t __stdcall getRegister(uint32_t addr) = 0;

	/*
	*	クロックを設定する
	*	clock = 設定するクロック
	*	ignore_lock = true を指定すると、PLL へのクロック設定を行った後、PLL のロックを待たずに次の処理を行う。
	*	              false のときは、PLL のロックを待って、次の処理を行う
	*/
	virtual bool __stdcall setClock(uint32_t clock, bool ignore_lock = false) = 0;

	/*
	*	チップを初期化する
	*	レジスタの初期化、及び設定に応じたハードウェアリセットを行う
	*/
	virtual bool __stdcall init(void) = 0;

	/*
	*	チップをハードウェアリセットする
	*	IC# = 0 を実行する
	*/
	virtual bool __stdcall reset(void) = 0;

	/*
	*	チップ名を返す
	*	返り値 = チップ名へのポインタ
	*/
	virtual const char * __stdcall getChipName(void) = 0;

	/*
	*	現在のチップのクロックを返す
	*/
	virtual uint32_t __stdcall getClock(void) = 0;
	
	/*
	*	チップが刺さっているボードの ip アドレスを返す
	*	interface_protocol = プロトコルを指定する。現在は IPV6UDP のみ対応している
	*	返り値 = ipアドレスへのポインタ
	*/
	virtual const char* __stdcall getBoardAddress(uint32_t interface_protocol = IPV6UDP) = 0;

	/*
	*	チップが刺さっているスロットの番号を返す
	*/
	virtual uint32_t __stdcall getSlotNo(void) = 0;
	
	/*
	*	PCM データをセットする
	*	現在は YM2610/B+H のみ実装されている
	*	pdat = YM2610_PCMDATA 構造体へのポインタ
	*/
	virtual bool __stdcall setPcmData(void *pdat) = 0;
};


// インターフェイス作成用公開関数の型定義
typedef UdplManager* (__stdcall *UDPLFUNC)(void);

// インターフェイス作成用公開関数
extern "C" UdplManager* __stdcall getManager(void);
