// MainFrm.cpp : implementation of the CMainFrame class
//

#include "stdafx.h"

#include "MainFrm.h"

#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <exception>

#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>

#include "etradeclient/boost_patch/property_tree/json_parser.hpp" // WARNIING! Make sure to include our patched version.
#include "etradeclient/mfc_ui/ETradeClient.h"
#include "etradeclient/mfc_ui/LoginDialog.h"
#include "etradeclient/mfc_ui/PopupBrowserDlgView.h"
#include "etradeclient/mfc_ui/ConfigDialog.h"

#include "etradeclient/browser/session.h"
#include "etradeclient/browser/browser_util.h"
#include "etradeclient/utility/logging.h"
#include "etradeclient/utility/application_config.h"
#include "etradeclient/utility/win_msg_define.h"
#include "etradeclient/utility/url_config.h"
#include "etradeclient/utility/string_converter.h"

#include "windows.h"
#include "TimeAPI.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

namespace fs = boost::filesystem;

namespace
{
	static const uint32_t MENU_ICON_WIDTH = 32, MENU_ICON_HEIGHT = 32;
	static const uint32_t TOOLBAR_ICON_WIDTH = 32, TOOLBAR_ICON_HEIGHT = 32;

	static const std::string JSON_TAG_CODE = "code";
	static const std::string JSON_TAG_MSG = "message";
	static const std::string JSON_VAL_SUCC = "success";

	enum ToolBarBtnID // Cannot use enum class due to TBBUTTON asks for integer.
	{
		RELOAD			= 4000,
		USER_MESSAGE	= 4001,
		USER_ACCOUNT	= 4002,
	};

	// Use relative path.
	static const std::wstring TOOLBAR_ICON_FOLDER(L".\\Resource\\Toolbar\\"); // Use 'wstring' because MFC ask for this.
	static const std::wstring MENU_ICON_FOLDER = L".\\Resource\\Menu\\";

	static std::vector<TBBUTTON> kQuickAccessBtn =
	{
		{ 0, ID_ISSUE_MASTER_CARD, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 },
		{ 1, ID_ISSUE_SLAVE_CARD, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 },
		{ 2, ID_ISSUE_ANONYMOUS_CARD, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 },
		{ 3, ID_CASH_RECHARGE, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 },
		{ 4, ID_CASH_WITHDRAW, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 },
		{ 5, ID_SETTLE_ACCOUNTS_APPLY, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 }
	};
} // namespace

// CMainFrame

IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
	ON_WM_CREATE()
	ON_WM_SETFOCUS()
	ON_WM_CLOSE()
	ON_WM_MEASUREITEM()
	ON_WM_DRAWITEM()
	ON_WM_INITMENUPOPUP()

	ON_NOTIFY(TBN_DROPDOWN, AFX_IDW_TOOLBAR, OnToolbarDropDown)
	ON_COMMAND(ID_EXIT, OnExit)
	ON_COMMAND(ID_MODIFY_PASSWORD, OnModifyPwd)
	ON_COMMAND(ID_MODIFY_CONFIG, OnModifyConfig)
	ON_UPDATE_COMMAND_UI(USER_MESSAGE, &CMainFrame::OnUpdateToolBarMsgCount)

	ON_COMMAND_RANGE(ID_ISSUE_MEIGUI_CARD, ID_OPER_LOG_QUERY, OnMenuBtnClicked)
	ON_COMMAND_RANGE(RELOAD, USER_ACCOUNT, OnToolBarBtnClicked)

	ON_MESSAGE(WM_CEF_SESSION_EXPIRED, OnSessionExpired)
	ON_MESSAGE(WM_UPDATE_USER_MSG_COUNT, OnGotMsgCount)

	ON_UPDATE_COMMAND_UI(ID_INDICATOR_TIME, OnUpdateStateBar)

	ON_WM_TIMER()

	//ON_MESSAGE(WM_TIME_TICK, UpdateTime)
END_MESSAGE_MAP()

static UINT indicators[] =
{
	ID_SEPARATOR,           // status line indicator
	ID_INDICATOR_CAPS,
	ID_INDICATOR_NUM,
	ID_INDICATOR_SCRL,
	ID_INDICATOR_TIME
};

