/*
SDK示例: YoseenDemo, Win32

简要说明SDK函数用法: 设备发现\配置, 用户登入\登出, 预览开始\停止, jpg\mp4\stream文件保存
*/
#include <YoseenSDK/YoseenSDK.h>
#include <YoseenSDK/YoseenAlg_Mea.h>

//
#include <Windows.h>
#include <stdio.h>

/**
控件序号
*/
enum control_id {
	//
	cid_hwndPreview = 1001,

	//
	cid_btnUrl,
	cid_tbUrl,
	cid_btnLogin,
	cid_btnPlayVideo,
	cid_btnPlayTemp,
	cid_btnStop,

	//
	cid_btnSaveJpgx,
	cid_btnSaveStream,
	cid_btnSaveMp4,
	cid_cmbTest,
	cid_btnTest,

	//
	cid_txtEcho,
};

/*
控件字符
*/
#define Const_hwndShell			L"YoseenDemo, Win32"
#define Const_hwndPreview		L"Preview"
#define Const_btnUrl			L"Url"

#define Const_btnLogin			L"Login"
#define Const_btnLogin2			L"Logout"
#define Const_btnPlayVideo		L"Play Video"
#define Const_btnPlayTemp		L"Play Temp"
#define Const_btnStop			L"Stop"

#define Const_btnSaveJpgx		L"Save .jpg"
#define Const_btnSaveMp4		L"Begin Save .mp4"
#define Const_btnSaveMp42		L"End Save .mp4"
#define Const_btnSaveStream		L"Begin Save .stream"
#define Const_btnSaveStream2	L"End Save .stream"

//
#define Const_cmbTest_SetMeasureTemplate			L"Measure Template"
#define Const_cmbTest_ManualFFC				L"Manual FFC"
#define Const_btnTest						L"Test"

/*
主程序变量集
*/
enum ShellViewFlags {
	ShellViewFlags_SaveMp4 = 0x0001,
	ShellViewFlags_SaveStream = 0x0002,
};
typedef struct _ShellView {
	s32 flags;
	HWND hwndShell;				//主窗口

	//
	HWND hwndPreview;			//预览窗口

	//
	HWND btnUrl;				//地址按钮
	wchar_t url[128];
	HWND tbUrl;					//地址文本
	HWND btnLogin;				//登入按钮
	HWND btnPlayVideo;			//预览视频流按钮
	HWND btnPlayTemp;			//预览温度流按钮
	HWND btnStop;				//停止预览按钮

	//
	HWND btnSaveJpgx;			//保存jpg按钮
	HWND btnSaveStream;			//录制stream按钮, 温度流
	HWND btnSaveMp4;			//录制mp4按钮, 视频流
	wchar_t fn[256];
	HWND cmbTest;				//测试选择框
	HWND btnTest;				//测试按钮

	//
	HWND txtEcho;				//按钮点击反馈文本
	wchar_t echo[256];

	//
	s32 userHandle;							//用户句柄
	s32 previewHandle;						//预览句柄
	YoseenLoginInfo loginInfo;				//登入信息
	CameraBasicInfo cameraBasicInfo;	//热像仪基本信息
	YoseenPreviewInfo previewInfo;		//预览信息
}ShellView;
static ShellView _shellView = { 0 };

static void ShellView_Layout(RECT& rect);

/**
按时间和后缀生成文件名
*/
static const char* _getFileName(const char* suffix) {
	static char fn[64];
	SYSTEMTIME t;
	GetLocalTime(&t);
	sprintf(fn, ".\\data\\%04d%02d%02d_%02d%02d%02d%s",
		t.wYear, t.wMonth, t.wDay,
		t.wHour, t.wMinute, t.wSecond,
		suffix);
	return fn;
}

/*
显示SDK函数调用结果
*/
static void ShellView_Echo(const wchar_t* szFormat, ...) {
	wchar_t* echo = _shellView.echo;

	va_list ap;
	va_start(ap, szFormat);
	vswprintf(echo, 256, szFormat, ap);
	va_end(ap);
	::SendMessage(_shellView.txtEcho,WM_SETTEXT,256, (LPARAM)_shellView.echo);
}

