#include "sgl.h"
#include <math.h>
#include <stdio.h>
#include "sample_graph.h"
#include "sample_extra.h"

#pragma warn(disable: 2118 2216)	/* disable some warnings						*/
					/* 2118: Parameter {parameter_name} is not referenced.			*/
					/* 2216: The return value from {function_name} is never used.	*/

static HWND mainPanel, table, textBox, sep, graph ;
static POINT editedCell = {-1, -1} ;					/* default: none			*/

#define ROW_NB 41
static double array[2][ROW_NB] ;

/*
	This sample program 2 vectors: X and Y.

	The X and Y values are stored in an 2D array: array[0] for X, array[1] for Y.

	These values are displayed in a table whose columns are:
		0: item number
		1: X values
		2: Y values

	These values are plotted, X on layer 2, Y on layer 3.
*/

/*======================================================================= TABLE ====*/

static SGL_TABLE_COLUMN_T columns[] =
{
	{ -5, 0, 0,	0xDDDDDD } ,	/* width, headerMerge, cellNoRightSep, cellBGcolor	*/
	{ -5, 0, 0,	0xAEAEFF } ,
	{ -5, 0, 0,	0xAAFFAA } ,
	{ 0 }
} ;


static void cellFormat(char* s, double val)		/* formatting a value of array[]	*/
{
	sprintf(s, "%.2f", val) ;
}

static int cellEditCB(WPARAM virtKey, char* s, int len, int caret, void *cbd)
{
	int update = 0 ;							/* function return value			*/

	char *endPtr ;								/* get user entry					*/
	double val = strtod(s, &endPtr) ;
	if (*endPtr) MessageBeep(MB_ICONASTERISK) ;

	switch (virtKey)							/* process up/down/scroll			*/
	{
		case VK_UP :
		case VK_DOWN :
		{
			double inc = SGL_KEY_SHIFT ? 0.1 : 0.01 ;		/* increment value		*/
			val += (virtKey == VK_UP) ? inc : - inc ;
			break ;
		}
	}
	val = fmax(0.0, fmin(val, 1.0)) ;			/* valid interval					*/

	char sNew[32] ;								/* value to string					*/
	cellFormat(sNew, val) ;
	if (strcmp(s, sNew))						/* update if changed				*/
	{
		array[editedCell.x][editedCell.y] = val ;				/* data array		*/
		strcpy(s, sNew) ;										/* cell value		*/
		SGL_GraphPlotRequest(graph, editedCell.x + 2, 			/* plotted value	*/
									editedCell.y) ;
		update = 1 ;							/* edit box must be updated			*/
	}

	if (virtKey == VK_ESCAPE)
	{
		editedCell.y = - 1 ;
		SGL_GraphPlotRequest(graph, editedCell.x + 2, editedCell.y) ;
	}
	SGL_Log(STD, "%s 0x%x %s", __func__, virtKey, update ? "UPDATE" : "") ;
	return update ;
}

static void cellEditRequest(int r, int c)
{
	if (r >= 0 && c > 0)								/* editable values			*/
	{
		SGL_TableRowShow(table, r) ;					/* make the cell visible	*/
		if (SGL_DimmedGet(table))
			return ;

		static char s[32] ;
		cellFormat(s, array[c - 1][r]) ;

		editedCell.x = c - 1 ;
		editedCell.y = r ;

		SGL_Log(STD, "%s %d %d", __func__, editedCell.x, editedCell.y) ;

		RECT rect ;										/* cell rectangle			*/
		SGL_TableCellRectangleGet(table, r, c, &rect) ;	/* used to locate a popup	*/
		SGL_PopupEdit(table, &rect, ES_CENTER, s, 8, cellEditCB, NULL) ;
		SGL_GraphPlotRequest(graph, editedCell.x + 2, editedCell.y) ;
	}
}

static void myTableFill(HWND hwnd, HDC hdc, int row, int col, RECT *rect)
{
	UINT style = DT_SINGLELINE | DT_VCENTER | DT_CENTER ;	/* text style			*/

	if (col < 0)											/* ignore notification	*/
		return ;

	if (row < 0)											/* draw header			*/
	{
		char *headers[] = { "item", "value X", "value Y" } ;
		DrawText(hdc, headers[col], -1, rect, style) ;
	}
	else													/* draw cell			*/
	{
		char l[32] ;
		if (col == 0)										/* column 0: row index	*/
			snprintf(l, 32, " %d", row) ;
		else												/* next column: array[]	*/
			cellFormat(l, array[col - 1][row]) ;
		DrawText(hdc, l, -1, rect, style) ;
	}
}

