#include "udpl_dll_imp.h"
#include <Windows.h>
#include <Shlwapi.h>
#include <io.h>
#include <string.h>
#include <CommCtrl.h>

#include "tstring.h"
#include "resource.h"

#include "tray_config.h"
#include "i2c_list.h"

extern HINSTANCE dllInstance;

// ***************************************
// タスクトレイ用コールバック
// ***************************************
LRESULT CALLBACK UdplManagerImp::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	UdplManagerImp *instance = (UdplManagerImp*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
	if (instance) {
		return instance->localWndProc(hWnd, msg, wParam, lParam);
	}
	return DefWindowProc(hWnd, msg, wParam, lParam);
}

// ************************************************
// クラスインスタンスのタスクトレイ用コールバック
// ************************************************
LRESULT UdplManagerImp::localWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {

	UINT icon_menu_ID = (UINT)wParam;	// == uid
	UINT mouseMsg = (UINT)lParam;		// mouse message
	TCHAR ipv4[INET_ADDRSTRLEN];
	std::tstring str;
	HMENU menu;
	POINT pt;
	UINT index;

	TCHAR exepath[MAX_PATH];
	DWORD exelen = _countof(exepath);

	bool ret = false;
	MENUITEMINFO minfo;

	switch (msg) {

	case WM_UDPL:
		
		switch (mouseMsg) {
		case WM_RBUTTONUP:
			SetForegroundWindow(hWnd);	// フォアグラウンドにしないとメニューがちゃんと消えない
			if (icon_menu_ID < board_num) {
				cur_iconID = icon_menu_ID;
			}
			GetCursorPos(&pt);
			menu = LoadMenu((HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), MAKEINTRESOURCE(IDR_MENU1));

			if (!vol_out_valid[cur_iconID]) {	// potent out が繋がってない
				minfo.cbSize = sizeof(MENUITEMINFO);
				minfo.fMask = MIIM_STATE;
				minfo.fState = MFS_DISABLED;
				SetMenuItemInfo(menu, ID_UDPLMENU_40004, FALSE, &minfo);	// メニュー無効化
			}

			index = TrackPopupMenu(GetSubMenu(menu, 0), TPM_LEFTALIGN | TPM_RETURNCMD, pt.x, pt.y, 0, hWnd, NULL);
			DestroyMenu(menu);
			switch (index) {
			case ID_UDPLMENU_40001:		// ボード情報
				if (infotray_m[cur_iconID].try_lock()) {	// lockできる -> ボード情報を表示していない. ボード情報は同時に1つだけ表示可能にする
					DialogBoxParam((HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), MAKEINTRESOURCE(IDD_DIALOG3), hWnd, InfoDlgProc, (LPARAM)this);
				} else {
					SetForegroundWindow(infotray_h[cur_iconID]);
				}
				break;

			case ID_UDPLMENU_40002:		// 遅延設定
				DialogBoxParam((HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DlgProc, (LPARAM)this);
				break;
			
			case ID_UDPLMENU_40004:		// ボリューム設定
				DialogBoxParam((HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), MAKEINTRESOURCE(IDD_DIALOG2), hWnd, VolDlgProc, (LPARAM)this);
				break;

			case ID_UDPLMENU_40003:		// web を開く
				
				memset(ipv4, '\0', sizeof(ipv4));
				char2tchar(ipv4, pBoardInfoList[cur_iconID].board_address_ipv4, _countof(ipv4));
				str = _T("http://");
				str += ipv4;
				str += _T("/");

				if (_taccess(firefoxpath, 0) == 0) {	// firefox が存在する

					ShellExecute(NULL, NULL, firefoxpath, str.c_str(), NULL, SW_SHOWNORMAL);

				} else if(AssocQueryString(ASSOCF_IS_PROTOCOL, ASSOCSTR_EXECUTABLE, L"http", NULL, exepath, &exelen) == S_OK) {

					if (_tcsstr(exepath, _T("chrome.exe")) != NULL) {	// http のデフォルトブラウザが chrome だ
						MessageBox(NULL, _T("chrome では正しく動作しませんが、起動します"), _T("Warning"), MB_OK);	// 警告する
					}
					ShellExecute(NULL, _T("open"), str.c_str(), NULL, NULL, SW_SHOWNORMAL);

				} else {	// 関連付けがない？
					ShellExecute(NULL, _T("open"), str.c_str(), NULL, NULL, SW_SHOWNORMAL);	// 一応実行する
				}
				break;
			
			default:
				break;
			}
			break;

		case WM_LBUTTONUP:
			if (icon_menu_ID < board_num) {
				cur_iconID = icon_menu_ID;
			}
			if (vol_out_valid[cur_iconID]) {	// potent out が繋がってる
				DialogBoxParam((HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), MAKEINTRESOURCE(IDD_DIALOG2), hWnd, VolDlgProc, (LPARAM)this);
			}
			break;

		default:
			break;
		}
		break;

	//case WM_COMMAND:
	//	switch (icon_menu_ID) {
	//	case ID_UDPLMENU_40001:
	//		getBoadInfo(str, cur_iconID);
	//		MessageBox(hWnd, str.c_str(), _T("Udpl Board Info"), MB_OK | MB_SETFOREGROUND);
	//		break;
	//	case ID_UDPLMENU_40002:
	//		// DialogBox 関数を使うと、dialog の hwnd をここで取得できない -> ちゃんと createwindows 使ったほうがいいだろう
	//		DialogBoxParam(dllInstance, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DlgProc, (LPARAM)this);
	//		break;
	//	case ID_UDPLMENU_40003:
	//		memset(ipv4, '\0', sizeof(ipv4));
	//		char2tchar(ipv4, pBoardInfoList[cur_iconID].board_address_ipv4, _countof(ipv4));
	//		str = _T("http://");
	//		str += ipv4;
	//		str += _T("/");
	//		ShellExecute(NULL, _T("open"), str.c_str(), NULL, NULL, SW_SHOWNORMAL);
	//		break;
	//	default:
	//		break;
	//	}
	//	break;
		
	case WM_CLOSE:
		DestroyWindow(hWnd);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, msg, wParam, lParam);
	}

	return 0L;
}