/**
发现在线设备
*/
static void _discover() {
	//发现在线设备, 返回响应链表
	DiscoverCameraResp2* dcrList = Yoseen_DiscoverCameras(xxxdiscoverflags_broadcast);

	//迭代响应链表
	if (dcrList == NULL) {
		swprintf(_shellView.echo, 16, L"discover no cameras");
	}
	else {
		s32 i = 0;
		wchar_t* w = _shellView.echo;
		swprintf(w, 32, L"discover cameras, ");
		w += wcslen(w);
		DiscoverCameraResp2* dcr = dcrList;
		while (dcr != NULL) {
			u32 ip = dcr->CameraIp;
			u8* p = (u8*)& ip;
			swprintf(w, 16, L"%d.%d.%d.%d, ", *(p + 3), *(p + 2), *(p + 1), *p);
			w += wcslen(w);
			dcr = dcr->pNext;
		}
	}
	SendMessage(_shellView.txtEcho,
		WM_SETTEXT,
		256,
		(LPARAM)_shellView.echo);


	//释放发现响应链表
	Yoseen_DiscoverCamerasFree(&dcrList);
}

/*
用户登入和登出
*/
static void _login() {
	s32 userHandle = _shellView.userHandle;
	if (userHandle >= 0) {
		s32 ret = Yoseen_Logout(userHandle);
		_shellView.userHandle = -1;
		SendMessage(_shellView.btnLogin,
			WM_SETTEXT,
			128,
			(LPARAM)Const_btnLogin);
		ShellView_Echo(L"Yoseen_Logout, ret %d", ret);
	}
	else {
		YoseenLoginInfo& loginInfo = _shellView.loginInfo;
		CameraBasicInfo& cameraBasicInfo = _shellView.cameraBasicInfo;
		SendMessage(_shellView.tbUrl,
			WM_GETTEXT,
			128,
			(LPARAM)_shellView.url);
		wcstombs(loginInfo.CameraAddr, _shellView.url, 128);
		//strcpy(loginInfo.Username, "admin");
		//strcpy(loginInfo.Password, "admin");
		s32 userHandle = Yoseen_Login(&loginInfo, &cameraBasicInfo);
		if (userHandle >= 0) {
			_shellView.userHandle = userHandle;
			SendMessage(_shellView.btnLogin,
				WM_SETTEXT,
				128,
				(LPARAM)Const_btnLogin2);
		}
		ShellView_Echo(L"Yoseen_Login, ret %d", userHandle);
	}
}

/*
预览回调, 此回调触发在工作线程
*/
static void __stdcall _previewCallback(s32 errorCode, DataFrame* frame, void* customData) {
	if (EError_None == errorCode) {
		//接收数据成功, 根据预览数据类型, 选择对应数据做后处理

		//视频流或混合流单帧
		//H264FrameHeader* h264Head = (H264FrameHeader*)frame->H264;//前端固件<20190901
		//H264FrameHeaderV2* h264Head = (H264FrameHeaderV2*)frame->H264;//前端固件>20190901
		//bgra* bmpData = (bgra*)frame->Bmp;

		//温度流单帧
		//H264FrameHeader* h264Head = (H264FrameHeader*)frame->H264;
		//DataFrameHeader* tempHead = (DataFrameHeader*)frame->Head;
		//s16* tempData = (s16*)frame->Temp;
		//bgra* bmpData = (bgra*)frame->Bmp;
	}
	else {
		//接收数据失败, 预览内置自动恢复, EError_PreviewRecoverBegin-EError_PreviewRecoverEnd
		ShellView_Echo(L"PreviewCallback, errorCode %d", errorCode);
	}
}

static void _stopPlay() {
	s32 previewHandle = _shellView.previewHandle;
	if (previewHandle >= 0) {
		s32 ret = Yoseen_StopPreview(previewHandle);
		_shellView.previewHandle = -1;
		ShellView_Echo(L"Yoseen_StopPreview, ret %d", ret);
	}
}