static int tableCB(HWND hwnd, UINT event, WPARAM wParam, LPARAM lParam)
{
	if (event == WM_LBUTTONUP)							/* edit the clicked cell	*/
	{
		int row, col ;
		if (0 == SGL_TableCellCoordinatesGet(hwnd, &row, &col))
			cellEditRequest(row, col) ;
	}
	return 0 ;
}

void tableInstall(HWND parent)
{
	table = SGL_New(parent, SGL_CTRL_TABLE, WS_VSCROLL, "Table", 0, 0) ;
	SGL_TableColumnsSet(table, columns) ;
	SGL_TableHeaderHeightSet(table, 140);
	SGL_TableFillFunctionSet(table, myTableFill) ;
	SGL_CallbackFunctionSet(table, &tableCB) ;
	SGL_BGcolorSet(table, 0xF0F080) ;

	SGL_TableRowNbSet(table, ROW_NB) ;
	for (int i = 0 ; i < ROW_NB ; i++)						/* initial values		*/
	{
		array[0][i] = lround(i * 100.0 / (ROW_NB - 1)) / 100.0 ;
		array[1][i] = 0.01 * (rand() % 100) ;
	}
}

/*======================================================================= GRAPH ====*/

int graphHeight ;						/* used by the separator object				*/
HCURSOR graphCursor ;

void myPlot(HWND hwnd, HDC hdc, int layer, int item)
{
	int i, xp, yp ;
	RECT rect ; GetClientRect(hwnd, &rect) ;

	if (layer < 0)								/*==== SYSTEM REQUEST [draw all] ===*/
	{
		CHKERR(SGL_GraphPlotRequest(hwnd, 0, 1)) ;		/* full background			*/
		for (layer = 1 ; layer < 4 ; layer++)			/* own's layers				**/
			CHKERR(SGL_GraphPlotRequest(hwnd, layer, -1)) ;
	}

    else switch (layer)							/*==== USER REQUEST ================*/
	{
		case 2 :								/* plot array[0][]					*/
		case 3 :								/*           [1][]					*/
		{
			CHKERR(SGL_GraphClear(hwnd, layer)) ;		/* clear previous drawings	*/
			int j = layer - 2 ;
			{											/* get color from the table	*/
				SetDCBrushColor(hdc, columns[j + 1].cellBGcolor) ;
				for (i = 0 ; i < ROW_NB ; i++)
				{
					xp = SGL_GraphUserToPixel(hwnd, SGL_BOTTOM , (double) i) ;
					yp = SGL_GraphUserToPixel(hwnd, SGL_LEFT, array[j][i]) ;
					if (xp < 0 || yp < 0)
						continue ;
					int boxSize = (i == item) ? 3 : 1 ;
					SetRect(&rect, xp - boxSize,
								   yp - boxSize,
								   xp + 1 + boxSize,
								   yp + 1 + boxSize) ;
					FillRect(hdc, &rect,  GetStockObject(DC_BRUSH)) ;
				}
			}
			break ;
		}
	}
	SGL_Redraw(hwnd) ;
    return ;
}

void myLabel(HWND hwnd, HDC hdc, double z, RECT *rect, int iaxis)
{
	char l[32] ;									/* label text					*/
	if (iaxis == SGL_LEFT && z == 1.0)
		sprintf(l, "%g s", z) ;
	else if (iaxis == SGL_BOTTOM && z == 30.0)
		sprintf(l, "%g m", z) ;
	else
		sprintf(l, "%g", z) ;

	UINT dtFormat = DT_SINGLELINE ;
	switch (iaxis)			/* set the position of the text in the input rectangle	*/
	{
		case SGL_LEFT :		dtFormat |= DT_RIGHT ;	break ;
		case SGL_BOTTOM :	dtFormat |= DT_CENTER ;	break ;
		default : return ;
	}

	DrawText(hdc, l, -1, rect, dtFormat) ;
}

static int graphCB(HWND hwnd, UINT event, WPARAM wParam, LPARAM lParam)
{
	switch (event)
    {
		case WM_SETCURSOR :
		{
			POINT cursorPos ;	SGL_CursorPositionGet(hwnd, 0, &cursorPos) ;
			RECT plotRect ;		SGL_GraphPlotRectGet(hwnd, &plotRect) ;
			
			if (PtInRect(&plotRect, cursorPos))
			{
				SetCursor(graphCursor) ;
				return 1 ;
			}
			break ;
		}

		case WM_LBUTTONUP :									/* select point to edit	*/
		{
			POINT cursorPos ;				/* cursor position (pixel coordinates)	*/
			CHKERR(SGL_CursorPositionGet(hwnd, 0, &cursorPos)) ;
			double x, y ;					/* cursor position (user coordinates)	*/
			CHKERR(SGL_GraphPixelToUser(hwnd, SGL_LEFT, cursorPos.y, &y)) ;
			CHKERR(SGL_GraphPixelToUser(hwnd, SGL_BOTTOM, cursorPos.x, &x)) ;

			int r = lround(x) ;				/* nearest point (cell coordinates)		*/
			if (r < 0 || r >= ROW_NB)
				break ;
			int c = 1 ;
			if (fabs(y - array[0][r]) > fabs(y - array[1][r]))
				c = 2 ;
			cellEditRequest(r, c) ;
			break ;
		}
	}

	return 0 ;
}

