#define _WINSOCKAPI_
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iphlpapi.h>

#include <string>
#include <list>
#include <vector>

#include "udpl_dll_imp.h"
#include "ym2151.h"
#include "ym2608.h"
#include "ym2612_3438.h"
#include "ym2203.h"
#include "ym3526_3812.h"
#include "y8950.h"
#include "sn76489a.h"
#include "scc.h"
#include "sccp.h"
#include "ym2610_ft232h.h"
#include "psg_ssg.h"
#include "ym2413.h"
#include "ymf262.h"
#include "unknownchip.h"
#include "command_list.h"
#include "setting_no.h"
#include "chipname.h"


// ******************************************************************************************
// ローカルの IPv6 アドレスを取得する。同じローカルエリアにつながってるものは重複を削除する
// ******************************************************************************************
void UdplManagerImp::getAddapterList(std::list<std::string> &list)
{
	PIP_ADAPTER_ADDRESSES paddress = nullptr;
	PIP_ADAPTER_ADDRESSES pcur;
	ULONG ret;
	ULONG size = 0;

	list.clear();
	std::list<LOCAL_NODE> gateway_list;	// local アドレスと ゲートウェイアドレスのリスト

	// 必要なバッファサイズ取得
	ret = GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, nullptr, nullptr, &size);
	if (ret != ERROR_BUFFER_OVERFLOW) {		// サイズ取得失敗
		return;
	}

	// バッファ確保
	paddress = (PIP_ADAPTER_ADDRESSES)malloc(size);

	// アダプターリスト取得
	ret = GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, nullptr, paddress, &size);
	if (ret != ERROR_SUCCESS) {		// 取得失敗
		free(paddress);
		return;
	}

	PIP_ADAPTER_UNICAST_ADDRESS pUnicast;	// ローカルアドレス一覧
	PIP_ADAPTER_GATEWAY_ADDRESS pGateway;	// ゲートウェイの一覧
	char ipv6_str[INET6_ADDRSTRLEN];		// ipv6 アドレス

	pcur = paddress;
	while (pcur) {

		pUnicast = pcur->FirstUnicastAddress;	// ローカルアドレス一覧
		pGateway = pcur->FirstGatewayAddress;	// ゲートウェイの一覧
		
		if (pcur->IfType == IF_TYPE_ETHERNET_CSMACD || pcur->IfType == IF_TYPE_IEEE80211) {		// イーサーネットか、WiFi

			LOCAL_NODE node;
			// node.local_addres を作成
			while (pUnicast) {
				memset(ipv6_str, '\0', sizeof(ipv6_str));
				inet_ntop(AF_INET6, &((struct sockaddr_in6 *)(pUnicast->Address.lpSockaddr))->sin6_addr, ipv6_str, sizeof(ipv6_str));	// ipv6 アドレスを文字列として取得
				std::string str(ipv6_str);
				std::size_t found = str.find("fe80:");		//リンクローカルアドレスを探す。必ず一つあるはず
				if (found != std::string::npos) {			// 見つけた
					node.local_address = str;
				}
				pUnicast = pUnicast->Next;
			}
			// node.address_list を作成
			while (pGateway) {	// ローカルアドレスのゲートウェイを探索する
				memset(ipv6_str, '\0', sizeof(ipv6_str));
				inet_ntop(AF_INET6, &((struct sockaddr_in6 *)(pGateway->Address.lpSockaddr))->sin6_addr, ipv6_str, sizeof(ipv6_str));
				node.address_list.push_back(std::string(ipv6_str));
				pGateway = pGateway->Next;
			}
			// local address と gateway のリストを作成
			gateway_list.push_back(node);
		}
		pcur = pcur->Next;	// すべてのアダプタに対して実行
	}

	// バッファ開放
	free(paddress);


	// address_list の比較のためにソートしておく
	for (auto &elem : gateway_list) {
		elem.address_list.sort();
	}


	// gateway が同じアダプタは削除する
	if (gateway_list.size()) {

		for (auto itsrc = gateway_list.begin(); itsrc != gateway_list.end(); ++itsrc) {
				
			if (itsrc->address_list.size() == 0) {	// ゲートウェイが無い
				continue;	// スキップ
			}

			auto itdest = itsrc;
			++itdest;

			while( itdest != gateway_list.end() ) {
				if (itsrc->address_list == itdest->address_list) {
					itdest = gateway_list.erase(itdest);
				} else {
					++itdest;
				}
			}
		}
	}

	// ゲートウェイの重複がない local ipv6 addres のリストが得られたので、参照に書き込む
	for (auto &elem : gateway_list) {
		list.push_back(elem.local_address);
	}

	list.sort();	// ソートしておく
}