static void _startPlay(s32 dataType) {
	_stopPlay();

	//
	s32 previewHandle;
	YoseenPreviewInfo& previewInfo = _shellView.previewInfo;
	previewInfo.DataType = dataType;
	previewInfo.CustomData = &_shellView;
	previewInfo.CustomCallback = _previewCallback;
	previewInfo.Hwnd = _shellView.hwndPreview;
	previewHandle = Yoseen_StartPreview(_shellView.userHandle, &previewInfo);

	//previewInfo.Hwnd = NULL;
	//previewHandle = Yoseen_StartPreviewTempScale(_shellView.userHandle, &previewInfo, 3);
	ShellView_Echo(L"Yoseen_StartPreview, ret %d", previewHandle);
	if (previewHandle < 0) {
		return;
	}
	_shellView.previewHandle = previewHandle;

	//strech_control sc = {};
	//sc.flags = SCF_DDELevel;
	//sc.dde_level = 8;
	//Yoseen_PreviewSetImage(previewHandle, &sc, xxxpalette_IronBow);

	//
	RECT rect;
	GetClientRect(_shellView.hwndShell, &rect);
	ShellView_Layout(rect);
}

/*
保存jpg文件, 文件内包含单帧温度数据
*/
static void _saveJpgx() {
	s32 userHandle = _shellView.userHandle;
	if (userHandle < 0)return;

	const char* fn = _getFileName(".jpg");
	s32 ret = Yoseen_SaveFrame(userHandle, fn, 1);

	mbstowcs(_shellView.fn, fn, 256);
	ShellView_Echo(L"Yoseen_SaveFrame, fn %s, ret %d", _shellView.fn, ret);
}

/*
保存mp4文件
*/
static void _saveMp4() {
	s32 previewHandle = _shellView.previewHandle;
	if (0 > previewHandle)return;
	s32 ret;
	s32& flags = _shellView.flags;
	if (flags & ShellViewFlags_SaveMp4) {
		ret = Yoseen_EndSave(previewHandle, xxxmediafile_mp4);
		flags &= (~ShellViewFlags_SaveMp4);
		SendMessage(_shellView.btnSaveMp4,
			WM_SETTEXT,
			128,
			(LPARAM)Const_btnSaveMp4);
		ShellView_Echo(L"Yoseen_EndSave, ret %d", ret);
	}
	else {
		const char* fn = _getFileName(".mp4");
		ret = Yoseen_BeginSave(previewHandle, fn, xxxmediafile_mp4);
		if (EError_None == ret) {
			flags |= ShellViewFlags_SaveMp4;
			SendMessage(_shellView.btnSaveMp4,
				WM_SETTEXT,
				128,
				(LPARAM)Const_btnSaveMp42);
		}
		mbstowcs(_shellView.fn, fn, 256);
		ShellView_Echo(L"Yoseen_BeginSave, fn %s, ret %d", _shellView.fn, ret);
	}
}

/*
保存stream文件, 文件内包含多帧温度数据
*/
static void _saveStream() {
	s32 previewHandle = _shellView.previewHandle;
	if (0 > previewHandle)return;
	s32 ret;
	s32& flags = _shellView.flags;
	if (flags & ShellViewFlags_SaveStream) {
		ret = Yoseen_EndSave(previewHandle, xxxmediafile_stream);
		flags &= (~ShellViewFlags_SaveStream);
		SendMessage(_shellView.btnSaveStream,
			WM_SETTEXT,
			128,
			(LPARAM)Const_btnSaveStream);
		ShellView_Echo(L"Yoseen_EndSave, ret %d", ret);
	}
	else {
		const char* fn = _getFileName(".stream");
		ret = Yoseen_BeginSave(previewHandle, fn, xxxmediafile_stream);
		if (EError_None == ret) {
			flags |= ShellViewFlags_SaveStream;
			SendMessage(_shellView.btnSaveStream,
				WM_SETTEXT,
				128,
				(LPARAM)Const_btnSaveStream2);
		}
		mbstowcs(_shellView.fn, fn, 256);
		ShellView_Echo(L"Yoseen_BeginSave, fn %s, ret %d", _shellView.fn, ret);
	}
}

/*
测试
*/
enum TestFuncId {
	TestFuncId_SetMeasureTemplate = 0,
	TestFuncId_ManualFFC,
	TestFuncId_All,
};

