/*

    To my parents
    Eduard Svjatoslavovich and Ludmila Aleksandrovna Barkhatov

	(c) Barkhatov Andrej Eduardovich

*/

#include "placer.h"

WNDPROC old_imp_HIST,old_imp_PYRM,old_imp_FILL,old_imp_RGBG,old_imp_EROD,old_imp_SMOO,old_imp_CONT,old_imp_THRE,
old_imp_EDGE,old_imp_APPR,old_imp_SNAK,old_imp_HOUG,old_imp_CNVX;

IMPROC_EDT(HIST)
IMPROC_EDT(PYRM)
IMPROC_EDT(FILL)
IMPROC_EDT(RGBG)
IMPROC_EDT(EROD)
IMPROC_EDT(SMOO)
IMPROC_EDT(CONT)
IMPROC_EDT(THRE)
IMPROC_EDT(EDGE)
IMPROC_EDT(APPR)
IMPROC_EDT(SNAK)
IMPROC_EDT(HOUG)
IMPROC_EDT(CNVX)

void dlg_init(void)
{
	RECT rc;
	HWND htip;
#define BTN_L {{1000,256},{256,0},{256,40},{3,3},{1,1},{5,1},{100,90},{255,200}}
#define BTN_R {{256,180},{1000,2},{30,3},{100,10},{1,0}}
	short btn_l[IDL_NUM][2] = BTN_L;
	short btn_r[IDR_NUM][2] = BTN_R;
	int i;
	TOOLINFO ti;
	WNDCLASSEX wcex;
	
	SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM) LoadIcon(hInstance, "APLACER"));
	SendDlgItemMessage(hDlg, ID_OPEN, BM_SETIMAGE, IMAGE_ICON, (LPARAM) LoadIcon(hInstance, "OPEN"));
	SendDlgItemMessage(hDlg, ID_ZOOMIN, BM_SETIMAGE, IMAGE_ICON, (LPARAM) LoadIcon(hInstance, "ZOOMIN"));
	SendDlgItemMessage(hDlg, ID_ZOOMOUT, BM_SETIMAGE, IMAGE_ICON, (LPARAM) LoadIcon(hInstance, "ZOOMOUT"));
	SendDlgItemMessage(hDlg, ID_RECT, BM_SETIMAGE, IMAGE_ICON, (LPARAM) LoadIcon(hInstance, "RECT"));
	SendDlgItemMessage(hDlg, ID_FLATTEN, BM_SETIMAGE, IMAGE_ICON, (LPARAM) LoadIcon(hInstance, "FLATTEN"));
	SendDlgItemMessage(hDlg, ID_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM) LoadIcon(hInstance, "SAVE"));
	SendDlgItemMessage(hDlg, ID_INFO, BM_SETIMAGE, IMAGE_ICON, (LPARAM) LoadIcon(hInstance, "INFO"));
	SendDlgItemMessage(hDlg, ID_UNDO, BM_SETIMAGE, IMAGE_ICON, (LPARAM) LoadIcon(hInstance, "UNDO"));
	SendDlgItemMessage(hDlg, ID_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM) LoadIcon(hInstance, "CLOSE"));
	SendDlgItemMessage(hDlg, ID_REF, BM_SETIMAGE, IMAGE_ICON, (LPARAM) LoadIcon(hInstance, "REF"));
	for (i = 0; i < IDL_NUM; i++) {
		SendDlgItemMessage(hDlg, ID_UD(i), UDM_SETRANGE, 0, 	(LPARAM) MAKELONG(btn_l[i][0], 0));
		SendDlgItemMessage(hDlg, ID_UD(i), UDM_SETPOS, 0, 	(LPARAM) MAKELONG(btn_l[i][1], 0));
	}
	for (i = 0; i < IDR_NUM; i++) {
		SendDlgItemMessage(hDlg, IDR_UD(i), UDM_SETRANGE, 0,	(LPARAM) MAKELONG(btn_r[i][0], 0));
		SendDlgItemMessage(hDlg, IDR_UD(i), UDM_SETPOS, 0, 	(LPARAM) MAKELONG(btn_r[i][1], 0));
	}
	SetDlgItemInt(hDlg, ID_COLOR_R, 0xC0, 0);
	SetDlgItemInt(hDlg, ID_COLOR_G, 0xC0, 0);
	SetDlgItemInt(hDlg, ID_COLOR_B, 0xC0, 0);
	htip = CreateWindowEx(0, TOOLTIPS_CLASS, 0, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hDlg, 0, hInstance, 0);
	SetWindowPos(htip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
	memset(&ti, 0, sizeof(TOOLINFO));
	ti.cbSize = sizeof(TOOLINFO);
	ti.hwnd = hDlg;
	ti.hinst = hInstance;
	ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
	{
		char *tip[ID_NUM + 3] = {
			"File Open",
				"Info text", "Zoom In", "Zoom Out", "Select Rect",
				"Shift Horz", "Shift Vert", "Width", "Height",
				"Color", "Red", "Green", "Blue",
				"Ref Point", "Ref x", "Ref y",
				"Flatten", "File Save", "Undo", "Close",
				"Macros Folder", "Save Macro", "Invoke Macro"
		};
		for (i = 0; i < ID_NUM + 3; i++) {
			ti.lpszText = tip[i];
			ti.uId = (DWORD) GetDlgItem(hDlg, ID_FIRST + i);
			GetClientRect(GetDlgItem(hDlg, ID_FIRST + i), &rc);
			memcpy(&ti.rect, &rc, sizeof(RECT));
			SendMessage(htip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
		}
	}
	SendMessage(htip, TTM_ACTIVATE, 1, 0);
	memset(&wcex, 0, sizeof(WNDCLASSEX));
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_OWNDC|CS_HREDRAW|CS_VREDRAW;
	wcex.lpfnWndProc = (WNDPROC) hwnd_proc;
	wcex.hInstance = hInstance;
	wcex.hCursor = LoadCursor(0, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH);
	wcex.lpszClassName = "WinPlacerL";
	RegisterClassEx(&wcex);
	wcex.lpfnWndProc = (WNDPROC) hwndR_proc;
	wcex.lpszClassName = "WinPlacerR";
	RegisterClassEx(&wcex);
	ENABLE_CMD(0, 0);
	IMPROC_SET(HIST);
	IMPROC_SET(PYRM);
	IMPROC_SET(FILL);
	IMPROC_SET(RGBG);
	IMPROC_SET(EROD);
	IMPROC_SET(SMOO);
	IMPROC_SET(CONT);
	IMPROC_SET(THRE);
	IMPROC_SET(EDGE);
	IMPROC_SET(APPR);
	IMPROC_SET(SNAK);
	IMPROC_SET(HOUG);
	IMPROC_SET(CNVX);
}

void dlg_size(LPARAM lParam)
{
	int i;
	if (hWnd) 
		MoveWindow(hWnd, 61, 37, (LOWORD(lParam)-122)/2, HIWORD(lParam)-59, 1);
	if (hWndR) 
		MoveWindow(hWndR, 61+(LOWORD(lParam)-122)/2+2, 37, (LOWORD(lParam)-122)/2-2, HIWORD(lParam)-59, 1);
	for (i = IDR_NUM - 1; i >= 0; i--) {
		MoveWindow(GetDlgItem(hDlg, IDR_UD(i)), LOWORD(lParam) - 16, 55 + i * 40, 16, 20, 1);
		MoveWindow(GetDlgItem(hDlg, IDR_EDT(i)),LOWORD(lParam) - 60, 55 + i * 40, 44, 20, 1);
		MoveWindow(GetDlgItem(hDlg, IDR_ID(i)), LOWORD(lParam) - 60, 35 + i * 40, 60, 20, 1);
	}
	MoveWindow(GetDlgItem(hDlg, ID_MSG), 0, HIWORD(lParam) - 16, LOWORD(lParam) - 61, 0, 1);
}

void dlg_info(void)
{
	MSGBOXPARAMS mb;
	memset(&mb, 0, sizeof(MSGBOXPARAMS));
	mb.cbSize = sizeof(MSGBOXPARAMS);
	mb.hwndOwner = hDlg;
	mb.hInstance = hInstance;
	mb.lpszText = PROG_ABOUT;
	mb.lpszCaption = PROG_NAME;
	mb.dwStyle = MB_USERICON | MB_OK;
	mb.lpszIcon = MAKEINTRESOURCE(1);
	MessageBoxIndirect(&mb);
}

void dlg_open(void)
{
	char *p, buf[MAX_PATH] = {
		0                                                                                                                                                                                                                                                                                               };
	OPENFILENAME ofn;
	IplImage *img = 0;
	RECT rc;

	GetClientRect(hDlg, &rc);
	memset(&ofn, 0, sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = hDlg;
	ofn.lpstrFile = buf;
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_HIDEREADONLY;
	ofn.lpstrFilter = "TIF\0*.tif\0";
	if (!GetOpenFileName(&ofn)
			|| !(p = strrchr(buf, '.'))
			|| strlen(++p) != 3
			|| p[0] != 'T' && p[0] != 't'
			|| p[1] != 'I' && p[1] != 'i'
			|| p[2] != 'F' && p[2] != 'f')
		return;
	CURS(hDlg, IDC_WAIT);
	if (!tif_read(buf, &img) || !img) {
		CURS(hWnd, IDC_ARROW);
		MessageBox(hDlg, "Unable to read TIF.", PROG_NAME, MB_OK);
		return;
	}
	hWnd = CreateWindow("WinPlacerL", "",
		WS_BORDER|WS_VISIBLE|WS_CHILD|WS_HSCROLL|WS_VSCROLL,
		61, 37, (rc.right - 122)/2, rc.bottom - 59, hDlg, (HMENU) ID_HWND, hInstance, img);
	cvReleaseImage(&img);
	ENABLE_CMD(1, 0);
    CURS(hDlg, IDC_ARROW);
}

void dlg_save(void)
{
	OPENFILENAME ofn;
	char buf[MAX_PATH];
	memset(buf, 0, MAX_PATH);
	memset(&ofn, 0, sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = hDlg;
	ofn.lpstrFile = buf;
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER;
	ofn.lpstrFilter = "Enhanced MetaFile\0*.emf\0";
	if (GetSaveFileName(&ofn) && buf[0]) {
		HDC hdc;
		if ((!ofn.nFileExtension || !*(ofn.lpstrFile + ofn.nFileExtension)) && ofn.nFilterIndex)
			strcat(buf, ".emf");
		hdc = CreateEnhMetaFile(GetDC(hWndR), buf, 0, "\0\0");
		SendMessage(hWndR, WM_COMMAND, ID_SAVE, (LPARAM) hdc);
		DeleteEnhMetaFile(CloseEnhMetaFile(hdc));
	}
}

BOOL CALLBACK color_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) {
	case WM_INITDIALOG:
		return 1;
	case WM_DRAWITEM:
		DRAW_COLOR(lParam);
		return 1;
	case WM_COMMAND: 
		{
			COLORREF color = ((COLORREF *) office)[min(NUM_COLORS - 1, max(0, LOWORD(wParam) - 1))];
			SetDlgItemInt(hDlg, ID_COLOR_R, GetBValue(color), 0); 
			SetDlgItemInt(hDlg, ID_COLOR_G, GetGValue(color), 0);
			SetDlgItemInt(hDlg, ID_COLOR_B, GetRValue(color), 0);
			EndDialog(hdlg, 0);
		}
		return 1;
	}
	return 0;
}

int dlg_drawitem(LPARAM lParam)
{
	if (((DRAWITEMSTRUCT *) lParam)->CtlID == ID_COLOR) {
		int i;
		COLORREF color = RGB(
			GetDlgItemInt(hDlg, ID_COLOR_B, 0, 0),
			GetDlgItemInt(hDlg, ID_COLOR_G, 0, 0),
			GetDlgItemInt(hDlg, ID_COLOR_R, 0, 0)
			);
		for (i = 0; i < NUM_COLORS; i++) 
			if (((COLORREF *) office)[i] == color) {
				((DRAWITEMSTRUCT *) lParam)->CtlID = i + 1;
				DRAW_COLOR(lParam);
				return 1;
			}
	}
	return 0;
}

BOOL CALLBACK hwnd_proc(HWND hwnd, UINT mes, WPARAM wParam, LPARAM lParam)
{
	typedef enum {
		MODE_NON, MODE_RECT, MODE_RECT_DRAW
	} mode_t;
	static mode_t mode;
	static SCROLLINFO si_x, si_y;
	static struct bmi bmis[5];
	static int imgs_i, imgs_n;
	static RECT rect;
	static IplImage *imgs[5];
	static HWND hWnd;

	switch (mes) {
	case WM_CREATE:
		{
			int i;
			CvSize sz;
			IplImage *img = (IplImage *) (((CREATESTRUCT *) lParam)->lpCreateParams);

			imgs_n = max(0, 4 - img->width / 256);
			sz.width = img->width;
			sz.height = img->height;
			imgs[imgs_n] = cvCreateImage(sz, IPL_DEPTH_8U, img->nChannels);
			cvCopy(img, imgs[imgs_n], 0);
			for (i = imgs_n - 1; i >= 0; i--) {
				sz.width = imgs[i + 1]->width << 1;
				sz.height = imgs[i + 1]->height << 1;
				imgs[i] = cvCreateImage(sz, IPL_DEPTH_8U, img->nChannels);
				cvResize(imgs[i + 1], imgs[i], CV_INTER_LINEAR);
			}
			for (i = imgs_n + 1; i < 5; i++) {
				sz.width = imgs[i - 1]->width >> 1;
				sz.height = imgs[i - 1]->height >> 1;
				imgs[i] = cvCreateImage(sz, IPL_DEPTH_8U, img->nChannels);
				cvResize(imgs[i - 1], imgs[i], CV_INTER_LINEAR);
			}
			for (i = 0; i < 5; i++) {
				SET_BMI(bmis[i], imgs[i]->width, imgs[i]->height, imgs[i]->nChannels);
			}
			imgs_i = 0;
			SET_SI((DWORD) (imgs[imgs_i]->width / 3), (DWORD) (imgs[imgs_i]->height / 3), (DWORD) imgs[imgs_i]->width, (DWORD) imgs[imgs_i]->height);
		}
		break;
	case WM_DESTROY:
		if (hWndR)
			SendMessage(hWndR, WM_CLOSE, 0, 0);
		{
			int i;
			for (i = 0; i < 5; i++) {
				cvReleaseImage(&imgs[i]);
				memset(bmis + i, 0, sizeof(struct bmi));
			}
		}
		mode = 0;
		memset(&rect, 0, sizeof(rect));
		ENABLE_CMD(0, 0);
		hWnd = 0;
		return 0;
	case WM_HSCROLL:
	case WM_VSCROLL:
		SCROLL(imgs[imgs_i]->width, imgs[imgs_i]->height);
		break;
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HPALETTE hpal = 0, old = 0;
			RECT rc;

			GetClientRect(hwnd, &rc);
			BeginPaint(hwnd, &ps);
			FillRect(ps.hdc, &rc, GetSysColorBrush(WHITE_BRUSH));
			if (imgs[imgs_i]->nChannels == 1) {
				if (hpal = CreatePalette(&bmis[imgs_i].lpal)) {
					old = SelectPalette(ps.hdc, hpal, 1);
					RealizePalette(ps.hdc);
				}
			}
			bmis[imgs_i].bi.bmiHeader.biHeight = -(si_y.nPos, imgs[imgs_i]->height - si_y.nPos);
			SetDIBitsToDevice(ps.hdc, 0, 0,
				imgs[imgs_i]->width - si_x.nPos,
				imgs[imgs_i]->height - si_y.nPos,
				si_x.nPos, si_y.nPos,
				si_y.nPos, imgs[imgs_i]->height - si_y.nPos,
				imgs[imgs_i]->imageData + si_y.nPos * imgs[imgs_i]->widthStep,
				&bmis[imgs_i].bi, imgs[imgs_i]->nChannels == 1 ? DIB_PAL_COLORS : DIB_RGB_COLORS);
			if (imgs[imgs_i]->nChannels == 1) {
				hpal = SelectPalette(ps.hdc, old, 1);
				if (hpal)
					DeleteObject(hpal);
			}
			if (rect.left != rect.right && rect.top != rect.bottom) {
				HPEN old_pen, hpen = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
				HBRUSH old_br = SelectObject(ps.hdc, GetStockObject(NULL_BRUSH));
				old_pen = SelectObject(ps.hdc, hpen);
				Rectangle(ps.hdc, rect.left - si_x.nPos, rect.top - si_y.nPos,
					rect.right - si_x.nPos, rect.bottom - si_y.nPos);
				hpen = SelectObject(ps.hdc, old_pen);
				if (hpen)
					DeleteObject(hpen);
				SelectObject(ps.hdc, old_br);
			}
			EndPaint(hwnd, &ps);
		}
		break;
	case WM_SIZE:
		SET_SI((DWORD) si_x.nPos, (DWORD) si_y.nPos, (DWORD) imgs[imgs_i]->width, (DWORD) imgs[imgs_i]->height);
		break;
	case WM_MOUSEMOVE:
		if (mode == MODE_RECT_DRAW) {
			int c_x = min(imgs[imgs_i]->width  - 1, max(0, si_x.nPos + LOWORD(lParam)));
			int c_y = min(imgs[imgs_i]->height - 1, max(0, si_y.nPos + HIWORD(lParam)));
			INVAL_RECT(rect, 0);
			SetDlgItemInt(hDlg, ID_RECT_right, rect.right = c_x, 0);
			SetDlgItemInt(hDlg, ID_RECT_bottom, rect.bottom = c_y, 0);
			INVAL_RECT(rect, 1);
		}
		break;
	case WM_LBUTTONDOWN:
		{
			int c_x = min(imgs[imgs_i]->width  - 1, max(0, si_x.nPos + LOWORD(lParam)));
			int c_y = min(imgs[imgs_i]->height - 1, max(0, si_y.nPos + HIWORD(lParam)));
			if (mode == MODE_RECT) {
				mode = MODE_RECT_DRAW;
				SetDlgItemInt(hDlg, ID_RECT_left, rect.right = rect.left = c_x, 0);
				SetDlgItemInt(hDlg, ID_RECT_top, rect.bottom = rect.top = c_y, 0);
			}
			else if (mode == MODE_RECT_DRAW) {
				mode = 0;
				CURS(hwnd, IDC_ARROW);
				SetDlgItemInt(hDlg, ID_RECT_right, rect.right = c_x, 0);
				SetDlgItemInt(hDlg, ID_RECT_bottom, rect.bottom = c_y, 0);
				if (hWndR) {
					DestroyWindow(hWndR);
					hWndR = 0;
				}
				if (rect.left < rect.right && rect.top < rect.bottom
						&& (rect.right - rect.left) < 1000
						&& (rect.bottom - rect.top) < 1000) {
					RECT rc;
					CvRect roi;
					roi.x = rect.left;
					roi.y = rect.top;
					roi.width = rect.right - rect.left;
					roi.height = rect.bottom - rect.top;
					cvSetImageROI(imgs[imgs_i], roi);
					GetClientRect(hDlg, &rc);
					hWndR = CreateWindow("WinPlacerR", "",
						WS_BORDER|WS_VISIBLE|WS_CHILD|WS_HSCROLL|WS_VSCROLL,
						61 + (rc.right - 122)/2+2, 37, (rc.right - 122)/2-2,rc.bottom - 59, hDlg, (HMENU) ID_HWNDR, hInstance, imgs[imgs_i]);
				}
			}
			InvalidateRect(hwnd, 0, 0);
			UpdateWindow(hwnd);
		}
		break;
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case ID_ZOOMIN:
			if (imgs_i > 0) {
				int x = 2 * si_x.nPos + si_x.nPage / 2;
				int y = 2 * si_y.nPos + si_y.nPage / 2;
				imgs_i--;
				SET_SI((DWORD) x, (DWORD) y, (DWORD) imgs[imgs_i]->width, (DWORD) imgs[imgs_i]->height);
				rect.left = min(2 * rect.left, imgs[imgs_i]->width);
				rect.right = min(2 * rect.right, imgs[imgs_i]->width);
				rect.top = min(2 * rect.top, imgs[imgs_i]->height);
				rect.bottom = min(2 * rect.bottom, imgs[imgs_i]->height);
				SetDlgItemInt(hDlg, ID_RECT_left, rect.left, 0);
				SetDlgItemInt(hDlg, ID_RECT_top, rect.top, 0);
				SetDlgItemInt(hDlg, ID_RECT_right, rect.right, 0);
				SetDlgItemInt(hDlg, ID_RECT_bottom, rect.bottom, 0);
			}
			InvalidateRect(hwnd, 0, 0);
			UpdateWindow(hwnd);
			break;
		case ID_ZOOMOUT:
			if (imgs_i < 4) {
				int x = si_x.nPos / 2 - si_x.nPage / 4;
				int y = si_y.nPos / 2 - si_y.nPage / 4;
				imgs_i++;
				SET_SI((DWORD) x, (DWORD) y, (DWORD) imgs[imgs_i]->width, (DWORD) imgs[imgs_i]->height);
				rect.left = min(rect.left / 2, imgs[imgs_i]->width);
				rect.right = min(rect.right / 2, imgs[imgs_i]->width);
				rect.top = min(rect.top / 2, imgs[imgs_i]->height);
				rect.bottom = min(rect.bottom / 2, imgs[imgs_i]->height);
				SetDlgItemInt(hDlg, ID_RECT_left, rect.left, 0);
				SetDlgItemInt(hDlg, ID_RECT_top, rect.top, 0);
				SetDlgItemInt(hDlg, ID_RECT_right, rect.right, 0);
				SetDlgItemInt(hDlg, ID_RECT_bottom, rect.bottom, 0);
			}
			InvalidateRect(hwnd, 0, 0);
			UpdateWindow(hwnd);
			break;
		case ID_RECT:
			if (mode == MODE_NON) {
				CURS(hwnd, IDC_CROSS);
				mode = MODE_RECT;
			}
			else {
				mode = 0;
				CURS(hwnd, IDC_ARROW);
			}
			break;
		case ID_UNDO:
			{
				struct {
					int n;
					void *buf;
				} b = {0, 0};
				SendMessage(hWndR, WM_COMMAND, ID_UNDO, (LPARAM) &b);
				if (hWndR) {
					DestroyWindow(hWndR);
					hWndR = 0;
				}
				if (rect.left < rect.right && rect.top < rect.bottom
					&& (rect.right - rect.left) < 1000
					&& (rect.bottom - rect.top) < 1000) {
					RECT rc;
					CvRect roi;
					roi.x = rect.left;
					roi.y = rect.top;
					roi.width = rect.right - rect.left;
					roi.height = rect.bottom - rect.top;
					cvSetImageROI(imgs[imgs_i], roi);
					GetClientRect(hDlg, &rc);
					hWndR = CreateWindow("WinPlacerR", "",
						WS_BORDER|WS_VISIBLE|WS_CHILD|WS_HSCROLL|WS_VSCROLL,
						61 + (rc.right - 122)/2+2, 37, (rc.right - 122)/2-2,rc.bottom - 59, hDlg, (HMENU) ID_HWNDR, hInstance, imgs[imgs_i]);
					InvalidateRect(hWndR, 0, 1);
					UpdateWindow(hWndR);
				}
				{
					int i;
					for (i = 0; i < b.n - 1; i++) {
						CvPoint *el = (CvPoint *) b.buf + i;
						SendDlgItemMessage(hDlg, el->x + 2, UDM_SETPOS, 0, (LPARAM) MAKELONG((short) el->y, 0));
						SendMessage(hDlg, WM_COMMAND, el->x, 0);
					}
				}
				cvFree(&b.buf);
			}
			break;
		case ID_FLATTEN:
			{
				struct {
					int n;
					void *buf;
				} b = {0, 0};
				SendMessage(hWndR, WM_COMMAND, ID_UNDO, (LPARAM) &b);
				DestroyWindow(hWndR);
				{
					RECT rc;
					GetClientRect(hDlg, &rc);
					hWndR = CreateWindow("WinPlacerR", "",
						WS_BORDER|WS_VISIBLE|WS_CHILD|WS_HSCROLL|WS_VSCROLL,
						61 + (rc.right - 122)/2+2, 37, (rc.right - 122)/2-2,rc.bottom - 59, hDlg, (HMENU) ID_HWNDR, hInstance, imgs[imgs_n]);
				}
				InvalidateRect(hWndR, 0, 1);
				UpdateWindow(hWndR);
				{
					int i;
					for (i = 0; i < b.n - 1; i++) {
						CvPoint *el = (CvPoint *) b.buf + i;
						SendDlgItemMessage(hDlg, el->x + 2, UDM_SETPOS, 0, (LPARAM) MAKELONG((short) el->y, 0));
						SendMessage(hDlg, WM_COMMAND, el->x, 0);
					}
				}
				cvFree(&b.buf);
			}
			{
				HENHMETAFILE hemf;
				RECT rc;
				HDC hdc;
				GetClientRect(hWndR, &rc);
				hdc = CreateEnhMetaFile(GetDC(0), 0, &rc, "\0\0");
				SendMessage(hWndR, WM_COMMAND, ID_PAINT, (LPARAM) hdc);
				hemf = CloseEnhMetaFile(hdc);
				OpenClipboard(0);
				EmptyClipboard();
				SetClipboardData(CF_ENHMETAFILE, hemf);
				CloseClipboard();
				DeleteEnhMetaFile(hemf);
				DeleteDC(hdc);
			}
			break;
		}
		break;
	}
	return DefWindowProc(hwnd, mes, wParam, lParam);
}