// ******************************************************************************************
// local address から socket を得る
// ******************************************************************************************
SOCKET UdplManagerImp::getSocket(std::string &localip, u_short port)
{

	SOCKET sock = INVALID_SOCKET;
	struct addrinfo *plocal_addr = nullptr;		// ローカルの情報
	struct addrinfo hint;

	memset(&hint, 0, sizeof(hint));
	hint.ai_flags = AI_PASSIVE;			// bind 用
	hint.ai_family = AF_INET6;			// ipV6
	hint.ai_socktype = SOCK_DGRAM;		// UDP
	hint.ai_protocol = IPPROTO_UDP;		// UDP

	int ret;
	if (port != 0) {
		ret = getaddrinfo(localip.c_str(), std::to_string(port).c_str(), &hint, &plocal_addr);
	}
	else {
		ret = getaddrinfo(localip.c_str(), nullptr, &hint, &plocal_addr);
	}
	if (ret != 0) {
		return INVALID_SOCKET;
	}

	// ソケット初期化と bind
	for (struct addrinfo *ptmp = plocal_addr; ptmp != nullptr; ptmp = ptmp->ai_next) {

		// ソケット初期化
		sock = socket(ptmp->ai_family, ptmp->ai_socktype, ptmp->ai_protocol);		// IPv6, UDP socket
		if (sock == INVALID_SOCKET) {	// socket 取得失敗
			continue;
		}
		struct sockaddr_in6 *pp = (struct sockaddr_in6 *)ptmp->ai_addr;

		// ファイルディスクリプターで参照されるソケットにオプションの設定
		// SOL_SOCKET : ソケット API 層
		// SO_REUSEADDR : bindが失敗するのを回避 TIME_WAIT のポートを使う http://yanchde.gozaru.jp/winsock2/setsockopt.html
		DWORD v6only = 1;
		if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only)) != SOCKET_ERROR) {	// IPV6 通信のみ可。IPv4 不可。
			INT protect = PROTECTION_LEVEL_RESTRICTED;
			if (setsockopt(sock, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (char *)&protect, sizeof(protect)) != SOCKET_ERROR) {	// IPV6 通信をリンクローカル内のみに限定する
				BOOL on = TRUE;
				if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != SOCKET_ERROR) {
					DWORD tout = RECV_TIMEOUT;	// recv のタイムアウトを設定する
					if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tout, sizeof(tout)) != SOCKET_ERROR) {	// SO_RCVTIMEO で設定したタイムアウトは select には適用されない
						// bind
						if (bind(sock, ptmp->ai_addr, (int)ptmp->ai_addrlen) != SOCKET_ERROR) {
							break;	// 成功したので for を抜ける
						}
					}
				}
			}
		}

		// どこかで失敗した
		closesocket(sock);
		sock = INVALID_SOCKET;
	}

	// free addrinfo
	freeaddrinfo(plocal_addr);
	plocal_addr = nullptr;

	return sock;
}