static MeasureTemplate _measureTemplate;
static void _testFunc() {
	s32 userHandle = _shellView.userHandle;
	if (0 > userHandle)return;
	s32 sel = ::SendMessage(_shellView.cmbTest, CB_GETCURSEL, 0, 0);
	s32 ret;
	switch (sel) {
		//设置测温模板, 视频流预览
	case TestFuncId_SetMeasureTemplate:
	{
        //
		MeasureTemplateHead* mth = &_measureTemplate.Head;
		mth->Width = _shellView.cameraBasicInfo.DataWidth;
		mth->Height = _shellView.cameraBasicInfo.DataHeight;
		s32 width = mth->Width;
		s32 height = mth->Height;

		//
		xxxshield* shd = &_measureTemplate.ShieldArray[0];
		u16* xydata = shd->xydata;
		xydata[0] = width / 8; xydata[1] = height / 8;
		xydata[2] = width * 2 / 8; xydata[3] = height / 8;
		xydata[4] = width * 2 / 8; xydata[5] = height * 2 / 8;
		xydata[6] = width / 8; xydata[7] = height * 2 / 8;
		mth->ShieldFlags = 0x01;

		//
		xxxmeasure* mea = &_measureTemplate.GMO;
		mea->measureflags = xxxmeasureflags_track_max | xxxmeasureflags_plot_max;

        //
		mea = &_measureTemplate.LMOArray[0];
		xydata = mea->xydata;
		mea->measureflags = xxxmeasureflags_track_max | xxxmeasureflags_plot_max;
		mea->emmi = 97;
		mea->measuretype = xxxmeasuretype_polygon;
		mea->points = 4;
		xydata[0] = width * 3 / 8; xydata[1] = height * 4 / 8;
		xydata[2] = width * 5 / 8; xydata[3] = height * 4 / 8;
		xydata[4] = width * 5 / 8; xydata[5] = height * 7 / 8;
		xydata[6] = width * 3 / 8; xydata[7] = height * 7 / 8;
		mth->LMOFlags = 0x01;

        //
		ret = Yoseen_UploadMem(_shellView.userHandle, xxxcameramem_measure, &_measureTemplate, sizeof(MeasureTemplate));
		break;
	}

	//手动挡板校零
	case TestFuncId_ManualFFC:
	{
		Ctl ctl = {};
		ctl.Type = CtlType_ManualFFC;
		ret = Yoseen_SendControl(userHandle, &ctl);
		ShellView_Echo(L"CtlType_ManualFFC, ret %d", ret);
		break;
	}

	default:
		break;
	}
}

static void ShellView_WM_COMMAND(int btn) {
	s32 ret;
	switch (btn)
	{
		//
	case cid_btnUrl:

		/*	_testRtsp();*/
		_discover();
		break;

		//
	case cid_btnLogin:
		_login();
		break;

	case cid_btnPlayVideo:
		_startPlay(xxxdatatype_video);
		break;

	case cid_btnPlayTemp:
		_startPlay(xxxdatatype_temp);
		break;

	case cid_btnStop:
		_stopPlay();
		break;

		//
	case cid_btnSaveJpgx:
		_saveJpgx();
		break;

	case cid_btnSaveMp4:
		_saveMp4();
		break;

	case cid_btnSaveStream:
		_saveStream();
		break;

	case cid_btnTest:
		_testFunc();
		break;

	default:
		break;
	}
}

