#ifndef PLACER_H
#define PLACER_H

#include <stdio.h>
#include <io.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <time.h>
#include <assert.h>
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <shellapi.h>
#include <shlobj.h>

#include "cv.h"

#include "rc.h"

#define PROG_ABOUT \
"\
PLACER is the freeware program to make raster image vectorization,\n\
and proceedings (thanx Sneginsk!) of (c)Intel Open CV library\n\n\
An opened uncompressed TIF image will appear at the view on the left,\n\
which processing part selected by SelectRect tool will be on the right\n\
Tool palette on the left side is for raster processing, on the right side - vectorization\n\
For FloodFill proc - RefPoint tools should be used with the right view\n\
Operation sequence could be undone or saved in a text file (PlacerMacro)\n\
MacroLoad sets current folder from that all the macros are listed\n\
Selecting a name from the list will directly execute this macro\n\
Flatten and Save will output result (EMF) to clipboard and file\n\n\
\t\tTo my parents\n\
\tEduard Svjatoslavovich and\n\tLudmila Aleksandrovna Barkhatov\n\n\
\tFor feedback mailto:abarkhatov@yahoo.com "

enum {
	TIF_SUBFT = 254, TIF_W = 256, TIF_H = 257, TIF_BIT_S = 258,
	TIF_CMP = 259, TIF_PHOT = 262, TIF_SP_OFF = 273, TIF_SAM_P = 277,
	TIF_ROW_SP = 278, TIF_SP_CNT = 279, TIF_C_MAP = 320
};

#define SWAP_2(b,m) \
	((unsigned char)(m)==0x4D?((*(unsigned short*)(b)>>8)&0xFF) \
	+((*(unsigned short*)(b)<<8)&0xFF00):*(unsigned short*)(b))
#define SWAP_4(b,m) \
	((unsigned char)(m)==0x4D?((*(unsigned int*)(b)>>24)&0xFF) \
	+((*(unsigned int*)(b)>>8)&0xFF00) \
	+((*(unsigned int*)(b)<<8)&0xFF0000) \
	+((*(unsigned int*)(b)<<24)&0xFF000000):*(unsigned int*)(b))

typedef struct _tiftag_t {
	int tag, type, len, val;
}
tiftag_t;

#define IS_FLT_ZERO(x) (fabs(x) < 2 * FLT_EPSILON)

#define ERROR_MSG(m,txt) { \
	char buf[128]; \
	sprintf(buf,"Unable to %s:%s\n", m, txt); \
	SendDlgItemMessage(hDlg, ID_MSG, SB_SETTEXT, 0, (LPARAM)buf); \
}

#define STATUS_MSG(m) { \
	SendDlgItemMessage(hDlg, ID_MSG, SB_SETTEXT, 0, (LPARAM)m); \
}

#define SWAP_BUF(buf,w,h,pad) { \
	unsigned char *p, b; \
	int i, j; \
	p = buf; \
	for (i = 0; i < h; i++) { \
		for (j = 0; j < w; j++, p += 3) { \
			b = *p; \
			*p = *(p + 2); \
			*(p + 2) = b; \
		} \
		p += pad; \
	} \
}

#define BREAD(name, sz, b) \
{ \
	size_t n; \
	unsigned char *buf = 0; \
	FILE *flh; \
	if (!(flh = fopen(name, "rb")) \
		|| fseek(flh, 0, SEEK_END) \
		|| (n = ftell(flh)) == (size_t) -1 \
		|| fseek(flh, 0, SEEK_SET) \
		|| !(buf = (unsigned char *) malloc(n)) \
		|| fread(buf, 1, n, flh) != n) { \
		ERROR_MSG(name, "Open"); \
		if (buf) \
			free(buf); \
		*b = 0; \
		n = 0; \
	} \
	if (sz) \
		*sz = n; \
	fclose(flh); \
	*b = buf; \
}

struct bmi {
	BITMAPINFO bi;
	short c[256];
	LOGPALETTE lpal;
	PALETTEENTRY b[256];
};

#define NUM_COLORS 40

#define SET_SI(x, y, w, h) \
{ \
	RECT rc; \
	GetClientRect(hwnd, &rc); \
	memset(&si_x, 0, sizeof(SCROLLINFO)); \
	memset(&si_y, 0, sizeof(SCROLLINFO)); \
	si_x.cbSize = si_y.cbSize = sizeof(SCROLLINFO); \
	si_x.fMask = si_y.fMask = SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL | SIF_POS; \
	si_x.nPos = x; \
	si_y.nPos = y; \
	si_x.nPage = rc.right; \
	si_y.nPage = rc.bottom; \
	si_x.nMax = w; \
	si_y.nMax = h; \
	if (si_x.nPos < 0 || si_x.nPage > w) \
		si_x.nPos = 0; \
	else if (si_x.nPos + si_x.nPage > w) \
		si_x.nPos = w - si_x.nPage; \
	if (si_y.nPos < 0 || si_y.nPage > h) \
		si_y.nPos = 0; \
	else if (si_y.nPos + si_y.nPage > h) \
		si_y.nPos = h - si_y.nPage; \
	SetScrollInfo(hwnd, SB_HORZ, &si_x, 1); \
	SetScrollInfo(hwnd, SB_VERT, &si_y, 1); \
}