// ***************************************
// 遅延設定ダイアログ用コールバック
// ***************************************
INT_PTR CALLBACK UdplManagerImp::DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	UdplManagerImp *instance = (UdplManagerImp*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
	if (instance) {
		return instance->localDlgProc(hWnd, msg, wParam, lParam);
	}
	else if (msg == WM_INITDIALOG) {	// dialobboxparam の param から this ポインタを受け取って dialog ハンドルのユーザーデータに突っ込んでおく
		SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)lParam);
		return ((UdplManagerImp*)lParam)->localDlgProc(hWnd, msg, wParam, lParam);
	}
	return FALSE;
}


// ************************************************
// クラスインスタンスの遅延設定ダイアログ用コールバック
// ************************************************
INT_PTR UdplManagerImp::localDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	TCHAR str[256];
	uint32_t wait;

	POINT pt;
	RECT rect;
	RECT rectrt;

	switch (msg) {
	case WM_INITDIALOG:
		// edit ボックスに初期値を入れる
		SetDlgItemText(hWnd, IDC_EDIT1, std::to_tstring(pBoardInfoList[cur_iconID].wait).c_str());

		GetCursorPos(&pt);
		GetWindowRect(hWnd, &rect);	// ウインドウの座標
		SystemParametersInfo(SPI_GETWORKAREA, 0, &rectrt, 0);	// タスクバーを除くテスクトップ領域

		// とりあえずタスクバーが上にある場合と下にある場合だけ
		if ((pt.y + rect.bottom - rect.top) > rectrt.bottom) {	// 下の方にはみ出る
			pt.y = rectrt.bottom - rect.bottom + rect.top;		// 上にずらす
		} else if (pt.y < rectrt.top) {							// 上にはみ出る
			pt.y = rectrt.top;									// 下にずらす
		}
		SetWindowPos(hWnd, NULL, pt.x - (rect.right - rect.left) / 2, pt.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_SHOWWINDOW);

		SetFocus(hWnd);
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDOK:
			memset(str, '\0', sizeof(str));
			GetDlgItemText(hWnd, IDC_EDIT1, str, _countof(str));
			wait = (uint32_t)_tstoi(str);
			pBoardInfoList[cur_iconID].wait = wait;
			ifList[cur_iconID]->setWait(wait);
			EndDialog(hWnd, IDOK);
			break;
		case IDCANCEL:
			EndDialog(hWnd, IDCANCEL);
			break;
		default:
			return FALSE;
		}
		break;
	default:
		return FALSE;
	}
	return TRUE;
}