/**
布局子控件, 界面自动缩放, 保持预览输出窗口的宽高比
*/
static void ShellView_LayoutPreview(s32 x, s32 y, s32 width, s32 height) {
	YoseenPreviewInfo& previewInfo = _shellView.previewInfo;
	s32 w2 = previewInfo.OutputWidth;
	s32 h2 = previewInfo.OutputHeight;

	s32 x3 = x, y3 = y, w3 = width, h3 = height;
	if (w2 != 0 && h2 != 0) {
		if (width * h2 > w2 * height) {
			w3 = height * w2 / h2;
			h3 = height;
			y3 = y;
			x3 = x + (width - w3) / 2;
		}
		else {
			w3 = width;
			h3 = width * h2 / w2;
			x3 = x;
			y3 = y + (height - h3) / 2;
		}
	}

	::MoveWindow(_shellView.hwndPreview, x3, y3, w3, h3, TRUE);
}
static void ShellView_Layout(RECT& rect) {
	//
	float xarr[] = { 1, 1, 1, 1, 1 };
	float yarr[] = { 10, 1, 1, 1, 1 };
	float dx = (float)(rect.right - rect.left) / 5.0;
	float dy = (float)(rect.bottom - rect.top) / 14.0;

	//00
	int x, y, width, height, mx, my;
	x = rect.left; y = rect.top; width = rect.right - rect.left; height = dy * yarr[0];
	ShellView_LayoutPreview(x, y, width, height);

	//10,11
	y += height; width = dx * xarr[0]; height = dy * yarr[1];
	mx = width * 0.1; my = height * 0.1;
	::MoveWindow(_shellView.btnUrl, x + mx, y + my, width - 2 * mx, height - 2 * my, TRUE);

	x += width; width = dx * (xarr[1] + xarr[2] + xarr[3] + xarr[4]);
	::MoveWindow(_shellView.tbUrl, x, y, width, height, TRUE);

	//20,
	x = rect.left; y += height; width = dx * xarr[0]; height = dy * yarr[2];
	mx = width * 0.1; my = height * 0.1;
	::MoveWindow(_shellView.btnLogin, x + mx, y + my, width - 2 * mx, height - 2 * my, TRUE);

	x += width; width = dx * xarr[1];
	mx = width * 0.1; my = height * 0.1;
	::MoveWindow(_shellView.btnPlayVideo, x + mx, y + my, width - 2 * mx, height - 2 * my, TRUE);

	x += width; width = dx * xarr[2];
	mx = width * 0.1; my = height * 0.1;
	::MoveWindow(_shellView.btnPlayTemp, x + mx, y + my, width - 2 * mx, height - 2 * my, TRUE);

	x += width; width = dx * xarr[2];
	mx = width * 0.1; my = height * 0.1;
	::MoveWindow(_shellView.btnStop, x + mx, y + my, width - 2 * mx, height - 2 * my, TRUE);

	//30
	x = rect.left; y += height; width = dx * xarr[0]; height = dy * yarr[3];
	mx = width * 0.1; my = height * 0.1;
	::MoveWindow(_shellView.btnSaveJpgx, x + mx, y + my, width - 2 * mx, height - 2 * my, TRUE);

	x += width; width = dx * xarr[1];
	mx = width * 0.1; my = height * 0.1;
	::MoveWindow(_shellView.btnSaveMp4, x + mx, y + my, width - 2 * mx, height - 2 * my, TRUE);

	x += width; width = dx * xarr[2];
	mx = width * 0.1; my = height * 0.1;
	::MoveWindow(_shellView.btnSaveStream, x + mx, y + my, width - 2 * mx, height - 2 * my, TRUE);

	x += width; width = dx * xarr[3];
	mx = width * 0.1; my = height * 0.1;
	::MoveWindow(_shellView.cmbTest, x + mx, y + my, width - 2 * mx, (height - 2 * my) * TestFuncId_All, TRUE);//cmb

	x += width; width = dx * xarr[4];
	mx = width * 0.1; my = height * 0.1;
	::MoveWindow(_shellView.btnTest, x + mx, y + my, width - 2 * mx, height - 2 * my, TRUE);

	//40
	x = rect.left; y += height; width = rect.right - rect.left; height = dy * yarr[4];
	::MoveWindow(_shellView.txtEcho, x, y, width, height, TRUE);
}

/**
主窗口过程函数
*/
LRESULT CALLBACK ShellView_winproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	switch (msg) {
	case WM_DESTROY:
		::PostQuitMessage(0);
		break;

	case WM_COMMAND:
		ShellView_WM_COMMAND(LOWORD(wParam));
		break;

	case WM_SIZE:
	{
		RECT rect;
		GetClientRect(_shellView.hwndShell, &rect);
		ShellView_Layout(rect);
		return 0;
	}

	default:
		break;
	}

	return ::DefWindowProc(hwnd, msg, wParam, lParam);
}