#define ENABLE_CMD(f1, f2) \
{ \
	int i; \
	EnableWindow(GetDlgItem(hDlg, ID_ZOOMIN), f1); \
	EnableWindow(GetDlgItem(hDlg, ID_ZOOMOUT), f1); \
	EnableWindow(GetDlgItem(hDlg, ID_RECT), f1); \
	EnableWindow(GetDlgItem(hDlg, ID_RECT_left), f1); \
	EnableWindow(GetDlgItem(hDlg, ID_RECT_top), f1); \
	EnableWindow(GetDlgItem(hDlg, ID_RECT_right), f1); \
	EnableWindow(GetDlgItem(hDlg, ID_RECT_bottom), f1); \
	EnableWindow(GetDlgItem(hDlg, ID_COLOR), f2); \
	EnableWindow(GetDlgItem(hDlg, ID_COLOR_R), f2); \
	EnableWindow(GetDlgItem(hDlg, ID_COLOR_G), f2); \
	EnableWindow(GetDlgItem(hDlg, ID_COLOR_B), f2); \
	EnableWindow(GetDlgItem(hDlg, ID_REF), f2); \
	EnableWindow(GetDlgItem(hDlg, ID_REF_x), f2); \
	EnableWindow(GetDlgItem(hDlg, ID_REF_y), f2); \
	EnableWindow(GetDlgItem(hDlg, ID_FLATTEN), f2); \
	EnableWindow(GetDlgItem(hDlg, ID_SAVE), f2); \
	EnableWindow(GetDlgItem(hDlg, ID_UNDO), f2); \
	EnableWindow(GetDlgItem(hDlg, ID_CLOSE), f1); \
	for (i = IDL_FIRST; i <= IDL_LAST; i++) \
		EnableWindow(GetDlgItem(hDlg, i),  f2); \
	for (i = IDR_FIRST; i <= IDR_LAST; i++)  \
		EnableWindow(GetDlgItem(hDlg, i),  f2); \
}

#define DRAW_COLOR(lp) \
{ \
	DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *) lp; \
	RECT rc; \
	memcpy(&rc, &dis->rcItem, sizeof(RECT)); \
	rc.bottom--; \
	FrameRect(dis->hDC, &rc, GetStockObject(BLACK_BRUSH)); \
	rc.left++, rc.top++, rc.right--, rc.bottom--; \
	FrameRect(dis->hDC, &rc, GetStockObject(BLACK_BRUSH)); \
	rc.left++, rc.top++, rc.right--, rc.bottom--; \
	FrameRect(dis->hDC, &rc, GetStockObject(WHITE_BRUSH)); \
	rc.left++, rc.top++, rc.right--, rc.bottom--; \
	FillRect(dis->hDC, &rc, \
		CreateSolidBrush(dis->CtlID < NUM_COLORS ? ((COLORREF *) office)[dis->CtlID - 1] : 0)); \
}

#define CURS(hwnd, idc) \
{ \
	HCURSOR hcurs; \
	if (idc == IDC_ARROW || idc == IDC_WAIT || idc == IDC_CROSS || idc == IDC_SIZEALL \
	|| idc == IDC_SIZENWSE || idc == IDC_SIZENESW || idc == IDC_SIZEWE || idc == IDC_SIZENS) \
		hcurs = LoadCursor(0, idc); \
	else \
		hcurs = LoadCursor(hInstance, idc); \
	SetClassLong(hwnd, GCL_HCURSOR, (LONG) hcurs); \
	SetCursor(hcurs); \
	if (idc != IDC_ARROW) \
		SetCapture(hwnd); \
	else \
		ReleaseCapture(); \
	ShowCursor(1); \
}

#define INVAL_RECT(rc, f) \
{ \
    HRGN hrgn0 = CreateRectRgn(rc.left - si_x.nPos - 2, \
		rc.top - si_y.nPos - 2, rc.right - si_x.nPos + 2, \
		rc.bottom - si_y.nPos + 2); \
    HRGN hrgn1 = CreateRectRgn(rc.left - si_x.nPos + 2, \
		rc.top - si_y.nPos + 2, rc.right - si_x.nPos - 2, \
		rc.bottom - si_y.nPos - 2); \
	CombineRgn(hrgn0, hrgn0, hrgn1, RGN_DIFF); \
	InvalidateRgn(hwnd, hrgn0, 1); \
	if (f) \
		UpdateWindow(hwnd);	\
	DeleteObject(hrgn0); \
	DeleteObject(hrgn1); \
}