// CMainFrame construction/destruction

CMainFrame::CMainFrame() : m_is_view_closing(false), m_msg_count(0)
{}

CMainFrame::~CMainFrame()
{}

bool CMainFrame::Launch()
{
	auto& url_cfg = URLConfig::Instance();
	if (m_view.CreateBrowser(url_cfg.FullHost() + url_cfg.MainPath()))
	{
		m_user_msg_monitor.Start(); // Start the user msg monitor to monitor user msg update.
		return true;
	}
	LOG_ERROR(L"创建浏览器失败!");// Create main browser failed!

	if (!LogOut())
		LOG_ERROR(L"服务端退出请求处理失败。");
	else
		LOG_TRACE(L"退出系统成功。");

	return false;
}

void CMainFrame::UpdateStatus(LPCTSTR status)
{
	//m_status_bar.SetPaneText(0, status);
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if( !CFrameWnd::PreCreateWindow(cs) )
		return FALSE;
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	cs.style = WS_OVERLAPPED | WS_CAPTION | FWS_ADDTOTITLE
		 | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_MAXIMIZE | WS_SYSMENU;

	cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
	cs.lpszClass = AfxRegisterWndClass(0);
	return TRUE;
}

BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
	// let the view have first crack at the command
	if (m_view.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
		return TRUE;

	// otherwise, do default handling
	return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

// CMainFrame diagnostics
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
	CFrameWnd::AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
	CFrameWnd::Dump(dc);
}
#endif //_DEBUG

// CMainFrame message handlers

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	do
	{
		if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
			break;
		if (!m_menu_res_auth_manager.UpdateAuth())
			break;
		if (!FilterMenuBar())
			break;
		if (!FilterQuickAccessToolBar())
			break;
		if (!CreateMainFrmUI())
			break;

		SetTimer(1, 60000, NULL);//这个级别低一点,用来从服务器获取时间
		SetTimer(2, 1000, NULL);//每分钟更新一次时间

		UINT nIndex = m_status_bar.CommandToIndex(ID_INDICATOR_TIME);//获取时间指示器索引 
		CString sTime = m_cTimeManager.GetServerTime();
		m_status_bar.SetPaneText(nIndex, sTime);


		return 0; // Return 0 if all succeed.
	} while (0);
	return -1; // Return -1 to indicate error happens.
}

void CMainFrame::OnClose()
{
	if (!m_is_view_closing)
	{
		if ( !Session::Instance().IsExpired() ) // Only prompt notification in logged in state.
		{
			if (IDNO == MessageBox(L"您确定要退出并关闭程序吗?", L"退出系统提示!", MB_YESNO | MB_ICONEXCLAMATION))
				return;
			if (!LogOut())
			{
				MessageBox(L"退出系统失败,若无法重新登录请联系维护人员!", L"退出系统提示!", MB_OK | MB_ICONWARNING);
				LOG_ERROR(L"服务端退出请求处理失败。程序关闭。");
			}
			else
				LOG_TRACE(L"退出系统成功。");
		}
		m_view.SendMessage(WM_CLOSE); // Request CETradeClientView to close.
		m_is_view_closing = true; // Enable next time close.
		return; // And cancel this close.
	}
	m_user_msg_monitor.Stop();
	LOG_TRACE(L"程序关闭。");

	CFrameWnd::OnClose();
}

void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	do
	{
		if ((lpMeasureItemStruct == NULL) || (lpMeasureItemStruct->CtlType != ODT_MENU))
			break; // Don't handle items other than menu.
		const uint32_t kIconWidth = 24, kIconHeight = 24; // This is the most suitable size for showing a icon.
		lpMeasureItemStruct->itemWidth = kIconWidth;
		lpMeasureItemStruct->itemHeight = kIconHeight;
	} while (0);
	CFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}