// ******************************************************************************************
// UDPL ボードを探す
// ******************************************************************************************
void UdplManagerImp::getUdplAddress(std::list<LOCAL_NODE> &list)
{
	SOCKET sock;
	for (auto &elem : list) {
		sock = getSocket(elem.local_address, udpport);
		if (sock == INVALID_SOCKET) {
			continue;
		}

		// socket が取得できた
		char buf[16];						// 受信バッファ
		struct sockaddr_in6 dest_addr;		// 送信先アドレス
		char broadcast_ip[] = "ff02::1";	// ローカルマルチキャストアドレス

		struct sockaddr_in6 from;			// リプライ元
		int from_len = sizeof(from);
		char from_addr[INET6_ADDRSTRLEN];	// リプライ元 IPV6 アドレス
		uint32_t count = 0;					// timeout count
		int ret;

		memset(&dest_addr, 0, sizeof(dest_addr));
		dest_addr.sin6_family = AF_INET6;
		dest_addr.sin6_port = htons(udpport);
		inet_pton(dest_addr.sin6_family, broadcast_ip, &dest_addr.sin6_addr);

		// 送信
		ret = sendto(sock, (char *)COMMAND_INQUIRY, sizeof(COMMAND_INQUIRY), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));

		memset(buf, 0, sizeof(buf));

		// 受信
		do {
			ret = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&from, &from_len);
			if (ret != SOCKET_ERROR) {		// 受信データあり
				if (memcmp(buf, RESPONSE_INQUIRY_UDPL, ret) == 0) {	// WiFi PLL Mother からの応答
					memset(from_addr, '\0', sizeof(from_addr));
					inet_ntop(from.sin6_family, &from.sin6_addr, from_addr, sizeof(from_addr));
					elem.address_list.push_back(std::string(from_addr));	// 応答があった ipv6 アドレスをリストに追加
				}
			}
			else if (WSAGetLastError() == WSAETIMEDOUT) {		// recvfrom がタイムアウトした
				break;	// do を抜ける
			}
		} while (true);
		closesocket(sock);		// ソケットは一旦クローズする
	}

	// ローカル IPV6 と WiFi PLL Mother IPV6 のリストが作成できた。

	// WiFi PLL Mother が相手にいないローカルアドレスはリストから消す
	for (auto it = list.begin(); it != list.end(); ) {
		if (it->address_list.size() == 0) {		// ターゲット無し
			it = list.erase(it);	// リストから削除
		}
		else {
			it->address_list.sort();	// ターゲットのアドレスをソートしておく
			++it;
		}
	}
}

