#include "sgl.h"
#include <stdio.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.	*/


typedef struct slider_t_
{
	HWND caption, num, bar;
	double dMin, dMax;
	double dValue;
	char aValue[16];
	void (*notifCB) (struct slider_t_*, int);
} SLIDER_T;


/*================================================================== SLIDER ====*/

#define SLIDER_HALFTHICKNESS 2

static void sliderPlot(HWND hwnd, HDC hdc, int layer, int item)
					/* item is the position of the graphic cursor (pixel value)	*/
{
	if (layer < 0)							/*==== SYSTEM REQUEST [draw all] ===*/
	{
		SGL_GraphPlotRequest(hwnd, 0, 1);				/* full background		*/
		SGL_GraphPlotRequest(hwnd, 1, -1);				/* slider				*/
	}

    else switch (layer)						/*==== USER REQUEST ================*/
	{
		case 1 :								
			SGL_GraphClear(hwnd, layer);			/* clear previous drawings	*/
			RECT  rect; SGL_GraphPlotRectGet(hwnd, &rect);

			SetDCBrushColor(hdc, SGL_ColorInterpolate(0x7070ff, 0xff7070,
						 ((double) item - rect.top) / (rect.bottom - rect.top)));

			rect.top = item - SLIDER_HALFTHICKNESS;
			rect.bottom = item + SLIDER_HALFTHICKNESS + 1;
			FillRect(hdc, &rect, GetStockObject(DC_BRUSH));
			break;
	}
	SGL_Redraw(hwnd);
    return;
}

static void sliderUpdate(HWND hwnd, int pixPos)
{
	SLIDER_T *slider;
	SGL_CallbackDataGet(hwnd, (void**) &slider);
	if (slider == NULL) return;

	if (pixPos == -9999)						/* input is the internal value	*/
		pixPos = SGL_GraphUserToPixel(hwnd, SGL_LEFT, slider->dValue);
	else										/* input is pixPos				*/
		SGL_GraphPixelToUser(hwnd, SGL_LEFT, pixPos, &(slider->dValue));

	sprintf(slider->aValue, "%d", (int) slider->dValue);	/* update .num		*/
	SGL_TitleSet(slider->num, slider->aValue);
	SGL_Redraw(slider->num);

	SGL_GraphPlotRequest(hwnd, 1, pixPos);					/* update .bar		*/
}

static int sliderNumEditCB(WPARAM virtKey, char* s, int len, int caret, void *cbd)
{
	SLIDER_T *slider = (SLIDER_T*) cbd;
	if (slider == NULL) return 0;

	int entry = strtol(s, NULL, 10);

	switch (virtKey)
	{
		case VK_UP :							/* up/down/scroll edit			*/
		case VK_DOWN :
		{
			int inc = SGL_KEY_SHIFT ? 1000 : 100;
			inc = (virtKey == VK_UP) ? inc - entry % 100
									 : - inc + (100 - entry % 100) % 100;
			entry += inc;
			break;
		}
	}

	entry = max(slider->dMin, min(entry, slider->dMax));
	if (slider->dValue != entry)				/* the slider value has changed	*/
	{
		sprintf(slider->aValue, "%d", entry);	/* update the structure			*/
		slider->dValue = entry;

		sliderUpdate(slider->bar, -9999);		/* update the display			*/

		if (slider->notifCB)
			slider->notifCB(slider, 0);
		return 1;
	}

	if (virtKey == VK_ESCAPE && slider->notifCB)
		slider->notifCB(slider, 1);
	return 0;
}

static int sliderNumCB(HWND hwnd, UINT event, WPARAM wParam, LPARAM lParam)
{
	if (event == WM_LBUTTONDOWN)
	{
		SLIDER_T *slider;
		SGL_CallbackDataGet(hwnd, (void**) &slider);

		RECT rect;
		GetClientRect(hwnd, &rect);
		MapWindowPoints(hwnd, NULL, (POINT*) &rect, 2);

		SGL_PopupEdit(hwnd, &rect, ES_CENTER | ES_NUMBER, 
						slider->aValue, 16, sliderNumEditCB, slider);
		return 1;
	}
	return 0;
}