void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	static const int32_t OFFSET_LEFT = -6, OFFSET_TOP = -4; // This is the most suitable size.
	do
	{
		if ((lpDrawItemStruct == NULL) || (lpDrawItemStruct->CtlType != ODT_MENU))
			break; // Don't handle items other than menu.

		::DrawIconEx(lpDrawItemStruct->hDC, lpDrawItemStruct->rcItem.left + OFFSET_LEFT, lpDrawItemStruct->rcItem.top + OFFSET_TOP,
			m_authorized_menu_icons[lpDrawItemStruct->itemID], MENU_ICON_WIDTH, MENU_ICON_HEIGHT, 0, NULL, DI_NORMAL);
	} while (0);

	CFrameWnd::OnDrawItem(nIDCtl, lpDrawItemStruct);
}

void CMainFrame::OnInitMenuPopup(CMenu* pMenu, UINT nIndex, BOOL bSysMenu)
{
	CFrameWnd::OnInitMenuPopup(pMenu, nIndex, bSysMenu);

	if (bSysMenu)
		pMenu = GetSystemMenu(FALSE); // Allows the application to access the Control menu for copying and modification.

	//@{ Set menu style.
	MENUINFO mnfo;
	mnfo.cbSize = sizeof(mnfo);
	mnfo.fMask = MIM_STYLE;
	mnfo.dwStyle = MNS_CHECKORBMP | MNS_AUTODISMISS;
	pMenu->SetMenuInfo(&mnfo);
	//@}

	//@{ Set menu item attributes.
	MENUITEMINFO minfo;
	minfo.cbSize = sizeof(minfo);
	// Warning!!! Currently we have only 2 menu levels: top menu & sub menu.
	// So this algorithm is not general enough to handle a menu tree.
	for (int pos = 0; pos < pMenu->GetMenuItemCount(); ++pos)
	{
		minfo.fMask = MIIM_FTYPE | MIIM_ID;
		pMenu->GetMenuItemInfo(pos, &minfo, TRUE);

		if ((minfo.fState & MFS_DISABLED) || (minfo.fState & MFS_UNCHECKED))
		{
			int i = 0;
		}

		if (minfo.fType == MFT_SEPARATOR) // Don't handle the separator.
			continue;

		if (!(minfo.fType & MFT_OWNERDRAW)) // If not a owner draw menu item.
		{
			minfo.fMask = MIIM_FTYPE | MIIM_BITMAP; // Indicates the members to be retrieved or set: the 'hbmpItem' member..
			// "HBMMENU_CALLBACK" indicates taht a bitmap that is drawn by the window that owns the menu. 
			// The application must process the WM_MEASUREITEM and WM_DRAWITEM messages.
			minfo.hbmpItem = HBMMENU_CALLBACK;
			minfo.fType = MFT_STRING; // Displays the menu item using a text string.
			pMenu->SetMenuItemInfo(pos, &minfo, TRUE);
		}
	}
	//@}
}

void CMainFrame::OnUpdateToolBarMsgCount(CCmdUI *pCmdUI)
{
	if (m_msg_count > 0)
		pCmdUI->Enable(true);
	else
		pCmdUI->Enable(false);
}

void CMainFrame::OnToolBarBtnClicked(UINT btn_id)
{
	switch (btn_id)
	{
	case RELOAD:
		m_view.Browser().Reload();
		break;
	case USER_MESSAGE:
		ShowUserMessageDlg();
		break;
	default:
		break;
	}
}

void CMainFrame::OnMenuBtnClicked(UINT btn_id)
{
	m_view.Browser().NavigateTo(MenuUrl(btn_id));
}

void CMainFrame::OnToolbarDropDown(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMTOOLBAR tool_bar = reinterpret_cast<LPNMTOOLBAR>(pNMHDR);

	if (USER_ACCOUNT == tool_bar->iItem)
	{
		CMenu menu;
		menu.LoadMenu(IDR_ACCOUNT);
		CMenu*popup_menu = menu.GetSubMenu(0);
		ASSERT(popup_menu);

		CRect rc;
		m_ex_func_tlb.SendMessage(TB_GETRECT, tool_bar->iItem, (LPARAM)&rc);
		m_ex_func_tlb.ClientToScreen(&rc);
		popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL, rc.left, rc.bottom, this, &rc);
	}
}