#define SET_BMI(bm, w, h, bp) \
{ \
	memset(&bm, 0, sizeof(bm)); \
	bm.bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); \
	bm.bi.bmiHeader.biPlanes = 1; \
	bm.bi.bmiHeader.biWidth = w; \
	bm.bi.bmiHeader.biHeight = -h; \
	bm.bi.bmiHeader.biBitCount = bp * 8; \
	bm.bi.bmiHeader.biCompression = BI_RGB; \
	if (bp == 1) { \
		int ii; \
		bm.lpal.palVersion = 0x300; \
		bm.lpal.palNumEntries = 256; \
		for (ii = 0; ii < 256; ii++) \
			((short *) bm.bi.bmiColors)[ii] \
			= bm.lpal.palPalEntry[ii].peRed \
			= bm.lpal.palPalEntry[ii].peGreen \
			= bm.lpal.palPalEntry[ii].peBlue \
			= ii; \
	} \
}

#define SCROLL(w, h) \
{ \
	RECT rc; \
	int i, d; \
	switch (LOWORD(wParam)) { \
	case SB_PAGELEFT: \
		d = -50; \
		break; \
	case SB_PAGERIGHT: \
		d = 50; \
		break; \
	case SB_LINELEFT: \
		d = -5; \
		break; \
	case SB_LINERIGHT: \
		d = 5; \
		break; \
	case SB_THUMBTRACK: \
		d = HIWORD(wParam) - (mes == WM_HSCROLL ? si_x.nPos : si_y.nPos); \
		break; \
	default: \
		d = 0; \
	} \
	GetClientRect(hwnd, &rc); \
	if (mes == WM_HSCROLL) { \
		i = (int) min(w - rc.right - si_x.nPos - 1, d); \
		d = max(-si_x.nPos, i); \
		if (!d) \
			break; \
		ScrollWindowEx(hwnd, -d, 0, (CONST RECT *) 0, (CONST RECT *) 0, 0, &rc, 0); \
		si_x.nPos += d; \
		si_x.fMask  = SIF_POS; \
		SetScrollInfo(hwnd, SB_HORZ, &si_x, 1); \
	} \
	else { \
		i = (int) min(h - rc.bottom - si_y.nPos - 1, d); \
		d = max(-si_y.nPos, i); \
		if (!d) \
			break; \
		ScrollWindowEx(hwnd, 0, -d, (CONST RECT *) 0, (CONST RECT *) 0, 0, &rc, 0); \
		si_y.nPos += d; \
		si_y.fMask  = SIF_POS; \
		SetScrollInfo(hwnd, SB_VERT, &si_y, 1); \
	} \
	InvalidateRect(hwnd, &rc, 0); \
	UpdateWindow(hwnd); \
}

#define IMPROC_EDT( xxx ) int CALLBACK imp_edt_##xxx (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) \
{ \
	if (uMsg == WM_GETDLGCODE) return DLGC_WANTALLKEYS; \
	else if (uMsg == WM_KEYDOWN && wParam == VK_RETURN) { \
		SendMessage(hDlg, WM_COMMAND, ID_##xxx , 0); \
	} \
	return CallWindowProc( old_imp_##xxx, hwnd, uMsg, wParam, lParam); \
}

#define IMPROC_SET( xxx ) old_imp_##xxx = (WNDPROC) SetWindowLong(GetDlgItem(hDlg, ID_BTN( ##xxx ) + 1), GWL_WNDPROC, (long) imp_edt_##xxx )


extern HINSTANCE hInstance;
extern HWND hDlg, hWnd, hWndR;
extern BYTE office[NUM_COLORS][4];

BOOL CALLBACK dlg_proc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK hwnd_proc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK hwndR_proc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK color_proc(HWND, UINT, WPARAM, LPARAM);

void dlg_init(void);
void dlg_size(LPARAM);
void dlg_info(void);
void dlg_open(void);
void dlg_save(void);
int dlg_drawitem(LPARAM);

int tif_read(char *, IplImage **);

void imp_hist(IplImage **);
void imp_pyrm(IplImage **);
void imp_fill(IplImage **, POINT *);
void imp_rgbg(IplImage **);
void imp_erod(IplImage **);
void imp_smoo(IplImage **);
void imp_cont(IplImage **);
void imp_thre(IplImage **);
void imp_edge(IplImage **, CvSeq **, IplImage *);
void imp_appr(IplImage **, CvSeq **, IplImage *);
void imp_snak(IplImage **, CvSeq **, IplImage *);
void imp_houg(IplImage **, CvSeq **, IplImage *);
void imp_cnvx(IplImage **, CvSeq **, IplImage *);

int macro_load(void);
int macro_save(CvSeq *);
int macro_sel(void);

#endif