static int sliderBarCB(HWND hwnd, UINT event, WPARAM wParam, LPARAM lParam)
{
	static int dragging = 0;
	POINT cursorPos;
	RECT  plotRect;

	SLIDER_T *slider;
	SGL_CallbackDataGet(hwnd, (void**) &slider);
	if (slider)	switch (event)
    {
	    case WM_LBUTTONDOWN :						/* start dragging			*/
			if (dragging == 0)
			{
				SGL_CursorPositionGet(hwnd, 0, &cursorPos);
				SGL_GraphPlotRectGet(hwnd, &plotRect);
				if (PtInRect(&plotRect, cursorPos))
				{
					dragging = 1;
					SetCapture(hwnd);

					sliderUpdate(hwnd, cursorPos.y);
					if (slider->notifCB)
						slider->notifCB(slider, 0);
				}
			}
			break;

		case WM_MOUSEMOVE :							/* drag						*/
			if (dragging)
			{
				cursorPos.y = (short) HIWORD(lParam);
				SGL_GraphPlotRectGet(hwnd, &plotRect);
				cursorPos.y = max(cursorPos.y, plotRect.top);	/* clip value	*/
				cursorPos.y = min(cursorPos.y, plotRect.bottom - 1);

				sliderUpdate(hwnd, cursorPos.y);
				if (slider->notifCB)
					slider->notifCB(slider, 0);
			}
			break;

	    case WM_LBUTTONUP :							/* end of dragging			*/
		{
        	dragging = 0;
			ReleaseCapture();

			if (slider->notifCB)
				slider->notifCB(slider, 1);
	        break;
		}
	}
	return 0;
}

void sliderCBset(SLIDER_T* slider, void (*func) (struct slider_t_*, int))
{
	if (slider)
		slider->notifCB = func;
}

void sliderSet(SLIDER_T* slider, double dValue)
{
	if (slider)
	{
		slider->dValue = dValue;
		sliderUpdate(slider->bar, -9999);
	}
}

double sliderGet(SLIDER_T* slider)
{
	return slider ? slider->dValue : 0;
}

SLIDER_T* sliderNew(HWND parent, char *title, int c, int r, SGL_GRAPH_AXIS_T *axis)
{
	SLIDER_T *slider = calloc(1, sizeof(SLIDER_T));
	if (slider)
	{
		HWND panel = SGL_New(parent, SGL_PANEL, 0, "Slider", c, r);
		SGL_AlignmentSet(panel, 0);
		SGL_PanelIpaddingSet(panel, 0);

		PCHECK(slider->caption = SGL_New(panel, SGL_CTRL_BUTTON, 0, title, 0, 0));
		SGL_SizeSet(slider->caption, SGL_WIDTH, -4);
		SGL_PaddingSet(slider->caption, NULL);
		SGL_BorderThicknessSet(slider->caption, 0);

		PCHECK(slider->num = SGL_New(panel, SGL_CTRL_BUTTON, 0, " ", 0, 1));
		SGL_SizeSet(slider->num, SGL_WIDTH, -3);
		SGL_CallbackFunctionSet(slider->num, sliderNumCB);
		SGL_CallbackDataSet(slider->num, slider);

		PCHECK(slider->bar = SGL_New(panel, SGL_CTRL_GRAPH, 0, "BarGraph", 0, 2));
		SGL_SizeSet(slider->bar, SGL_HEIGHT, -2000);			/* height: set  */
		SGL_AlignmentSet(slider->bar, SGL_LEFT | SGL_RIGHT);	/* width: span	*/
		SGL_CallbackFunctionSet(slider->bar, sliderBarCB);
		SGL_CallbackDataSet(slider->bar, slider);

													/* set margin for scales	*/
		RECT margin = { .left = -250, .right = 0, .top = -50, .bottom = -50};
		SGL_GraphMarginSet(slider->bar, &margin);
		SGL_GraphAxisSet(slider->bar, SGL_LEFT, axis);
		slider->dMin = axis->lowValue;
		slider->dMax = axis->highValue;

		SGL_GraphPlotFunctionSet(slider->bar, sliderPlot);
	}
	return slider;
}


/*==============================================================================*/

#define TRACKNB 4									/* number of sliders		*/

void notif(SLIDER_T* slider, int event)
{
	char *label[2] = {"VALUE CHANGED ", "SLIDER RELEASED"};
	SGL_Log(STD, "Slider event %s [%.f]", label[event], sliderGet(slider));
}

void label(HWND hwnd, HDC hdc, double z, RECT *rect, int iaxis)
{
	char l[32];										/* label text				*/
	sprintf(l, "%g", z / 1000.0);

	DrawText(hdc, l, -1, rect, DT_SINGLELINE | DT_RIGHT);
}

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

	HWND mainPanel = SGL_New(0, SGL_PANEL, 0, "SGL - Slider demo", -1, -1);

	SLIDER_T *slider[TRACKNB];						/* create sliders			*/
	char l[TRACKNB][32];
	SGL_GRAPH_AXIS_T axis = {						/* scale					*/
		.lowValue = 0 ,
		.highValue = 42000 ,
		.interval = 6000 ,
		.ntick = 2,
		.labelCB = label ,
		.grid = 2 ,
		.color = 0xffffff };
	for (int i = 0; i < TRACKNB; i++)
	{
		sprintf(l[i], "Altitude %d", i);
		PCHECK(slider[i] = sliderNew(mainPanel, l[i], i, 0, &axis));
	}
	sliderCBset(slider[0], notif);

	SGL_Layout(mainPanel);
	SGL_VisibleSet(mainPanel, 1);
	sliderSet(slider[0], 10000);					/* preset slider 0			*/
	SGL_Run();

	SGL_Exit();
	return 0;
}