void CMainFrame::OnExit()
{
	AfxGetMainWnd()->PostMessage(WM_CLOSE);
}

void CMainFrame::OnModifyPwd()
{
	m_user_msg_monitor.Stop(); // Stop monitoring before launching the password modification dialog.
	RECT rect;
	GetWindowRect(&rect);
	CModifyPwdView(rect).DoModal();
	m_user_msg_monitor.Start(); // Recover monitoring.
}

void CMainFrame::OnModifyConfig()
{
	m_user_msg_monitor.Stop(); // Stop monitoring before launching the password modification dialog.
	CConfigDialog().DoModal();
	m_user_msg_monitor.Start(); // Recover monitoring.
}

afx_msg LRESULT CMainFrame::OnSessionExpired(WPARAM wParam, LPARAM lParam)
{
	m_user_msg_monitor.Stop(); // First stop the user message monitoring.
	if (!CLoginDialog(L"当前连接已过期,请重新登录!", this).Launch())
	{
		OnExit();
		return 0;
	}
	Session::Instance().OnValid(); // If login succeeded, recover the session state to be "not expired".
	m_user_msg_monitor.Start(); // Start again after relogged in.
	return 0;
}

afx_msg LRESULT CMainFrame::OnGotMsgCount(WPARAM wParam, LPARAM lParam)
{
	m_msg_count = (int)wParam;
	return 0;
}

bool CMainFrame::FilterMenuBar()
{
	CWnd* main_wnd = AfxGetApp()->m_pMainWnd;
	CMenu* menu = main_wnd->GetMenu();

	// Remove unauthorized menu.
	// !!! Through reverse traversal, guarantee the index order will not disorderly when remove next menu item.
	MenuResAuthMgr::MenuItemsType::const_reverse_iterator criter;
	const auto& menu_items = m_menu_res_auth_manager.MenuItems();

	std::vector<uint32_t>::const_iterator menu_index_iter;
	for (criter = menu_items.crbegin(); criter != menu_items.crend(); ++criter)
	{
		if (!criter->second.is_authorized) // Remove those unauthorized menu items.
		{
			CMenu* sub_menu = menu;
			// Traverse to the parent menu of the leaf node menu(which has no more submenu).
			const auto& index_list = criter->second.index_list;
			if (index_list.empty())
				break;
			// " -1 " means only traverse to the leaf node's parent node. Then the iter will be directed to the lead node.
			// , then we can call "RemoveMenu(*menu_index_iter, MF_BYPOSITION)" to remove the leaf node.
			// This works the same to the top level menu.
			for (menu_index_iter = index_list.cbegin(); menu_index_iter != index_list.cend() - 1; ++menu_index_iter)
			{
				sub_menu = sub_menu->GetSubMenu(*menu_index_iter);
				if (NULL == sub_menu)
				{
					std::stringstream err_msg;
					err_msg << "菜单配置错误, menu resource id: " << criter->first << "; menu index: " << *menu_index_iter << "!";
					LOG_ERROR(str_2_wstr(err_msg.str()));
					break;
				}
			}
			// Here we only need to remove the sub menu, since web server will not return an empty parent menu which has none sub menu.
			if (NULL == sub_menu || !sub_menu->RemoveMenu(*menu_index_iter, MF_BYPOSITION))
				LOG_ERROR(L"移除未授权菜单失败, menu resource id = " + std::to_wstring(criter->first) + L"!");
		}
		else
		{
			std::wstring file_path = MENU_ICON_FOLDER + str_2_wstr(criter->second.icon_name);
			if (!fs::exists(file_path))
			{
				LOG_ERROR(L"图片文件:" + file_path + L"不存在, 请检查!");
				break;
			}
			HICON hicon = (HICON)LoadImage(AfxGetResourceHandle(),
				file_path.c_str(), IMAGE_ICON, MENU_ICON_WIDTH, MENU_ICON_HEIGHT, LR_DEFAULTCOLOR | LR_LOADFROMFILE);
			m_authorized_menu_icons.emplace(std::make_pair(criter->first, hicon));
			
			// 针对需要服务器端授权的菜单项,使用从服务器获取的菜单标题来更新菜单标题,
			// 对于无需服务器端授权的“本地菜单项”,则无需更新。
			if (!m_menu_res_auth_manager.IsLocalMenuItem(criter->first))
				menu->ModifyMenu(criter->first, MF_BYCOMMAND, criter->first, criter->second.remark.c_str());
		}
	}
	main_wnd->DrawMenuBar();
	return true;
}