// メモリ位置配列
int db_array[] = {
  0,
  -1,
  -2,
  -3,
  -4,
  -5,
  -6,
  -7,
  -8,
  -9,
  -10,
  -11,
  -12,
  -14,
  -16,
  -18,
  -20,
  -22,
  -24,
  -26,
  -28,
  -30,
  -32,
  -34,
  -36,
  -39,
  -42,
  -45,
  -48,
  -51,
  -54,
  -57,
  -60,
  -80
};

// ***************************************
// ボリューム設定ダイアログ用コールバック
// ***************************************
INT_PTR CALLBACK UdplManagerImp::VolDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	UdplManagerImp* instance = (UdplManagerImp*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
	if (instance) {
		return instance->localVolDlgProc(hWnd, msg, wParam, lParam);
	} else if (msg == WM_INITDIALOG) {	// dialobboxparam の param から this ポインタを受け取って dialog ハンドルのユーザーデータに突っ込んでおく
		SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)lParam);
		return ((UdplManagerImp*)lParam)->localVolDlgProc(hWnd, msg, wParam, lParam);
	}
	return FALSE;
}

// ************************************************
// クラスインスタンスのボリューム設定ダイアログ用コールバック
// ************************************************
INT_PTR UdplManagerImp::localVolDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	HWND hBar;
	hBar = GetDlgItem(hWnd, IDC_SLIDER1);

	POINT pt;
	RECT rect;
	RECT rectrt;

	std::chrono::steady_clock::time_point cur_timing;
	uint32_t set_vol = 0;	// スライドの初期値
	int pos = 0;

	switch (msg) {
	case WM_INITDIALOG:

		SendMessage(hBar, TBM_SETRANGE, TRUE, MAKELPARAM(0, _countof(db_array)-1));	// レンジの設定 小→大
		
		SendMessage(hBar, TBM_SETTICFREQ, 3, 0);
																										
		SendMessage(hBar, TBM_SETLINESIZE, 0, 1);	// ラインサイズ設定
		SendMessage(hBar, TBM_SETPAGESIZE, 0, 3);	// ページサイズ設定

		set_vol = vol_out_value[cur_iconID];		// ボリューム設定値
		pos = (_countof(db_array) - 1) - set_vol;	// スライダーの位置
		if (pos < 0) {
			pos = 0;
		} else if (pos > _countof(db_array) - 1) {
			pos = _countof(db_array) - 1;
		}
		SendMessage(hBar, TBM_SETPOS, TRUE, pos);	// 位置の設定
		SetDlgItemText(hWnd, IDC_STATIC3, std::to_tstring(db_array[set_vol]).c_str());
		
		GetCursorPos(&pt);
		GetWindowRect(hWnd, &rect);	// ウインドウの座標
		SystemParametersInfo(SPI_GETWORKAREA, 0, &rectrt, 0);	// タスクバーを除くテスクトップ領域

		// とりあえずタスクバーが上にある場合と下にある場合だけ
		if ((pt.y + rect.bottom - rect.top) > rectrt.bottom) {	// 下の方にはみ出る
			pt.y = rectrt.bottom - rect.bottom + rect.top;		// 上にずらす
		} else if (pt.y < rectrt.top) {							// 上にはみ出る
			pt.y = rectrt.top;									// 下にずらす
		}
		SetWindowPos(hWnd, NULL, pt.x - (rect.right - rect.left)/2, pt.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_SHOWWINDOW);

		SetFocus(hWnd);
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDOK:
			EndDialog(hWnd, IDOK);
			break;
		default:
			return FALSE;
		}
		break;

	case WM_HSCROLL:
		if (GetDlgItem(hWnd, IDC_SLIDER1) == (HWND)lParam) {
			pos = SendMessage(hBar, TBM_GETPOS, NULL, NULL);	// 現在の位置取得
			set_vol = (_countof(db_array) - 1) - pos;			// ボリュームに変換
			if (set_vol > _countof(db_array) - 1) {		// 最大値を超えないようにする
				set_vol = _countof(db_array) - 1;
			}
			vol_out_value[cur_iconID] = set_vol;	// 新しいボリュームを覚えておく

			SetDlgItemText(hWnd, IDC_STATIC3, std::to_tstring(db_array[set_vol]).c_str());

			//cur_timing = std::chrono::steady_clock::now();
			//auto dur = std::chrono::duration_cast<std::chrono::milliseconds>(cur_timing - pre_vol_out_timing[cur_iconID]).count();	// 前回更新してから経過した時間[ms]
			//if (dur > VOL_UPDATE_PERIOD) {	// 指定時間以上過ぎていたら
			//	setVol(pBoardInfoList[cur_iconID].board_address_ipv6, set_vol);		// ボリュームセット
			//	pre_vol_out_timing[cur_iconID] = cur_timing;	// 更新時間の更新
			//}
		
			std::this_thread::sleep_until(pre_vol_out_timing[cur_iconID] + std::chrono::milliseconds(VOL_UPDATE_PERIOD));	// 時間を待つ
			setVol(pBoardInfoList[cur_iconID].board_address_ipv6, set_vol);		// ボリュームセット
			pre_vol_out_timing[cur_iconID] = std::chrono::steady_clock::now();	// 更新時間の更新
		}
		break;

	case WM_CLOSE:
		EndDialog(hWnd, IDOK);
		break;

	default:
		return FALSE;
	}

	return TRUE;
}