// ******************************************************************************************
// UDPL ボード一つに付き一つの socket を用意する
// socket には違うポートを割り当て、ボードの設定を変更する
// !! 現状は 一つのソケットを一つのボードに対する送受信の両方に使う -> 一つのソケットには別々の port を割り当てる。そのために COMMAND_SET_PORT を必要とする
// !! TBD. 受信専用ソケットを作り、パケットの送信元によって受信データを振り分けるようにすると、COMMAND_SET_PORT は不要になる
// ******************************************************************************************
void UdplManagerImp::getSocketList(std::list<SOCKET_ELEM> &list)
{
	u_short port = udpport;

	int ret = 0;
	struct sockaddr_in6 dest_addr;		// 送信先アドレス
	uint8_t send_buf[16];
	uint8_t *ptmp = send_buf;
	uint8_t recv_buf[16];

	// 送信先アドレス用意
	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.sin6_family = AF_INET6;
	dest_addr.sin6_port = htons(udpport);

	//
	memcpy(send_buf, COMMAND_SET_PORT, sizeof(COMMAND_SET_PORT));
	ptmp += sizeof(COMMAND_SET_PORT);

	for (auto &elem : list) {
		elem.sock = getSocket(elem.local_address, port);	// udpport から一つづつポートを割り当てていく
		elem.listen_port = port;

		inet_pton(dest_addr.sin6_family, elem.target_address.c_str(), &dest_addr.sin6_addr);	// 送信先セット

		// リトルエンディアンで COMMAND_SET_PORT に port 番号をセットして送信する
		*ptmp = port & 0xff;
		*(ptmp + 1) = (port >> 8) & 0xff;

		ret = sendto(elem.sock, (char *)send_buf, COMMAND_SET_PORT[0] + 1, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
		ret = recvfrom(elem.sock, (char *)recv_buf, sizeof(recv_buf), 0, nullptr, nullptr);
		
		++port;
	}
}

// ******************************************************************************************
// ソケットを閉じる
// その前にポートを戻しておく
// ******************************************************************************************
void UdplManagerImp::closeSocketList(std::list<SOCKET_ELEM> &list)
{
	struct sockaddr_in6 dest_addr;		// 送信先アドレス
	uint8_t send_buf[16];
	uint8_t recv_buf[16];
	uint8_t *ptmp = send_buf;
	int ret = 0;

	// 送信先アドレス用意
	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.sin6_family = AF_INET6;
	dest_addr.sin6_port = htons(udpport);

	//	ボードの送信ポートをデフォルトに戻す
	memcpy(send_buf, COMMAND_SET_PORT, sizeof(COMMAND_SET_PORT));
	ptmp += sizeof(COMMAND_SET_PORT);
	*ptmp++ = 0x00;
	*ptmp++ = 0x00;

	for (auto &elem : sock_list) {
		inet_pton(dest_addr.sin6_family, elem.target_address.c_str(), &dest_addr.sin6_addr);	// 送信先セット
		ret = sendto(elem.sock, (char *)send_buf, COMMAND_SET_PORT[0] + 1, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
		ret = recvfrom(elem.sock, (char*)recv_buf, sizeof(recv_buf), 0, nullptr, nullptr);
		closesocket(elem.sock);
	}
}

// ******************************************************************************************
// UDPL のボード情報を取得
// ******************************************************************************************
void UdplManagerImp::getBoardInfo(const std::list<SOCKET_ELEM> &list, UDPL_BOARD_INFO *pInfo, UDPL_BOARD_INFO_EXT *pInfoExt)
{
	uint8_t send_buf[16];
	uint8_t *psb = send_buf;
	uint8_t recv_buf[128];
	int ret;
	
	struct sockaddr_in6 dest_addr;		// 送信先アドレス
	// 送信先アドレス用意
	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.sin6_family = AF_INET6;
	dest_addr.sin6_port = htons(udpport);

	// コマンド用意
	memset(send_buf, '\0', sizeof(send_buf));
	memcpy(send_buf, COMMAND_READ_SETTING, sizeof(COMMAND_READ_SETTING));	// ヘッダ設定
	psb += sizeof(COMMAND_READ_SETTING);	// ポインタを本体部分へ
	*psb++ = SETTING_BOARD;					// ターゲットはボード
	uint8_t param[] = { SETTING_BOARD_VERSION, SETTING_BOARD_SLOTNUM, SETTING_BOARD_WAIT, SETTING_BOARD_BUFSIZE, SETTING_BOARD_NAME, SETTING_BOARD_IPV4, SETTING_BOARD_HBCYCLE };

	uint32_t list_index = 0;
	for (auto elem : list) {
		// ipアドレスをコピー
		strncpy_s(pInfo[list_index].host_address_ipv6, sizeof(pInfo[list_index].host_address_ipv6), elem.local_address.c_str(), elem.local_address.size());	// ローカル IPV6
		strncpy_s(pInfo[list_index].board_address_ipv6, sizeof(pInfo[list_index].board_address_ipv6), elem.target_address.c_str(), elem.target_address.size());	// ターゲット IPV6

		// ip バージョンセット
		pInfo[list_index].protocol = IPV6UDP;			// 現状 IPV6 のみ対応
		pInfo[list_index].listen_port = udpport;		// 応答したってことはこのポート番号
		pInfo[list_index].send_port = elem.listen_port;	// ボードの送信ポートはソケットの受信ポート

		// info ext セット
		pInfoExt[list_index].blank = WAIT_MS;

		// ターゲットから情報を取得する
		inet_pton(dest_addr.sin6_family, elem.target_address.c_str(), &dest_addr.sin6_addr);	// 送信先セット
		for (uint32_t i = 0; i < _countof(param); ++i) {

			*psb = param[i];	//コマンドセット

			ret = sendto(elem.sock, (char *)send_buf, send_buf[0] + 1, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
			ret = recvfrom(elem.sock, (char *)recv_buf, sizeof(recv_buf), 0, nullptr, nullptr);

			if ((ret != SOCKET_ERROR) && (recv_buf[1] == COMMAND_READ_SETTING[1]) && (recv_buf[2] == SETTING_BOARD)) {		// 受信データあり

				switch (recv_buf[3]) {
				case SETTING_BOARD_VERSION:
				{
					pInfo[list_index].version = recv_buf[4] << 8 | recv_buf[5] << 16 | recv_buf[6] << 24;
					break;
				}
				case SETTING_BOARD_SLOTNUM:
				{
					pInfo[list_index].slotnum = recv_buf[4];
					break;
				}
				case SETTING_BOARD_WAIT:
				{
					pInfo[list_index].wait = recv_buf[4] | recv_buf[5] << 8;
					break;
				}
				case SETTING_BOARD_BUFSIZE:
				{
					pInfo[list_index].bufsize = recv_buf[4] | recv_buf[5] << 8 | recv_buf[6] << 16 | recv_buf[7] << 24;
					break;
				}
				case SETTING_BOARD_NAME:
				{
					if (recv_buf[0] > 3) {
						memcpy_s(pInfo[list_index].name, sizeof(pInfo[list_index].name), recv_buf + 4, recv_buf[0] - 3);
					}
					break;
				}
				case SETTING_BOARD_IPV4:
				{
					if (recv_buf[0] > 3) {
						memcpy_s(pInfo[list_index].board_address_ipv4, sizeof(pInfo[list_index].board_address_ipv4), recv_buf + 4, recv_buf[0] - 3);
					}
					break;
				}
				case SETTING_BOARD_HBCYCLE:
				{
					pInfoExt[list_index].hbcycle = recv_buf[4];
					break;
				}
				default:
					break;
				}
			}
			else if (WSAGetLastError() == WSAETIMEDOUT) {		// recv がタイムアウトした
			}
		}
		++list_index;
	}
}

// ****************************************************************************************************
// chip 情報を全取得する
// ****************************************************************************************************
void UdplManagerImp::getChipInfo(const std::list<SOCKET_ELEM> &list, const UDPL_BOARD_INFO *pInfo, std::list<UDPL_CHIP_INFO_ENABLE> &infolist)
{
	infolist.clear();

	uint8_t send_buf[16];
	uint8_t *pslotno;
	uint8_t *pparam;
	uint8_t recv_buf[128];
	int ret;

	struct sockaddr_in6 dest_addr;		// 送信先アドレス
										// 送信先アドレス用意
	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.sin6_family = AF_INET6;
	dest_addr.sin6_port = htons(udpport);

	// コマンド用意
	memset(send_buf, '\0', sizeof(send_buf));
	memcpy(send_buf, COMMAND_READ_SETTING, sizeof(COMMAND_READ_SETTING));	// ヘッダ設定
	pslotno = send_buf + sizeof(COMMAND_READ_SETTING);	// スロット No 指定部分
	pparam = pslotno + 1;								// info 種類の指定部
	
	char param[] = { SETTING_CHIP_ENABLE, SETTING_CHIP_PLLEN, SETTING_CHIP_HWRESET, SETTING_CHIP_CLOCK, SETTING_CHIP_NAME, SETTING_CHIP_EXINFO };

	uint32_t list_index = 0;
	for (auto elem : list) {
		
		// ターゲットから情報を取得する
		inet_pton(dest_addr.sin6_family, elem.target_address.c_str(), &dest_addr.sin6_addr);	// 送信先セット

		for (uint32_t no = 0; no < pInfo[list_index].slotnum; ++no) {	// slot を 0 から調べていく

			UDPL_CHIP_INFO_ENABLE cinfo;
			memset(&cinfo, 0, sizeof(cinfo));

			strncpy_s(cinfo.info.board_address_ipv6, sizeof(cinfo.info.board_address_ipv6), elem.target_address.c_str(), elem.target_address.size());	// アドレスをセット
			cinfo.info.slotNo = no;

			*pslotno = SETTING_SLOT_0 + no;		// スロットNO をセット

			for (uint32_t i = 0; i < sizeof(param); ++i) {

				*pparam = param[i];		//コマンドセット

				ret = sendto(elem.sock, (char *)send_buf, send_buf[0] + 1, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
				ret = recvfrom(elem.sock, (char *)recv_buf, sizeof(recv_buf), 0, nullptr, nullptr);
				
				if ((ret != SOCKET_ERROR) && (recv_buf[1] == COMMAND_READ_SETTING[1]) && (recv_buf[2] >= SETTING_SLOT_0)) {		// 受信データあり
				
					switch (recv_buf[3]) {
					case SETTING_CHIP_ENABLE:
					{
						cinfo.enable = recv_buf[4] ? true : false;
						break;
					}
					case SETTING_CHIP_PLLEN:
					{
						cinfo.info.variable_clock = recv_buf[4] ? true : false;
						break;
					}
					case SETTING_CHIP_HWRESET:
					{
						cinfo.info.hardware_reset = recv_buf[4] ? true : false;
						break;
					}
					case SETTING_CHIP_CLOCK:
					{
						cinfo.info.clock = recv_buf[4] | recv_buf[5] << 8 | recv_buf[6] << 16 | recv_buf[7] << 24;
						break;
					}
					case SETTING_CHIP_NAME:
					{
						if (recv_buf[0] > 3) {
							memcpy_s(cinfo.info.name, sizeof(cinfo.info.name), recv_buf + 4, recv_buf[0] - 3);
						}
						break;
					}
					case SETTING_CHIP_EXINFO:
					{
						if (recv_buf[0] > 3) {
							memcpy_s(cinfo.info.extinfo, sizeof(cinfo.info.extinfo), recv_buf + 4, recv_buf[0] - 3);
						}
						break;
					}
					default:
						break;
					}
				}
				else if (WSAGetLastError() == WSAETIMEDOUT) {		// recv がタイムアウトした
				}
			}
			// 1 slot 分の情報を集め終わった
			infolist.push_back(cinfo);	// リストに突っ込む
		}
		// 1 target 分の情報を集め終わった
		++list_index;
	}
}

// ****************************************************************************************************
// enable の chip 情報だけにする
// ****************************************************************************************************
void UdplManagerImp::getValidChipInfo(std::list<UDPL_CHIP_INFO_ENABLE> &list)
{
	for (auto it = list.begin(); it != list.end(); ) {
		if (!it->enable) {
			it = list.erase(it);	// enable じゃなかったら消す
		}
		else {
			++it;
		}
	}
}

// ****************************************************************************************************
// list 型から配列にする
// ****************************************************************************************************
void UdplManagerImp::completeChipInfo(const std::list<UDPL_CHIP_INFO_ENABLE> &list, UDPL_CHIP_INFO *pInfo)
{
	uint32_t index = 0;
	for (auto elem : list) {
		memcpy(pInfo + index, &elem.info, sizeof(elem.info));
		++index;
	}
}
// ****************************************************************************************************
// UdplSocketIf を作成する
// ****************************************************************************************************
void UdplManagerImp::createSocketIf(const std::list<SOCKET_ELEM> &slist, UDPL_BOARD_INFO *pbinfo, UDPL_BOARD_INFO_EXT *pbinfoExt, const uint32_t binfo_size, std::vector<UdplSocketIf *> &siflist, std::list<IF_ADDR> &ifaddrlist)
{
	ifaddrlist.clear();
	IF_ADDR ifaddr_elem;
	UDPL_BOARD_INFO *pbtmp = pbinfo;
	UDPL_BOARD_INFO_EXT* pbtmpExt = pbinfoExt;
	for (auto elem : slist) {
		UdplSocketIf *psockif = new UdplSocketIf(elem.sock, elem.target_address.c_str(), pbtmp->wait, pbtmp->bufsize, pbtmpExt->hbcycle, pbtmpExt->blank);
		siflist.push_back(psockif);

		ifaddr_elem.address = elem.target_address;
		ifaddr_elem.psif = psockif;
		ifaddrlist.push_back(ifaddr_elem);

		++pbtmp;
		++pbtmpExt;
	}
}

//*****************************************************************************************************
// ファームウェアバージョンチェック
//*****************************************************************************************************
void UdplManagerImp::firm_check(void)
{
	for (uint32_t i = 0; i < board_num; ++i) {
		if (pBoardInfoList[i].version < REQ_FIRM_VER) {
			std::string str;
			str = "[";
			str += pBoardInfoList[i].board_address_ipv6;
			str += "] のボードのファームウェアバージョンを ";
			str += std::to_string((REQ_FIRM_VER >> 24) & 0xff) + ".";
			str += std::to_string((REQ_FIRM_VER >> 16) & 0xff) + ".";
			str += std::to_string((REQ_FIRM_VER >> 8) & 0xff) + " にアップデートしてください";
			MessageBoxA(NULL, str.c_str(), "firmware warning", MB_OK);
		}
	}
}

// ****************************************************************************************************
// UdplChip を作成する
// ****************************************************************************************************
void UdplManagerImp::createUdplChip(const std::list<IF_ADDR> &ifaddrlist, UDPL_CHIP_INFO *pcinfo, const uint32_t cinfo_num, std::vector<UdplChipImp *> &uclist)
{
	UdplChipImp *puc;

	uint32_t cindex = 0;
	std::vector<uint32_t> chipNoList;
	
	UDPL_CHIP_INFO *pinfotmp = pcinfo;
	std::list<UDPL_CHIP_INFO*> cinfolist;
	for (uint32_t i = cinfo_num; i != 0; --i) {
		cinfolist.push_back(pinfotmp++);
	}

	for (auto ifelem : ifaddrlist) {
		chipNoList.clear();
		for (auto it = cinfolist.begin(); it != cinfolist.end();) {
			if (ifelem.address == (*it)->board_address_ipv6) {
				if (strcmp((*it)->name, CHIP_YM2608) == 0) {
					puc = new UdplChipYM2608(ifelem.psif, *it);	// YM2608
				}
				else if (strcmp((*it)->name, CHIP_YM2151) == 0) {
					puc = new UdplChipYM2151(ifelem.psif, *it);	// YM2151
				}
				else if (strcmp((*it)->name, CHIP_YM2612) == 0 || strcmp((*it)->name, CHIP_YM3438) == 0) {
					puc = new UdplChipYM2612(ifelem.psif, *it);	// YM2612, 3438
				}
				else if (strcmp((*it)->name, CHIP_YM2203) == 0) {
					puc = new UdplChipYM2203(ifelem.psif, *it);	// YM2203
				}
				else if (strcmp((*it)->name, CHIP_YM3812) == 0 || strcmp((*it)->name, CHIP_YM3526) == 0) {
					puc = new UdplChipYM3812(ifelem.psif, *it);	// YM3812, 3526
				}
				else if (strcmp((*it)->name, CHIP_Y8950) == 0 ) {
					puc = new UdplChipY8950(ifelem.psif, *it);	// Y8950
				}
				else if (strcmp((*it)->name, CHIP_SN76489) == 0) {
					puc = new UdplChipSN76489A(ifelem.psif, *it);	// SN76489
				}
				else if (strcmp((*it)->name, CHIP_SCC) == 0) {
					puc = new UdplChipSCC(ifelem.psif, *it);	// SCC
				}
				else if (strcmp((*it)->name, CHIP_SCCP) == 0) {
					puc = new UdplChipSCCP(ifelem.psif, *it);	// SCC+
				}
				else if (strcmp((*it)->name, CHIP_YM2610_H) == 0 || strcmp((*it)->name, CHIP_YM2610B_H) == 0) {
					puc = new UdplChipYM2610_H(ifelem.psif, *it);	// YM2610+FT232H, YM2610B+FT232H
				}
				else if (strcmp((*it)->name, CHIP_AY8910) == 0 || strcmp((*it)->name, CHIP_YM2149) == 0) {
					puc = new UdplChipPSG(ifelem.psif, *it);	// AY8910, YM2149
				}
				else if (strcmp((*it)->name, CHIP_YM2413) == 0) {
					puc = new UdplChipYM2413(ifelem.psif, *it);	// YM2413
				}
				else if (strcmp((*it)->name, CHIP_YMF262) == 0) {
					puc = new UdplChipYMF262(ifelem.psif, *it);	// YMF262
				}
				else {
					puc = new UdplChipUNKNOWN(ifelem.psif, *it);	// デフォルト
				}

				chipNoList.push_back(cindex++);
				uclist.push_back(puc);

				it = cinfolist.erase(it);
			}
			else {
				++it;
			}
		}
		// chipNoList 完成
		ip2chipNoList_map.insert(std::make_pair(ifelem.address, chipNoList));
	}
}

// ****************************************************************************************************
// UdplChip を解放する
// ****************************************************************************************************
void UdplManagerImp::releaseUdplChip(std::vector<UdplChipImp *> &uclist)
{
	for (auto &elem : uclist) {
		if (strcmp(elem->getChipName(), CHIP_YM2608) == 0) {
			delete dynamic_cast<UdplChipYM2608 *>(elem);		// YM2608
		}
		else if(strcmp(elem->getChipName(), CHIP_YM2151) == 0) {
			delete dynamic_cast<UdplChipYM2151 *>(elem);		// YM2151
		}
		else if (strcmp(elem->getChipName(), CHIP_YM2612) == 0 || strcmp(elem->getChipName(), CHIP_YM3438) == 0) {
			delete dynamic_cast<UdplChipYM2612 *>(elem);		// YM2612, 3438
		}
		else if (strcmp(elem->getChipName(), CHIP_YM2203) == 0) {
			delete dynamic_cast<UdplChipYM2203 *>(elem);		// YM2203
		}
		else if (strcmp(elem->getChipName(), CHIP_YM3812) == 0 || strcmp(elem->getChipName(), CHIP_YM3526) == 0) {
			delete dynamic_cast<UdplChipYM3812 *>(elem);		// YM3812, 3526
		}
		else if (strcmp(elem->getChipName(), CHIP_Y8950) == 0 ) {
			delete dynamic_cast<UdplChipY8950 *>(elem);			// Y8950
		}
		else if (strcmp(elem->getChipName(), CHIP_SN76489) == 0) {
			delete dynamic_cast<UdplChipSN76489A *>(elem);		// SN76489
		}
		else if (strcmp(elem->getChipName(), CHIP_SCC) == 0) {
			delete dynamic_cast<UdplChipSCC *>(elem);			// SCC
		}
		else if (strcmp(elem->getChipName(), CHIP_SCCP) == 0) {
			delete dynamic_cast<UdplChipSCCP*>(elem);			// SCC+
		}
		else if (strcmp(elem->getChipName(), CHIP_YM2610_H) == 0 || strcmp(elem->getChipName(), CHIP_YM2610B_H) == 0) {
			delete dynamic_cast<UdplChipYM2610_H *>(elem);		// YM2610+FT232H, YM2610B+FT232H
		}
		else if (strcmp(elem->getChipName(), CHIP_AY8910) == 0 || strcmp(elem->getChipName(), CHIP_YM2149) == 0) {
			delete dynamic_cast<UdplChipPSG *>(elem);		// AY8910, YM2149
		}
		else if (strcmp(elem->getChipName(), CHIP_YM2413) == 0) {
			delete dynamic_cast<UdplChipYM2413*>(elem);		// YM2413
		}
		else if (strcmp(elem->getChipName(), CHIP_YMF262) == 0) {
			delete dynamic_cast<UdplChipYMF262*>(elem);		// YMF262
		}
		else {
			delete dynamic_cast<UdplChipUNKNOWN *>(elem);		// デフォルト
		}

		elem = nullptr;
	}
}