bool CMainFrame::FilterQuickAccessToolBar()
{
	typedef MenuResAuthMgr::MenuItemsType MenuItemsT;
	const auto& menu_items = m_menu_res_auth_manager.MenuItems();
	int index = 0;
	for (auto iter = kQuickAccessBtn.begin(); iter != kQuickAccessBtn.end();)
	{
		MenuItemsT::const_iterator cit = menu_items.find(iter->idCommand);
		if (cit != menu_items.cend() && cit->second.is_authorized) // If found and authorized.
		{ 
			iter->iBitmap = index;// modify the icon index mapping.
			index++;
			++iter;
		}	
		else // If not found or unauthorized.
			iter = kQuickAccessBtn.erase(iter);
	}
	return true;
}

bool CMainFrame::CreateMainFrmUI()
{
	do
	{
		HICON frame_icon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
		// Specifies a 32 pixel by 32 pixel icon if TRUE; specifies a 16 pixel by 16 pixel icon if FALSE.
		SetIcon(frame_icon, TRUE); // Set big icon
		SetIcon(frame_icon, FALSE); // Set small icon
		// create a view to occupy the client area of the frame
		if (!m_view.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL))
		{
			LOG_ERROR(L"Failed to create view window\n");
			break;
		}

		if (!m_status_bar.Create(this))
		{
			LOG_ERROR(L"Failed to create status bar\n");
			break;      // fail to create
		}
		m_status_bar.SetIndicators(indicators, sizeof(indicators) / sizeof(UINT));

		//m_status_bar.SetPaneStyle(0, SBPS_NORMAL);

		//@{
		if (!CreateWebNaviToolBar())
		{
			LOG_ERROR(L"Failed to create web navigation toolbar\n");
			break;      // fail to create
		}

		if (!CreateQuickAccessToolBar())
		{
			LOG_ERROR(L"Failed to create quick access toolbar\n");
			break;      // fail to create
		}

		if (!CreateExFuncToolBar())
		{
			LOG_ERROR(L"Failed to create extra function toolbar\n");
			break;      // fail to create
		}

		if (!m_re_bar.Create(this) ||
			!m_re_bar.AddBar(&m_web_navi_tlb, NULL, NULL, RBBS_NOGRIPPER) ||
			!m_re_bar.AddBar(&m_quick_access_tlb, NULL, NULL, RBBS_NOGRIPPER) ||
			!m_re_bar.AddBar(&m_ex_func_tlb, NULL, NULL, RBBS_NOGRIPPER))
		{
			LOG_ERROR(L"Failed to create rebar\n");
			break;      // fail to create
		}
		//@}

		m_re_bar.GetReBarCtrl().MinimizeBand(0);
		return true;
	} while (0);

	return false;
}

bool CMainFrame::CreateWebNaviToolBar()
{
	static const std::vector<std::wstring> kWebNaviIcon = { _T("reload.ico") };
	static std::vector<TBBUTTON> kWebNaviBtn = { { 0, RELOAD, TBSTATE_ENABLED, BTNS_BUTTON, 0, 0 } };

	if (!m_web_navi_tlb.CreateEx(this, TBSTYLE_TRANSPARENT))
		return false; // fail to create

	if (!m_web_navi_tlb_imgs.Create(TOOLBAR_ICON_WIDTH, TOOLBAR_ICON_HEIGHT, ILC_COLOR32 | ILC_MASK, 0, 0))
		return false;

	for (const auto& icon_name : kWebNaviIcon)
	{
		std::wstring file_path = TOOLBAR_ICON_FOLDER + icon_name;
		if (!fs::exists(file_path))
		{
			LOG_ERROR(L"图片文件:" + file_path + L"不存在, 请检查!");
			return false;
		}
		HICON icon = (HICON)LoadImage(
			AfxGetResourceHandle(), file_path.c_str(), IMAGE_ICON, TOOLBAR_ICON_WIDTH, TOOLBAR_ICON_HEIGHT, LR_DEFAULTCOLOR | LR_LOADFROMFILE);
		m_web_navi_tlb_imgs.Add(icon);
	}

	CToolBarCtrl& tbc = m_web_navi_tlb.GetToolBarCtrl();
	for (auto& btn : kWebNaviBtn)
		tbc.AddButtons(1, &btn);

	tbc.SetImageList(&m_web_navi_tlb_imgs);

	const uint32_t kPaddingWidth = 45;
	UpdateToolbarBtnSize(m_web_navi_tlb, kPaddingWidth); // Make sure to set toolbar button size after the button text has been set.
	
	return true;
}