HWND graphInstall(HWND parent)
{
	HWND hh = SGL_New(parent, SGL_CTRL_GRAPH, 0, "Graph", 0, 3) ;
	CHKERR(SGL_CallbackFunctionSet(hh, graphCB)) ;
	CHKERR(SGL_GraphBGcolorSet(hh, 0, 0x114460)) ;
	graphCursor = SGL_GraphCursorCreate(34) ;
	CHKERR(SGL_SizeSet(hh, SGL_HEIGHT, -2000)) ;		/* heigth: initial value	*/
	CHKERR(SGL_AlignmentSet(hh, SGL_LEFT | SGL_RIGHT)) ;			/* width: span	*/
	CHKERR(SGL_PaddingSet(hh, NULL)) ;					/* no padding				*/

														/* set margin for scales	*/
	RECT padding = { .left = -420, .right = -150, .top = -150, .bottom = -210} ;
	CHKERR(SGL_GraphMarginSet(hh, &padding)) ;

	SGL_GRAPH_AXIS_T xaxis = {							/* set X axis				*/
		.lowValue = 0.97 ,
		.highValue = ROW_NB ,
		.interval = 0.0 ,
		.ntick = 3 ,
		.grid = 2 ,
		.labelCB = myLabel ,
		.color = 0xaaff00 } ;
	CHKERR(SGL_GraphAxisSet(hh, SGL_BOTTOM, &xaxis)) ;

	SGL_GRAPH_AXIS_T yaxis = {							/* set Y axis				*/
		.lowValue = 0.0 ,
		.highValue = 1.0 ,
		.interval = 0.2 ,
		.ntick = 1 ,
		.labelCB = myLabel ,
		.grid = 2 ,
		.color = 0xffffff } ;
	CHKERR(SGL_GraphAxisSet(hh, SGL_LEFT, &yaxis)) ;

	CHKERR(SGL_GraphPlotFunctionSet(hh, myPlot)) ;		/* set the plot function	*/
	return hh ;
}


/*=================================================================== SEPARATOR ====*/

int movingSeparatorCB(HWND hwnd, UINT event, WPARAM wParam, LPARAM lParam)
{
	static int minHeight = -1 ;		/* uninitialized; x1 for table, x2 for graph	*/

	if (event == WM_USER)		/* the separator has been moved {wParam} pixels		*/
	{
		if (minHeight < 0)								/* initialize: height of a	*/
			minHeight = SGL_TableHeightAdjust(table, 2) ;	/* table with 2 rows	*/

		int dh = (int) wParam ;							/* move distance			*/

		RECT tableRect, graphRect ;						/* set min & max values		*/		
		GetClientRect(table, &tableRect) ;
		dh = max(dh, minHeight - tableRect.bottom) ;
		GetClientRect(graph, &graphRect) ;
		dh = min(dh, graphRect.bottom -  2 * minHeight) ;

		if (dh)
		{
			int tableHeightNew = tableRect.bottom + dh ;
			SGL_SizeSet(table, SGL_HEIGHT, tableHeightNew) ;

			int graphHeightNew = graphRect.bottom - dh ;
			SGL_SizeSet(graph, SGL_HEIGHT, graphHeightNew) ;

			SGL_Layout(mainPanel) ;
		}
	}
	return 0 ;
}

void separatorsInstall(HWND parent)
{
	SGL_New(parent, SGL_CTRL_HSEP, 0, "Bar", 0, 0) ;		/* fixed separator		*/

	sep = SGL_New(parent, SGL_CTRL_HSEP, 0, "Sep", 0, 2) ;	/* moving separator		*/
	CHKERR(SGL_CallbackFunctionSet(sep, movingSeparatorCB)) ;
}


/*==================================================================== TEXT BOX ====*/

