#include "WimpLib:Slider.h"

#include "WimpLib:Exception.h"
#include "WimpLib:DragDrop.h"
#include "WimpLib:Task.h"
#include "WimpLib:Window.h"

static struct
{
	HWind w;
	HIcon i;
} Sliders_DragInfo;

typedef struct
{
	int             type;
	Slider_SetValue update;
	void*           pData;
	int             mousemin;
	int             maxval;
	int             value;
	int             prevvalue;
} Slider_Data;

void Slider_Set(HWind w, HIcon i1, HIcon i2, int type, float value)
{
	try
	{
		CIcon info1, info2;
		CRect box;
		int oldpos, pos, maxval;

		Icon_GetInfo(w, i1, &info1);
		Icon_GetInfo(w, i2, &info2);

		if (type & ESliderType_Button)
		{
			box = info2.box;

			// Icon 1 is fixed, Icon 2 moves within limits of icon 1

			if (type & ESliderType_Vertical)
			{
				pos = info1.box.y1 - info1.box.y0 - (info2.box.y1 - info2.box.y0);
				pos = info1.box.y0 + (int)(pos * value);
				oldpos = info2.box.y0;
				info2.box.y1 = pos + (info2.box.y1 - info2.box.y0);
				info2.box.y0 = pos;
				if ((info2.box.y0 > box.y0) && (info2.box.y0 < box.y1))
					box.y1 = info2.box.y0;
				if ((info2.box.y1 > box.y0) && (info2.box.y1 < box.y1))
					box.y0 = info2.box.y1;
			}
			else
			{
				pos = info1.box.x1 - info1.box.x0 - (info2.box.x1 - info2.box.x0);
				pos = info1.box.x0 + (int)(pos * value);
				oldpos = info2.box.x0;
				info2.box.x1 = pos + (info2.box.x1 - info2.box.x0);
				info2.box.x0 = pos;
				if ((info2.box.x0 > box.x0) && (info2.box.x0 < box.x1))
					box.x1 = info2.box.x0;
				if ((info2.box.x1 > box.x0) && (info2.box.x1 < box.x1))
					box.x0 = info2.box.x1;
			}

			if (oldpos != pos)
			{
				Window_Invalidate(w, &box);
				throw_Icon_Move(w, i2, &info2.box);
				Window_Invalidate(w, &info2.box);
			}
		}
		else
		{
			// Icon 1 min pos is fixed
			// Icon 1 max pos is Icon 2 min pos
			// Icon 2 max pos is fixed

			if (type & ESliderType_Vertical)
			{
				maxval = info1.box.y1 - info2.box.y0;
				oldpos = info2.box.y1;
				pos = info2.box.y0 + (int)(maxval * value);

				if (oldpos != pos)
				{
					info2.box.y1 = pos;
					throw_Icon_Move(w, i2, &info2.box);

					info1.box.y0 = pos;
					throw_Icon_Move(w, i1, &info1.box);

					if (oldpos < pos)
					{
						info1.box.y0 = oldpos;
						info1.box.y1 = pos;
					}
					else
					{
						info1.box.y0 = pos;
						info1.box.y1 = oldpos;
					}
					Window_Invalidate(w, &info1.box);
				}
			}
			else
			{
				maxval = info1.box.x1 - info2.box.x0;
				oldpos = info2.box.x1;
				pos = info2.box.x0 + (int)(maxval * value);

				if (oldpos != pos)
				{
					info2.box.x1 = pos;
					throw_Icon_Move(w, i2, &info2.box);

					info1.box.x0 = pos;
					throw_Icon_Move(w, i1, &info1.box);

					if (oldpos < pos)
					{
						info1.box.x0 = oldpos;
						info1.box.x1 = pos;
					}
					else
					{
						info1.box.x0 = pos;
						info1.box.x1 = oldpos;
					}
					Window_Invalidate(w, &info1.box);
				}
			}
		}
	}
	catch
	{
		Task_ReportException();
	}
	catch_end
}

static void Slider_NullEvent(void* pSliderData, const Mouse* m)
{
	Slider_Data* pData = pSliderData;
	int value;

	if (pData->type & ESliderType_Vertical)
		value = m->pt.y - pData->mousemin;
	else
		value = m->pt.x - pData->mousemin;

	if (value < 0) value = 0;
	if (value > pData->maxval) value = pData->maxval;

	if ((value == pData->value)
	&&  (pData->value != pData->prevvalue))
	{
		pData->update(pData->pData, pData->value, pData->maxval, ESlider_UpdateMode_Dragging);
		pData->prevvalue = pData->value;
	}
	else
		pData->value = value;
}