bool CMainFrame::CreateQuickAccessToolBar()
{
	if (!m_quick_access_tlb.CreateEx(this, TBSTYLE_TRANSPARENT | TBSTYLE_LIST))
		return false; // fail to create
	if (!m_quick_access_tlb_imgs.Create(TOOLBAR_ICON_WIDTH, TOOLBAR_ICON_HEIGHT, ILC_COLOR32 | ILC_MASK, 0, 0))
		return false;
	
	CToolBarCtrl& tbc = m_quick_access_tlb.GetToolBarCtrl();
	tbc.SetImageList(&m_quick_access_tlb_imgs); // 注意这一句必须放在为“SetButtonText”和“UpdateToolbarBtnSize”之前,否则会出现宽度计算错误

	const auto& res_auth_map = m_menu_res_auth_manager.MenuItems();
	int index = 0;
	for (auto& btn : kQuickAccessBtn)
	{
		tbc.AddButtons(1, &btn);

		std::wstring file_path = TOOLBAR_ICON_FOLDER + str_2_wstr(res_auth_map.at(btn.idCommand).icon_name);
		if (!fs::exists(file_path))
		{
			LOG_ERROR(L"图片文件:" + file_path + L"不存在, 请检查!");
			return false;
		}
		HICON icon = (HICON)LoadImage(
			AfxGetResourceHandle(), file_path.c_str(), IMAGE_ICON, TOOLBAR_ICON_WIDTH, TOOLBAR_ICON_HEIGHT, LR_DEFAULTCOLOR | LR_LOADFROMFILE);
		m_quick_access_tlb_imgs.Add(icon);
		m_quick_access_tlb.SetButtonText(index, res_auth_map.at(btn.idCommand).remark.c_str());
		index++;
	}
	
	UpdateToolbarBtnSize(m_quick_access_tlb); // Make sure to set toolbar button size after the button text has been set.
	return true;
}