void textBoxInstall(HWND parent)
{
	textBox = SGL_New(parent, SGL_CTRL_EDIT,
						  WS_VSCROLL | ES_AUTOVSCROLL | ES_LEFT | 
						  ES_MULTILINE | ES_READONLY,  "TextBox", 2, 0) ;
	CHKERR(SGL_SizeSet(textBox, SGL_WIDTH, -20)) ;
	CHKERR(SGL_SizeSet(textBox, SGL_HEIGHT, 0)) ;	/* span height with 0 minimum	*/
	CHKERR(SGL_AlignmentSet(textBox, SGL_TOP | SGL_BOTTOM)) ;
	CHKERR(SGL_BGcolorSet(textBox, 0xdddd44)) ;

	CHKERR(SGL_EditTextSet(textBox, "TABLE: "
								"To change a value, click in a cell and use:\r\n"
								"- the keyboard entry,\r\n"
								"- the up/down keys,\r\n"
								"- or the mouse wheel.\r\n"
								"\r\nGRAPH: Click on a point to edit its value. ")) ;
}

/*======================================================================== MENU ====*/

static int menuCB(HWND hwnd, UINT event, WPARAM wParam, LPARAM lParam)
{
	if (event == WM_COMMAND && HIWORD(wParam) == 0)			/* menu event			*/
	{
		SetFocus(hwnd) ;									/* close pending popups	*/

		HMENU hmenu = GetMenu(hwnd) ;
		int menuID = LOWORD(wParam) ;
		switch (menuID)
		{
			case IDM_DBG_TABLE :							/* debug settings		*/
			case IDM_DBG_EDIT :
			case IDM_DBG_SEP :
			case IDM_DBG_GRAPH :
			{
				int debug = GetMenuChecked(hmenu, menuID) ? 0 : 1 ;
				SetMenuChecked(hmenu, menuID, debug) ;		/* update menu state	*/
				HWND debugH = NULL ;
				switch (menuID)								/* select object		*/
				{
					case IDM_DBG_TABLE :	debugH = table ;	break ;
					case IDM_DBG_EDIT :		debugH = textBox ;	break ;
					case IDM_DBG_SEP :		debugH = sep ;		break ;
					case IDM_DBG_GRAPH :	debugH = graph ;	break ;
				}
				SGL_DebugSet(debugH, debug) ;				/* apply new state		*/
				break ;
			}

			case IDM_DIM_TABLE :
			{
				int dim = SGL_DimmedGet(table) ? 0 : 1 ;	/* new dimming state	*/
				CHKERR(SGL_DimmedSet(table, dim)) ;			/* update object		*/
				SGL_Redraw(table) ;							/* redraw object		*/
				SetMenuChecked(hmenu, menuID, dim) ;		/* update menu state	*/
				break ;
			}

			case IDM_DIM_GRAPH :
			{
				int dim = SGL_DimmedGet(graph) ? 0 : 1 ;	/* new dimming state	*/
				CHKERR(SGL_DimmedSet(graph, dim)) ;			/* update object		*/
				SGL_GraphPlotRequest(graph, -1, 0) ;		/* redraw object		*/
				SetMenuChecked(hmenu, menuID, dim) ;		/* update menu state	*/
				break ;
			}

			case IDM_APP_ABOUT :
				MessageBox(hwnd, "GRAPH Demo\n(c) Henri Serindat, 2016",
							"SGL", MB_ICONINFORMATION | MB_OK) ;
				break ;

			case IDM_APP_EXIT :
				SGL_Destroy(hwnd) ;
				break ;
		}
	}
	return 0 ;
}

void menuInstall(HWND parent, HINSTANCE hInstance)
{
	SetMenu(mainPanel, LoadMenu(hInstance, "MENUDEMO")) ;	/* cf. resource file	*/
	CHKERR(SGL_CallbackFunctionSet(mainPanel, &menuCB)) ;
}

/*======================================================================== MAIN ====*/

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
	AllocConsole() ;									/* for console messages		*/
	SGL_Init(hInstance, "#8001") ;
	SGL_Log(STD, "==== SGL DEMO ====\n") ;

	mainPanel = SGL_New(0, SGL_PANEL, 0, "SGL Graph Demo", -1, 30) ;
	SGL_LayoutConfigure(mainPanel, "MAIN", SGL_SAVE_POSITION) ;
	SGL_PanelIpaddingSet(mainPanel, NULL) ;

	HWND subpanel = SGL_New(mainPanel, SGL_ROUNDEDFRAME, DT_RIGHT, "TOP PANEL", 0, 1) ;

	menuInstall(mainPanel, hInstance) ;
	tableInstall(subpanel) ;
	textBoxInstall(subpanel) ;
	graph = graphInstall(mainPanel) ;
	separatorsInstall(mainPanel) ;

	SGL_Layout(mainPanel) ;
	SGL_VisibleSet(mainPanel, 1) ;
	SGL_Run() ;

	SGL_Exit() ;
	return 0 ;
}