BOOL CALLBACK hwndR_proc(HWND hwnd, UINT mes, WPARAM wParam, LPARAM lParam)
{
	static IplImage *img, *img_seq;
	static double s;
	static POINT ref;
	typedef enum {
		MODE_NON, MODE_REF, MODE_CNVX
	}
	modeR_t;
	static modeR_t mode;
	static CvSeq *seq, *cmd_seq;

	switch (mes) {
	case WM_CREATE:
		{
			IplImage *img0 = (IplImage *) (((CREATESTRUCT *) lParam)->lpCreateParams);
			CvRect rc = cvGetImageROI(img0);
			CvSize sz;
			sz.width = rc.width;
			sz.height = rc.height;
			img = cvCreateImage(sz, 8, img0->nChannels);
			cvCopy(img0, img, 0);
			img_seq = cvCreateImage(sz, 8, 3);
			cvZero(img_seq);
			cvResetImageROI(img0);
			seq = cvCreateSeq(CV_SEQ_POLYGON, sizeof(CvContour), sizeof(CvPoint), cvCreateMemStorage(0));	
			cmd_seq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), cvCreateMemStorage(0));
			ENABLE_CMD(1, 1);
		}
		return 1;
	case WM_DESTROY:
		s = mode = 0;
		memset(&ref, 0, sizeof(POINT));
		cvReleaseImage(&img);
		cvReleaseMemStorage(&seq->storage);
		cvReleaseMemStorage(&cmd_seq->storage);
		hWndR = 0;
		ENABLE_CMD(1, 0);
		return 0;
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			BeginPaint(hwnd, &ps);
			SendMessage(hwnd, WM_COMMAND, ID_PAINT, (LPARAM) ps.hdc);
			EndPaint(hwnd, &ps);
		}
		break;
	case WM_COMMAND:
		{
			int cmd = LOWORD(wParam);
			if (cmd_seq && cmd >= IDL_FIRST && cmd <= IDR_LAST 
				&& cmd != ID_MSAVE && cmd != ID_UNDO) {
				CvPoint el;
				el.x = cmd;
				el.y = SendDlgItemMessage(hDlg, cmd + 2, UDM_GETPOS, 0, 0);
				cvSeqPush(cmd_seq, &el);
			}
		}
		switch (LOWORD(wParam)) {
		case ID_HIST:
			imp_hist(&img);
			break;
		case ID_PYRM:
			imp_pyrm(&img);
			break;
		case ID_FILL:
			imp_fill(&img, &ref);
			break;
		case ID_RGBG:
			imp_rgbg(&img);
			break;
		case ID_EROD:
			imp_erod(&img);
			break;
		case ID_SMOO:
			imp_smoo(&img);
			break;
		case ID_CONT:
			imp_cont(&img);
			break;
		case ID_THRE:
			imp_thre(&img);
			break;
		case ID_EDGE:
			imp_edge(&img, &seq, img_seq);
			break;
		case ID_APPR:
			imp_appr(&img, &seq, img_seq);
			break;
		case ID_SNAK:
			imp_snak(&img, &seq, img_seq);
			break;
		case ID_HOUG:
			imp_houg(&img, &seq, img_seq);
			break;
		case ID_CNVX:
			mode |= MODE_CNVX;
			imp_cnvx(&img, &seq, img_seq);
			break;
		case ID_PAINT: 
			{
				HBITMAP hbmp, old_bm;
				HDC dc = (HDC) lParam;
				{
					RECT rc;
					struct bmi bm, bm0;
					
					SET_BMI(bm, img->width, img->height, img->nChannels);
					GetClientRect(hwnd, &rc);
					SET_BMI(bm0, rc.right, rc.bottom, 3);
					s = min((double) rc.right / img->width, (double) rc.bottom / img->height);
					SetStretchBltMode(dc, COLORONCOLOR);
					hbmp = CreateDIBitmap(dc, &bm0.bi.bmiHeader, 0, 0, &bm0.bi, DIB_RGB_COLORS);
					old_bm = SelectObject(dc, hbmp);
					StretchDIBits(dc, 0, 0, (int) (s * img->width), (int) (s * img->height), 0, 0, img->width, img->height,
					img->imageData, &bm.bi, DIB_RGB_COLORS, SRCCOPY);
					StretchDIBits(dc, 0, 0, (int) (s * img->width), (int) (s * img->height), 0, 0, img->width, img->height,
					img_seq->imageData, &bm.bi, DIB_RGB_COLORS, SRCPAINT);
				}
				{
					HPEN old_pen, hpen = CreatePen(PS_SOLID, 3, RGB(181, 0, 0));
					HBRUSH old_br = SelectObject(dc, GetStockObject(NULL_BRUSH));
					old_pen = SelectObject(dc, hpen);
					Ellipse(dc, max(0, (int) (s * ref.x - 5)), max(0, (int) (s * ref.y - 5)), (int) (s * ref.x + 5), (int) (s * ref.y + 5));
					hpen = SelectObject(dc, old_pen);
					if (hpen)
						DeleteObject(hpen);
					SelectObject(dc, old_br);
				}
				SelectObject(dc, old_bm);
				if (hbmp)
					DeleteObject(hbmp);
			}
			break;
		case ID_REF:
			CURS(hwnd, "ref");
			mode |= MODE_REF;
			ref.x = ref.y = 0;
			break;
		case ID_SAVE:
			if (seq->total) {
				HDC dc = (HDC) lParam;
				CvSeq *p = seq;
				CvPoint *pt;
				HPEN old_pen, hpen = CreatePen(PS_SOLID, 1, RGB(GetDlgItemInt(hDlg, ID_COLOR_R, 0, 0), GetDlgItemInt(hDlg, ID_COLOR_G, 0, 0),GetDlgItemInt(hDlg, ID_COLOR_B, 0, 0)));
				old_pen = SelectObject(dc, hpen);
				while (p) {
					int i;
					HBRUSH old_hbr, hbr;
					if (mode & MODE_CNVX) {
						CvPoint2D32f center;
						float radius;
						cvMinEnclosingCircle(p, &center, &radius);
						if (center.x > 0 && center.x < img->width
							&& center.y > 0 && center.y < img->height) {
							LOGBRUSH lbr;
							char *b = img->imageData + (int) center.x * img->nChannels 
								+ (int) center.y * img->widthStep;
							lbr.lbStyle = BS_SOLID;
							lbr.lbColor = img->nChannels == 3 
								? RGB(*b, *(b + 1), *(b + 2)) : RGB(*b, *b, *b);
							hbr = CreateBrushIndirect(&lbr);
							BeginPath(dc);
							old_hbr = SelectObject(dc, hbr);
						}
					}
					for (i = 0; i < p->total; i++) {
						pt = (CvPoint*) cvGetSeqElem(p, i, 0);
						if (i)
							LineTo(dc, (int) (s * pt->x), (int) (s * pt->y));
						else
							MoveToEx(dc, (int) (s * pt->x), (int) (s * pt->y), 0);
					}
					if (mode & MODE_CNVX) {
						EndPath(dc);
						StrokeAndFillPath(dc);
						hbr = SelectObject(dc, old_hbr);
						if (hbr)
							DeleteObject(hbr);
					}
					p = p->h_next;
				}
				hpen = SelectObject(dc, old_pen);
				if (hpen)
					DeleteObject(hpen);
			}
			break;
		case ID_MSAVE:
			macro_save(cmd_seq);
			break;
		case ID_UNDO:
			{
				struct {
					int n;
					void *buf;
				} *pb = (void *) lParam;
				pb->buf = cvAlloc(cmd_seq->total * sizeof(CvPoint));
				pb->buf = cvCvtSeqToArray(cmd_seq, pb->buf, CV_WHOLE_SEQ);
				pb->n = cmd_seq->total;
			}
			break;
		}
		if (LOWORD(wParam) != ID_PAINT) {
			InvalidateRect(hwnd, 0, 1);
			UpdateWindow(hwnd);
		}
		break;
	case WM_LBUTTONDOWN:
		if (mode & MODE_REF) {
			unsigned char *b;
			COLORREF color;
			mode &= ~MODE_REF;
			CURS(hwnd, IDC_ARROW);
			ref.x = max(0, min((int) (LOWORD(lParam) / s), img->width - 1));
			ref.y = max(0, min((int) (HIWORD(lParam) / s), img->height - 1));
			b = img->imageData + img->nChannels * ref.x + ref.y * img->widthStep;
			color = img->nChannels == 1 ? CV_RGB(*b, *b, *b) : CV_RGB(*b, *(b + 1), *(b + 2));
			SetDlgItemInt(hDlg, ID_REF_x, ref.x, 0);
			SetDlgItemInt(hDlg, ID_REF_y, ref.y, 0);
			SetDlgItemInt(hDlg, ID_COLOR_R, GetRValue(color), 0);
			SetDlgItemInt(hDlg, ID_COLOR_G, GetGValue(color), 0);
			SetDlgItemInt(hDlg, ID_COLOR_B, GetBValue(color), 0);
			InvalidateRect(hwnd, 0, 0);
			UpdateWindow(hwnd);
		}
		break;
	}
	return DefWindowProc(hwnd, mes, wParam, lParam);
}