bool CMainFrame::CreateExFuncToolBar()
{
	static std::vector<std::wstring> kExFuncText =
	{
		_T("消息"),
		_T("账户")
	};
	static const std::vector<std::wstring> kExFuncIcon =
	{
		_T("message.ico"),
		_T("user.ico")
	};
	// Icon for the disabled toolbar buttons.
	static const std::vector<std::wstring> kExFuncIconDisabled =
	{
		_T("message_d.ico"),
		_T("user_d.ico")
	};
	static std::vector<TBBUTTON> kExFuncBtn =
	{
		{ 0, USER_MESSAGE, TBSTATE_ENABLED, BTNS_AUTOSIZE | BTNS_BUTTON, 0, 0 },
		{ 1, USER_ACCOUNT, TBSTATE_ENABLED, BTNS_AUTOSIZE | BTNS_BUTTON | BTNS_WHOLEDROPDOWN, 0, 0 }
	};

	if (!m_ex_func_tlb.CreateEx(this, TBSTYLE_TRANSPARENT | TBSTYLE_LIST))
		return false; // fail to create
	if (!m_ex_func_tlb_imgs.Create(TOOLBAR_ICON_WIDTH, TOOLBAR_ICON_HEIGHT, ILC_COLOR32 | ILC_MASK, 0, 0) ||
		!m_ex_func_tlb_disabled_imgs.Create(TOOLBAR_ICON_WIDTH, TOOLBAR_ICON_HEIGHT, ILC_COLOR32 | ILC_MASK, 0, 0))
		return false;

	for (const auto& icon_name : kExFuncIcon)
	{
		std::wstring file_path = TOOLBAR_ICON_FOLDER + icon_name;
		if (!fs::exists(file_path))
		{
			LOG_ERROR(L"图片文件:" + file_path + L"不存在, 请检查!");
			return false;
		}
		HICON icon = (HICON)LoadImage(
			AfxGetResourceHandle(), file_path.c_str(), IMAGE_ICON, TOOLBAR_ICON_WIDTH, TOOLBAR_ICON_HEIGHT, LR_DEFAULTCOLOR | LR_LOADFROMFILE);
		m_ex_func_tlb_imgs.Add(icon);
	}

	for (const auto& icon_name : kExFuncIconDisabled)
	{
		std::wstring file_path = TOOLBAR_ICON_FOLDER + icon_name;
		if (!fs::exists(file_path))
		{
			LOG_ERROR(L"图片文件:" + file_path + L"不存在, 请检查!");
			return false;
		}
		HICON icon = (HICON)LoadImage(
			AfxGetResourceHandle(), file_path.c_str(), IMAGE_ICON, TOOLBAR_ICON_WIDTH, TOOLBAR_ICON_HEIGHT, LR_DEFAULTCOLOR | LR_LOADFROMFILE);
		m_ex_func_tlb_disabled_imgs.Add(icon);
	}

	CToolBarCtrl& tbc = m_ex_func_tlb.GetToolBarCtrl();
	for (auto& btn : kExFuncBtn)
		tbc.AddButtons(1, &btn);

	tbc.SetImageList(&m_ex_func_tlb_imgs);
	tbc.SetDisabledImageList(&m_ex_func_tlb_disabled_imgs);

	int index = 0;
	for (auto& text : kExFuncText)
	{
		m_ex_func_tlb.SetButtonText(index, text.c_str());
		index++;
	}

	// 设置用户名
	const int kAccountBtnIndex = 1;
	m_ex_func_tlb.SetButtonText(kAccountBtnIndex, Session::Instance().UserName().c_str());
	UpdateToolbarBtnSize(m_ex_func_tlb); // Make sure to set toolbar button size after the button text has been set.
	return true;
}

void CMainFrame::UpdateToolbarBtnSize(CToolBar& toolbar, uint32_t padding_width, uint32_t padding_height)
{
	CRect btn_rect;
	int total_btns_width = 0;
	int btns_count = toolbar.GetToolBarCtrl().GetButtonCount();
	if (btns_count<=0)
		return;
	for (int idx = 0; idx < btns_count; ++idx)
	{
		toolbar.GetItemRect(idx, &btn_rect);
		total_btns_width += btn_rect.Size().cx;
	}
	const int btn_size_x = total_btns_width / btns_count;// 计算按钮宽度平均值
	const int btn_size_y = btn_rect.Size().cy;
	toolbar.SetSizes(CSize(btn_size_x + padding_width, btn_size_y + padding_height), CSize(TOOLBAR_ICON_WIDTH, TOOLBAR_ICON_HEIGHT));
}

bool CMainFrame::LogOut()
{
	bool log_out_res = true;
	std::string response_body;
	try
	{
		response_body = WinHttpGet(URLConfig::Instance().LogoutPath());
	}
	catch (std::exception& ex)
	{
		LOG_ERROR(L"网络连接有问题,退出系统失败。错误信息: " + gbk_2_wstr(ex.what()));
		return false;
	}

	namespace PT = boost::property_tree;
	try //Parse the configuration file
	{
		PT::ptree ptree;
		std::stringstream ss;
		ss << response_body;
		PT::read_json(ss, ptree);
		std::string res = ptree.get<std::string>(JSON_TAG_CODE);
		log_out_res = res.compare(JSON_VAL_SUCC) == 0 ? true : false;
		if (!log_out_res)
			LOG_ERROR(L"服务器退出处理失败,返回结果为:" + str_2_wstr(ptree.get<std::string>(JSON_TAG_MSG)));
	}
	catch (...)
	{
		LOG_ERROR(L"解析服务器返回的退出信息时出错!请确认返回数据不为空,返回的数据格式为正确的Json格式!");
		return false;
	}
	return log_out_res;
}