/**
主窗口建立函数
*/
static void ShellView_Create(HINSTANCE instance) {
	//
	WNDCLASSEX   wndclassex = { 0 };
	wndclassex.cbSize = sizeof(WNDCLASSEX);
	wndclassex.style = CS_HREDRAW | CS_VREDRAW;
	wndclassex.lpfnWndProc = ShellView_winproc;
	wndclassex.cbClsExtra = 0;
	wndclassex.cbWndExtra = 0;
	wndclassex.hInstance = instance;
	wndclassex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclassex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclassex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclassex.lpszMenuName = NULL;
	wndclassex.lpszClassName = L"ShellView";
	wndclassex.hIconSm = wndclassex.hIcon;
	RegisterClassEx(&wndclassex);
	HWND hwndShell = CreateWindow(L"ShellView",
		Const_hwndShell,
		WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
		0, 0, 800, 600,
		NULL,
		NULL,
		instance,
		NULL);
	_shellView.hwndShell = hwndShell;

	//00
	_shellView.hwndPreview = CreateWindow(L"Static",
		Const_hwndPreview,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_hwndPreview,
		instance,
		NULL);

	//10
	_shellView.btnUrl = CreateWindow(L"Button",
		Const_btnUrl,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_btnUrl,
		instance,
		NULL);
	_shellView.tbUrl = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit",
		L"",
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_tbUrl,
		instance,
		NULL);

	//20
	_shellView.btnLogin = CreateWindow(L"Button",
		Const_btnLogin,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_btnLogin,
		instance,
		NULL);

	_shellView.btnPlayVideo = CreateWindow(L"Button",
		Const_btnPlayVideo,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_btnPlayVideo,
		instance,
		NULL);
	_shellView.btnPlayTemp = CreateWindow(L"Button",
		Const_btnPlayTemp,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_btnPlayTemp,
		instance,
		NULL);
	_shellView.btnStop = CreateWindow(L"Button",
		Const_btnStop,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_btnStop,
		instance,
		NULL);

	//30
	_shellView.btnSaveJpgx = CreateWindow(L"Button",
		Const_btnSaveJpgx,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_btnSaveJpgx,
		instance,
		NULL);
	_shellView.btnSaveMp4 = CreateWindow(L"Button",
		Const_btnSaveMp4,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_btnSaveMp4,
		instance,
		NULL);
	_shellView.btnSaveStream = CreateWindow(L"Button",
		Const_btnSaveStream,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_btnSaveStream,
		instance,
		NULL);

	_shellView.cmbTest = CreateWindow(L"COMBOBOX",
		L"",
		WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_DROPDOWNLIST,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_cmbTest,
		instance,
		NULL);
	::SendMessage(_shellView.cmbTest, CB_ADDSTRING, TestFuncId_SetMeasureTemplate, (LPARAM)Const_cmbTest_SetMeasureTemplate);
	::SendMessage(_shellView.cmbTest, CB_INSERTSTRING, TestFuncId_ManualFFC, (LPARAM)Const_cmbTest_ManualFFC);
	::SendMessage(_shellView.cmbTest, CB_SETCURSEL, 0, 0);

	_shellView.btnTest = CreateWindow(L"Button",
		Const_btnTest,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_btnTest,
		instance,
		NULL);

	//40
	_shellView.txtEcho = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit",
		L"",
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hwndShell,
		(HMENU)cid_txtEcho,
		instance,
		NULL);
	SendMessage(_shellView.txtEcho, EM_SETREADONLY, 1, 0);

	//
	RECT rect;
	GetClientRect(_shellView.hwndShell, &rect);
	ShellView_Layout(rect);
}

/*
主程序建立
*/
static s32 setup(HINSTANCE hInstance) {
	//
	s32 ret = Yoseen_InitSDK();
	if (EError_None != ret) {
		return EError_SDKUninited;
	}

	//
	ShellView_Create(hInstance);
	_shellView.userHandle = -1;
	_shellView.previewHandle = -1;
	return EError_None;
}

/*
主程序消息循环
*/
static s32 msgloop() {
	//
	HWND hwnd = _shellView.hwndShell;
	::ShowWindow(hwnd, SW_SHOW);
	::UpdateWindow(hwnd);

	//
	MSG msg;
	::ZeroMemory(&msg, sizeof(MSG));
	while (msg.message != WM_QUIT) {
		if (::GetMessage(&msg, 0, 0, 0)) {
			::TranslateMessage(&msg);
			::DispatchMessage(&msg);
		}
	}
	return msg.wParam;
}

/*
主程序清理
*/
static s32 cleanup() {
	Yoseen_StopPreview(_shellView.previewHandle);
	Yoseen_Logout(_shellView.userHandle);
	Yoseen_FreeSDK();

	::DestroyWindow(_shellView.hwndShell);

	return 0;
}

/*
主程序入口函数
*/
int WINAPI WinMain(HINSTANCE hInstance,
	HINSTANCE prevInstance,
	PSTR cmdLine,
	int showCmd) {

	if (setup(hInstance) < 0) {
		return 0;
	}

	msgloop();

	cleanup();

	return 0;
}