// ***************************************
// 情報表示ダイアログ用コールバック
// ***************************************
INT_PTR CALLBACK UdplManagerImp::InfoDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	UdplManagerImp* instance = (UdplManagerImp*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
	if (instance) {
		return instance->localInfoDlgProc(hWnd, msg, wParam, lParam);
	} else if (msg == WM_INITDIALOG) {	// dialobboxparam の param から this ポインタを受け取って dialog ハンドルのユーザーデータに突っ込んでおく
		SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)lParam);
		return ((UdplManagerImp*)lParam)->localInfoDlgProc(hWnd, msg, wParam, lParam);
	}
	return FALSE;
}


// ************************************************
// クラスインスタンスの情報表示ダイアログ用コールバック
// ************************************************
INT_PTR UdplManagerImp::localInfoDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	POINT pt;
	RECT rect;
	RECT rectrt;

	std::vector<uint32_t>& chipNoList = ip2chipNoList_map.at(pBoardInfoList[cur_iconID].board_address_ipv6);	// chip no のリストを得る
	std::string str;
	char tmp[64] = { '\0' };

	unsigned int slotinfo[] = { IDC_STATIC_INFO0, IDC_STATIC_INFO1, IDC_STATIC_INFO2, IDC_STATIC_INFO3 };
	unsigned int slotuse[]  = { IDC_STATIC_USE0, IDC_STATIC_USE1, IDC_STATIC_USE2, IDC_STATIC_USE3 };
	unsigned int slotmax = (pBoardInfoList[cur_iconID].slotnum > _countof(slotinfo)) ? _countof(slotinfo) : pBoardInfoList[cur_iconID].slotnum;	// とりあえず slot は4まで表示
	
	switch (msg) {
	case WM_INITDIALOG:
		if (cur_iconID < board_num) {

			for (uint32_t i = 0; i < pBoardInfoList[cur_iconID].slotnum; ++i) {
				curColor[cur_iconID][i] = RGB(0, 0, 0);
			}
			
			infotray_h[cur_iconID] = hWnd;	// 表示しているハンドルを覚えておく. mutex unlock用

			// ボードネーム
			str = "[";
			str += pBoardInfoList[cur_iconID].name;
			str += "]";
			SetDlgItemTextA(hWnd, IDC_STATIC_NAME, str.c_str());
			// IPv6
			SetDlgItemTextA(hWnd, IDC_STATIC_IPV6, pBoardInfoList[cur_iconID].board_address_ipv6);
			// IPv4
			SetDlgItemTextA(hWnd, IDC_STATIC_IPV4, pBoardInfoList[cur_iconID].board_address_ipv4);
			// chip ネームとクロックと払い出し状況
			for (unsigned int slot = 0; slot < slotmax; ++slot) {
				for (auto cno : chipNoList) {
					if (pChipInfoList[cno].slotNo == slot) {
						str = pChipInfoList[cno].name;
						str += " / ";
						sprintf_s(tmp, sizeof(tmp), "%.3f", (float)(pChipInfoList[cno].clock) / 1000000.0f);
						str += tmp;
						str += "MHz";
						
						// slot 情報
						SetDlgItemTextA(hWnd, slotinfo[slot], str.c_str());
						
						if (cpList[cno]->getIsUsed()) {
							SetDlgItemTextA(hWnd, slotuse[slot], "o");			// アプリに払い出していると o、払い出してないものは -
						}
						break;
					}
				}
			}
			if (infotray_th[cur_iconID].joinable()) {
				infotray_th[cur_iconID].join();
			}
			infotray_th[cur_iconID] = std::thread(&UdplManagerImp::updateInfoDlg, this, hWnd, cur_iconID);
		}

		GetCursorPos(&pt);
		GetWindowRect(hWnd, &rect);	// ウインドウの座標
		SystemParametersInfo(SPI_GETWORKAREA, 0, &rectrt, 0);	// タスクバーを除くテスクトップ領域

		// とりあえずタスクバーが上にある場合と下にある場合だけ
		if ((pt.y + rect.bottom - rect.top) > rectrt.bottom) {	// 下の方にはみ出る
			pt.y = rectrt.bottom - rect.bottom + rect.top;		// 上にずらす
		} else if (pt.y < rectrt.top) {							// 上にはみ出る
			pt.y = rectrt.top;									// 下にずらす
		}
		SetWindowPos(hWnd, NULL, pt.x - (rect.right - rect.left) / 2, pt.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_SHOWWINDOW);

		SetFocus(hWnd);
		break;

	case WM_CTLCOLORSTATIC:		// static の色変更
		for (uint32_t bno = 0; bno < board_num; ++bno) {
			if (infotray_h[bno] == hWnd) {	// ハンドル情報を元に board no. を探す
				for (uint32_t sno = 0; sno < _countof(slotinfo); ++sno) {
					if (GetDlgCtrlID((HWND)lParam) == slotinfo[sno]) {
						SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE));
						SetTextColor((HDC)wParam, curColor[bno][sno]);
						return (BOOL)GetStockObject(NULL_BRUSH);
					}
				}
			}
		}
		return FALSE;
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDOK:
			for (uint32_t i = 0; i < board_num; ++i) {
				if (infotray_h[i] == hWnd) {	// ハンドル情報を元に board no. を探す
					infotray_m[i].unlock();		// mutex を unlock
					infotray_h[i] = nullptr;
					infotray_on[i].store(false, std::memory_order_release);
					break;
				}
			}
			EndDialog(hWnd, IDOK);
			break;
		default:
			return FALSE;
		}
		break;

	case WM_CLOSE:
		for (uint32_t i = 0; i < board_num; ++i) {
			if (infotray_h[i] == hWnd) {	// ハンドル情報を元に board no. を探す
				infotray_m[i].unlock();		// mutex を unlock
				infotray_h[i] = nullptr;
				infotray_on[i].store(false, std::memory_order_release);
				break;
			}
		}
		EndDialog(hWnd, IDOK);
		break;

	default:
		return FALSE;
	}
	return TRUE;
}