void CMainFrame::ShowUserMessageDlg()
{
	m_user_msg_monitor.Stop(); // Stop monitoring before launching the msg view dialog.
	RECT rect;
	GetClientRect(&rect);
	ClientToScreen(&rect);
	CUserMsgView(rect).DoModal();
	m_user_msg_monitor.Start(); // Recover monitoring.
}

std::string CMainFrame::MenuUrl(uint32_t menu_res_id) const
{
	auto& url_cfg = URLConfig::Instance();
	return url_cfg.FullHost() + m_menu_res_auth_manager.MenuItems().at(menu_res_id).url_path;
}

std::string CMainFrame::WinHttpGet(const std::string& url_path) const
{
	const uint32_t kHTTPOK = 200;
	auto& url_cfg = URLConfig::Instance();

	WinHttp win_http;
	win_http.ConnectHost(url_cfg.Host(), url_cfg.Port(), url_cfg.IsHttps());
	auto& request = win_http.OpenRequest(WinHttp::Method::GET, url_path);
	if (url_cfg.IsHttps())
	{
		auto& app_cfg = ApplicationConfig::Instance();
		request.SetClientCertificate(app_cfg.ClientCertStore(), app_cfg.ClientCertSubject());
	}
	request.SetCookies(Session::Instance().Cookies());
	request.Send();
	uint32_t status_code = request.GetResponseStatus();
	if (kHTTPOK != status_code)
	{
		std::string err_msg = "网络请求错误! 错误码: " + std::to_string(status_code);
		throw std::exception(err_msg.c_str());
	}
	std::string response_body = request.ReadResponseBody();
	if (response_body.empty())
		throw std::exception("获取服务器响应数据失败,请确保网络连接正常");
	return response_body;
}

LRESULT CMainFrame::OnCrashClose(WPARAM wParam, LPARAM lParam)
{
	if (!m_is_view_closing)
	{
		if (!Session::Instance().IsExpired()) // Only prompt notification in logged in state.
		{
			if (!LogOut())
			{
				MessageBox(L"退出系统失败,若无法重新登录请联系维护人员!", L"退出系统提示!", MB_OK | MB_ICONWARNING);
				LOG_ERROR(L"崩溃时,服务端退出请求处理失败。程序关闭。");
			}
			else
				LOG_TRACE(L"退出崩溃系统成功。");
		}
		/* Request CETradeClientView to close.this will make view to exit cef,
		and cef will send wm_close message to this fram window.*/
		m_view.SendMessage(WM_CLOSE);
		m_is_view_closing = true; // Enable next time close.
	}
	m_user_msg_monitor.Stop();
	LOG_TRACE(L"崩溃程序关闭。");
	CFrameWnd::OnClose();
	return 0;
}



void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{
	CString sTime;
	UINT nIndex = m_status_bar.CommandToIndex(ID_INDICATOR_TIME);//获取时间指示器索引 
	if (nIDEvent == 1)
	{
		sTime = m_cTimeManager.GetServerTime();
	}
	else if (nIDEvent == 2)
	{
		sTime = m_cTimeManager.UpdateTime();
	}
	m_status_bar.SetPaneText(nIndex, sTime);

	CFrameWnd::OnTimer(nIDEvent);
}

void CMainFrame::OnUpdateStateBar(CCmdUI *pCmdUI)
{
	pCmdUI->Enable();
}

LRESULT CMainFrame::UpdateTime(WPARAM wParam, LPARAM lParam)
{
	CString sTime = m_cTimeManager.UpdateTime();
	UINT nIndex = m_status_bar.CommandToIndex(ID_INDICATOR_TIME);//获取时间指示器索引  
	m_status_bar.SetPaneText(nIndex, sTime);

	return NULL;
}