void Slider_Update(HWind w, HIcon i1, HIcon i2, int type, const Mouse* m, Slider_SetValue update, void* pData)
{
	try
	{
		CWindCvt cvt = Window_GetPosInfo(w);
		CIcon old1, old2;
		CIcon info1, info2;
		Slider_Data Data;

		Data.type = type;
		Data.update = update;
		Data.pData = pData;

		Icon_GetInfo(w, i1, &old1);
		Icon_GetInfo(w, i2, &old2);
		info1.box = RectToScreen(&old1.box, &cvt);
		info2.box = RectToScreen(&old2.box, &cvt);

		if (type & ESliderType_Button)
		{
			// Must be on button
			if (m->i != i2) goto done;

			// Icon 1 is fixed, Icon 2 moves within limits of icon 1

			if (type & ESliderType_Vertical)
			{
				Data.mousemin = info1.box.y0 - Task_GetModeInfo()->dy // Don't like to see mouse above top
				              + (m->pt.y - info2.box.y0);
				Data.maxval   = info1.box.y1 - info1.box.y0
				              - (info2.box.y1 - info2.box.y0);
			}
			else
			{
				Data.mousemin = info1.box.x0
				              + (m->pt.x - info2.box.x0);
				Data.maxval   = info1.box.x1 - info1.box.x0
				              - (info2.box.x1 - info2.box.x0);
			}
		}
		else
		{
			// Icon 1 min pos is fixed
			// Icon 1 max pos is Icon 2 min pos
			// Icon 2 max pos is fixed

			if (type & ESliderType_Vertical)
			{
				Data.mousemin = info2.box.y0 - Task_GetModeInfo()->dy; // Don't like to see mouse above top
				Data.maxval   = info1.box.y1 - info2.box.y0;
			}
			else
			{
				Data.mousemin = info2.box.x0;
				Data.maxval   = info1.box.x1 - info2.box.x0;
			}
		}

		if (type & ESliderType_Vertical)
			Data.value = m->pt.y - Data.mousemin;
		else
			Data.value = m->pt.x - Data.mousemin;

		if (Data.value < 0) Data.value = 0;
		if (Data.value > Data.maxval) Data.value = Data.maxval;

		// Start dragging ?
		if (m->but & (EBut_DragSelect | EBut_DragAdjust))
		{
			Data.prevvalue = Data.value;
			Data.update(pData, Data.value, Data.maxval, ESlider_UpdateMode_Dragging);

			if (type & ESliderType_Vertical)
			{
				info1.box.y0 = Data.mousemin;
				info1.box.y1 = info1.box.y0 + Data.maxval + 1; // Exclusive
				info1.box.x0 = m->pt.x;
				info1.box.x1 = m->pt.x + 4; // Exclusive
			}
			else
			{
				info1.box.x0 = Data.mousemin;
				info1.box.x1 = info1.box.x0 + Data.maxval + 1; // Exclusive
				info1.box.y0 = m->pt.y;
				info1.box.y1 = m->pt.y + 4; // Exclusive
			}
			if (type & ESliderType_Button)
				Icon_SetHighlight(w, i2, true);

			Sliders_DragInfo.w = w;
			Sliders_DragInfo.i = i1;

			if (Drag_DragPoint(&Data, Slider_NullEvent, &info1.box, NULL))
				update(pData, Data.value, Data.maxval, ESlider_UpdateMode_Set);
			else
			{
				Icon_Redraw(w, i1);
				Icon_Redraw(w, i2);
				throw_Icon_Move(w, i1, &old1.box);
				throw_Icon_Move(w, i2, &old2.box);
				Icon_Redraw(w, i1);
				Icon_Redraw(w, i2);
				update(pData, Data.value, Data.maxval, ESlider_UpdateMode_Cancel);
			}

			Sliders_DragInfo.w = HWind_None;
			Sliders_DragInfo.i = HIcon_None;

			if (type & ESliderType_Button)
				Icon_SetHighlight(w, i2, false);
		}
		// Set value on click? not if button
		else if (!(type & ESliderType_Button))
		{
			Data.prevvalue = Data.value;
			Data.update(pData, Data.value, Data.maxval, ESlider_UpdateMode_Set);
		}

done: ;
	}
	catch
	{
		Task_ReportException();
	}
	catch_end
}

bool Slider_IsDragging(HWind w, HIcon i1, HIcon i2)
{
	i2 = i2;

	return (   (Sliders_DragInfo.w == w)
	        && (Sliders_DragInfo.i == i1));
}