// *****************************************************************************************************
// タスクトレイアイコン用初期化
// *****************************************************************************************************
void UdplManagerImp::setTasktrayIcon(void)
{
	// 電子ボリューム検知, 現在値取得, 設定時刻
	if (board_num) {

		vol_out_valid = new bool[board_num];
		vol_out_value = new uint32_t[board_num];

		pre_vol_out_timing = new std::chrono::steady_clock::time_point[board_num];

		for (uint32_t i = 0; i < board_num; ++i) {
			vol_out_valid[i] = getVol(pBoardInfoList[i].board_address_ipv6, (vol_out_value + i), LEFT_CH);
			pre_vol_out_timing[i] = std::chrono::steady_clock::now();
		}

		infotray_m = new std::mutex[board_num];
		infotray_h = new HWND[board_num];
		memset(infotray_h, 0, sizeof(HWND) * board_num);
		infotray_th = new std::thread[board_num];
		infotray_on = new std::atomic_bool[board_num];
		curColor = new COLORREF *[board_num];
		memset(curColor, 0, sizeof(COLORREF*) * board_num);
		for (uint32_t i = 0; i < board_num; ++i) {
			curColor[i] = new COLORREF[pBoardInfoList[i].slotnum];
		}
	}
	
	InitCommonControls();	// コモンコントロール初期化

	// メッセージ処理のスレッドを生成
	msgth = std::thread(&UdplManagerImp::msgloop, this);
	std::this_thread::sleep_for(std::chrono::milliseconds(10));  // とりあえずアイコンが登録されるのを待つ
	
	modicon.store(true, std::memory_order_release);
	iconth = std::thread(&UdplManagerImp::updateTasktrayIcon, this);
}

// *******************************************************
// タスクトレイアイコン用終了処理
// *******************************************************
void UdplManagerImp::delTasktrayIcon(void)
{
	// アイコン更新スレッドを終了する
	modicon.store(false, std::memory_order_release);
	if (iconth.joinable()) {
		iconth.join();
	}

	// アイコン削除
	for (unsigned int i = 0; i < board_num; ++i) {
		Shell_NotifyIcon(NIM_DELETE, pnid + i);
	}

	// 情報ダイアログを閉じる
	for (uint32_t i = 0; i < board_num; ++i) {
		if (infotray_h[i] != nullptr) {
			PostMessage(infotray_h[i], WM_CLOSE, 0, 0);	// close する
		}
	}

	// 隠しウインドウを終了させる
	PostMessage(hidWin, WM_CLOSE, 0, 0);	// close する
	
	// メッセージ処理スレッドの終了を待つ
	if (msgth.joinable()) {
		msgth.join();
	}

	// NOTIFYICONDATA を開放
	if (pnid) {
		delete[] pnid;
		pnid = nullptr;
	}

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

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

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

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

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

	if (infotray_on) {
		for (uint32_t i = 0; i < board_num; ++i) {
			infotray_on[i].store(false, std::memory_order_release);
		}
	}
	if (infotray_th) {
		for (uint32_t i = 0; i < board_num; ++i) {
			if (infotray_th[i].joinable()) {
				infotray_th[i].join();
			}
		}
		delete[] infotray_th;
		infotray_th = nullptr;
	}

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

	if (curColor) {
		for (uint32_t i = 0; i < board_num; ++i) {
			if (curColor[i]) {
				delete[] curColor[i];
				curColor[i] = nullptr;
			}
		}
		delete[] curColor;
		curColor = nullptr;
	}
}

// *******************************************************
// タスクトレイアイコンの更新
// *******************************************************
void UdplManagerImp::updateTasktrayIcon(void)
{
	bool* stat = nullptr;
	unsigned int* activeNo = nullptr;
	bool ret;

	if (board_num) {
		stat = new bool[board_num];
		for (unsigned int i = 0; i < board_num; ++i) {
			stat[i] = false;
		}
		activeNo = new unsigned int[board_num];
		memset(activeNo, 0, sizeof(unsigned int) * board_num);
	}


	do {
		std::this_thread::sleep_for(std::chrono::milliseconds(TRAY_SCAN));

		for (unsigned int i = 0; i < board_num; ++i) {
			ret = ifList[i]->getSendStatus();
			if (ret) {	// 送信中
				if (ret != stat[i]) {	// 送信停止 -> 送信中
					activeNo[i] = 0;
				} else {	// 送信中が続いている
					activeNo[i] = !activeNo[i];
				}
				pnid[i].hIcon = hIconActive[activeNo[i]];
				Shell_NotifyIcon(NIM_MODIFY, pnid + i);
			}
			else {	// idle
				if (ret != stat[i]) {	// active -> idle
					pnid[i].hIcon = hIconIdle;
					Shell_NotifyIcon(NIM_MODIFY, pnid + i);
				}
			}
			stat[i] = ret;
		}
	} while (modicon.load(std::memory_order_acquire));

	if (stat) {
		delete[] stat;
	}
	if (activeNo) {
		delete[] activeNo;
	}
}

// *******************************************************
// 情報ダイアログの更新
// *******************************************************
void UdplManagerImp::updateInfoDlg(HWND hwnd, uint32_t no)
{
	if (no < board_num) {

		infotray_on[no].store(true, std::memory_order_release);		// ループ ON
		std::vector<uint32_t>& chipNoList = ip2chipNoList_map.at(pBoardInfoList[no].board_address_ipv6);	// chip no のリストを得る
		bool *preStat = new bool[pBoardInfoList[no].slotnum];
		for (uint32_t i = 0; i < pBoardInfoList[no].slotnum; ++i) {
			preStat[i] = false;
		}

		do {
			for (auto cno : chipNoList) {
				bool stat = cpList[cno]->getSendStatus();
				uint32_t slotno = cpList[cno]->getSlotNo();
				if (preStat[slotno] != stat) {	// 状態変化
					preStat[slotno] = stat;
					if (stat) {	// 書き込んでる
						curColor[no][slotno] = RGB(255, 0, 0);
					} else {	// 書き込んでない
						curColor[no][slotno] = RGB(0, 0, 0);
					}
				}
			}
			InvalidateRect(hwnd, NULL, FALSE);
			UpdateWindow(hwnd);

			std::this_thread::sleep_for(std::chrono::milliseconds(TRAY_SCAN));

		} while (infotray_on[no].load(std::memory_order_acquire));

		delete[] preStat;
	}
	
}

// *******************************************
// タスクトレイアイコンの表示 & メッセージ処理
// *******************************************
void UdplManagerImp::msgloop(void)
{
	//
	// タスクトレイアイコンの表示
	//
	TCHAR classname[] = _T("udpltray");

	WNDCLASS wc = { 0 };
	wc.lpfnWndProc = UdplManagerImp::WndProc;
	wc.hInstance = dllInstance;
	wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
	wc.lpszClassName = classname;

	if (!RegisterClass(&wc)) return;

	// 隠しウインドウの作成
	hidWin = CreateWindow(
		classname,
		_T("UdplTrayWnd"),
		WS_OVERLAPPED,
		0,
		0,
		0,
		0,
		NULL,
		NULL,
		dllInstance,
		this);

	SetWindowLongPtr(hidWin, GWLP_USERDATA, (LONG_PTR)this);

	// icon ロード
	hIconIdle = (HICON)LoadImage(dllInstance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE);
	hIconActive[0] = (HICON)LoadImage(dllInstance, MAKEINTRESOURCE(IDI_ICON2), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE);
	hIconActive[1] = (HICON)LoadImage(dllInstance, MAKEINTRESOURCE(IDI_ICON3), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE);

	if (board_num) {
		pnid = new NOTIFYICONDATA[board_num];
		memset(pnid, 0, sizeof(NOTIFYICONDATA) * board_num);
	}

	// タスクトレイアイコンを置く
	TCHAR ipv4[INET_ADDRSTRLEN];

	for (unsigned int i = 0; i < board_num; ++i) {
		pnid[i].cbSize = sizeof(NOTIFYICONDATA);
		pnid[i].uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
		pnid[i].hWnd = hidWin;
		pnid[i].uCallbackMessage = WM_UDPL;
		pnid[i].hIcon = hIconIdle;
		pnid[i].uID = i;
		memset(pnid[i].szTip, '\0', sizeof(pnid[i].szTip));
		memset(ipv4, '\0', sizeof(ipv4));
		char2tchar(ipv4, pBoardInfoList[i].board_address_ipv4, _countof(ipv4));
		_tcscpy_s(pnid[i].szTip, _countof(pnid[i].szTip), _T("Udpl ["));
		_tcscat_s(pnid[i].szTip, _countof(pnid[i].szTip), ipv4);
		_tcscat_s(pnid[i].szTip, _countof(pnid[i].szTip), _T("]"));
		Shell_NotifyIcon(NIM_ADD, pnid + i);
	}

	MSG msg;

	while (GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}