//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//agenttype_switchedstate_getdata
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void *agenttype_switchedstate_getdata(agent *a, int datatype)
{
	agenttype_switchedstate_details *details = (agenttype_switchedstate_details *) a->agentdetails;

	bool *boolptr;

	switch (datatype)
	{
		case DATAFETCH_VALUE_TEXT:
			//Get the boolean value
			boolptr = (bool *) agent_getdata(details->agents[AGENTTYPE_SWITCHEDSTATE_AGENT_BOOL], DATAFETCH_VALUE_BOOL);

			//If the boolean value is true...
			if (boolptr != NULL && *boolptr == true) return agent_getdata(details->agents[AGENTTYPE_SWITCHEDSTATE_AGENT_WHENTRUE], datatype);
			else return agent_getdata(details->agents[AGENTTYPE_SWITCHEDSTATE_AGENT_WHENFALSE], datatype);

			break;

		case DATAFETCH_SUBAGENTS_POINTERS_ARRAY:
			return details->agents;
		case DATAFETCH_SUBAGENTS_NAMES_ARRAY:
			return agenttype_switchedstate_agentdescriptions;
		case DATAFETCH_SUBAGENTS_TYPES_ARRAY:
			return (void *) agenttype_switchedstate_agenttypes[details->datatype];
		case DATAFETCH_SUBAGENTS_COUNT:
			return (void *) &agenttype_switchedstate_subagentcount;
	}

	return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//controltype_button_notify
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void controltype_button_notify(control *c, int notifytype, void *messagedata)
{
	//Variables
	controltype_button_details *details;
	bool *newvalueptr;

	//Get details
	details = (controltype_button_details *) c->controldetails;

	//Find out what to do
	switch (notifytype)
	{       
		case NOTIFY_NEEDUPDATE:
			//When we are told we need an update, figure out what to do
			//Update the caption first
			if (details->agents[CONTROLTYPE_BUTTON_CAPTION])
			{
				details->caption = (char *) agent_getdata(details->agents[CONTROLTYPE_BUTTON_CAPTION], DATAFETCH_VALUE_TEXT);
			}
			else
			{
				details->caption = NULL;
			}

			//Only two state buttons need updates at this point
			if (details->is_twostate)
			{
				//Get the value
				newvalueptr = (bool *)(agent_getdata(details->agents[CONTROLTYPE_TWOSTATEBUTTON_AGENT_VALUECHANGE], DATAFETCH_VALUE_BOOL));
				if (newvalueptr)
				{
					//Set the new value
					details->is_on = *newvalueptr;
				}
			}

			//Tell the button to draw itself again
			style_draw_invalidate(c);
			break;

		case NOTIFY_SAVE_CONTROL:
			//Save all local values
			config_write(config_get_control_setcontrolprop_c(c, "HAlign", button_haligns[details->halign]));
			config_write(config_get_control_setcontrolprop_c(c, "VAlign", button_valigns[details->valign]));
			int i;
			for (i = 0; i < CONTROLTYPE_BUTTON_AGENTCOUNT; i++)
				agent_notify(details->agents[i], NOTIFY_SAVE_AGENT, NULL);
			if (details->is_twostate)
				config_write(config_get_control_setcontrolprop_b(c, "Pressed", &details->is_on));
			break;

		case NOTIFY_DRAGACCEPT:
			DragAcceptFiles(c->windowptr->hwnd, NULL != details->agents[details->is_twostate ? CONTROLTYPE_TWOSTATEBUTTON_AGENT_ONDROP : CONTROLTYPE_BUTTON_AGENT_ONDROP]);
			break;

	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//controltype_button_getdata
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void *controltype_button_getdata(control *c, int datatype)
{
	controltype_button_details *details = (controltype_button_details *) c->controldetails;
	switch (datatype)
	{
		case DATAFETCH_VALUE_BOOL:
			if (details->is_twostate) return &details->is_on;
			break;
		case DATAFETCH_INT_DEFAULTHEIGHT:
			return (void *) &controltype_button_data_defaultheight;
		case DATAFETCH_INT_DEFAULTWIDTH:
			return (void *) &controltype_button_data_defaultwidth;
		case DATAFETCH_INT_MIN_WIDTH:
			return (void *) &controltype_button_data_minimumwidth;
		case DATAFETCH_INT_MIN_HEIGHT:
			return (void *) &controltype_button_data_minimumheight;
		case DATAFETCH_INT_MAX_WIDTH:
			return (void *) &controltype_button_data_maximumwidth;
		case DATAFETCH_INT_MAX_HEIGHT:
			return (void *) &controltype_button_data_maximumheight;
		case DATAFETCH_CONTENTSIZES:
		{			
			if (details->agents[CONTROLTYPE_BUTTON_IMAGE])
				return agent_getdata(details->agents[CONTROLTYPE_BUTTON_IMAGE], datatype);
			break;
		}
	}
	
	return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//agenttype_switchedstate_notify
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void agenttype_switchedstate_notify(agent *a, int notifytype, void *messagedata)
{
	//Get the agent details
	agenttype_switchedstate_details *details = (agenttype_switchedstate_details *) a->agentdetails;

	//Declare variables
	bool *boolptr;

	switch(notifytype)
	{
		case NOTIFY_DRAW:
		case NOTIFY_CHANGE:
			//Get the boolean value
			boolptr = (bool *) agent_getdata(details->agents[AGENTTYPE_SWITCHEDSTATE_AGENT_BOOL], DATAFETCH_VALUE_BOOL);

			//Do the appropriate action
			if (boolptr != NULL && *boolptr == true) return agent_notify(details->agents[AGENTTYPE_SWITCHEDSTATE_AGENT_WHENTRUE], notifytype, messagedata);
			else return agent_notify(details->agents[AGENTTYPE_SWITCHEDSTATE_AGENT_WHENFALSE], notifytype, messagedata);

			break;

		case NOTIFY_SAVE_AGENT:

			//Write existance
			config_write(config_get_control_setagent_c(a->controlptr, a->agentaction, a->agenttypeptr->agenttypename, agenttype_switchedstate_typenames[details->datatype]));

			//Save all child agents, if necessary
			for (int i = 0; i < 3; i++) agent_notify(details->agents[i], NOTIFY_SAVE_AGENT, NULL);
			break;
	}

}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//controltype_label_getdata
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void *controltype_label_getdata(control *c, int datatype)
{
	switch (datatype)
	{
	case DATAFETCH_INT_DEFAULTHEIGHT:
		return (void *) &controltype_label_data_defaultheight;
	case DATAFETCH_INT_DEFAULTWIDTH:
		return (void *) &controltype_label_data_defaultwidth;
	case DATAFETCH_INT_MIN_WIDTH:
		return (void *) &controltype_label_data_minimumwidth;
	case DATAFETCH_INT_MIN_HEIGHT:
		return (void *) &controltype_label_data_minimumheight;
	case DATAFETCH_INT_MAX_WIDTH:
		return (void *) &controltype_label_data_maximumwidth;
	case DATAFETCH_INT_MAX_HEIGHT:
		return (void *) &controltype_label_data_maximumheight;
	case DATAFETCH_CONTENTSIZES:
	{
		controltype_label_details *details = (controltype_label_details *) c->controldetails;
		if (details->agents[CONTROLTYPE_LABEL_AGENT_IMAGE])
			return agent_getdata(details->agents[CONTROLTYPE_LABEL_AGENT_IMAGE], datatype);
		break;
	}
	}

	return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//controltype_label_notify
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void controltype_label_notify(control *c, int notifytype, void *messagedata)
{
	//Variables
	controltype_label_details *details;
	int i;

	//Get details
	details = (controltype_label_details *) c->controldetails;

	//Find out what to do
	switch (notifytype)
	{
	case NOTIFY_NEEDUPDATE:
		//When we are told we need an update, figure out what to do
		//Update the caption first
		if (details->agents[CONTROLTYPE_LABEL_AGENT_CAPTION])
		{
			details->caption = (char *) agent_getdata(details->agents[CONTROLTYPE_LABEL_AGENT_CAPTION], DATAFETCH_VALUE_TEXT);
		}
		else
		{
			details->caption = NULL;
		}

		//Tell the label to draw itself again
		style_draw_invalidate(c);
		break;

	case NOTIFY_SAVE_CONTROL:
		//Save all local values
		config_write(config_get_control_setcontrolprop_c(c, "HAlign", label_haligns[details->halign]));
		config_write(config_get_control_setcontrolprop_c(c, "VAlign", label_valigns[details->valign]));
		if (details->is_frame)
		{
			config_write(config_get_control_setcontrolprop_b(c, "HasTitleBar", &details->has_titlebar));
			config_write(config_get_control_setcontrolprop_b(c, "IsLocked", &details->is_locked));
		}

		for (i = 0; i < CONTROLTYPE_LABEL_AGENT_COUNT; i++)
			agent_notify(details->agents[i], NOTIFY_SAVE_AGENT, NULL);

		if (details->is_frame)
			controltype_frame_saveplugins(c);
		break;

	case NOTIFY_DRAGACCEPT:
		DragAcceptFiles(c->windowptr->hwnd, NULL != details->agents[CONTROLTYPE_LABEL_AGENT_ONDROP]);
		break;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//agenttype_graph_notify
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void agenttype_graph_notify(agent *a, int notifytype, void *messagedata)
{
	//Get the agent details
	agenttype_graph_details *details = (agenttype_graph_details *) a->agentdetails;
	styledrawinfo *di;
	HPEN hpen;
	HGDIOBJ oldobj;
	int offset_left;
	int width;
	int offset_top;
	int offset_bottom;
	int height;
	int currenty;
	int xoffset;
	double *currentvalue;
	double finalvalue;

	switch (notifytype)
	{
	case NOTIFY_TIMER:

		//Get the current value
		currentvalue = (double *) agent_getdata(details->agents[AGENTTYPE_GRAPH_AGENT_VALUE], DATAFETCH_VALUE_SCALE);
		finalvalue = (currentvalue == NULL || *currentvalue < 0.0 || *currentvalue > 1.0 ? 0.0 : *currentvalue);

		//Store this value in the value history
		details->historyindex++;
		if (details->historyindex == AGENTTYPE_GRAPH_HISTORYLENGTH) details->historyindex = 0;
		details->valuehistory[details->historyindex] = finalvalue;

		//Notify the control it is time to redraw
		control_notify(a->controlptr, NOTIFY_NEEDUPDATE, NULL);

		break;

	case NOTIFY_DRAW:

		//Get set up for drawing the graph
		di = (styledrawinfo *) messagedata;
		hpen = CreatePen(PS_SOLID, 1, style_get_text_color(STYLETYPE_TOOLBAR));
		oldobj = SelectObject(di->buffer, hpen);

		//Prepare some variables for convenience
		offset_left = di->rect.left + 2;
		width = di->rect.right - di->rect.left - 4;
		offset_top = di->rect.top + 2;
		offset_bottom = di->rect.bottom - 2;
		height = di->rect.bottom - di->rect.top - 4;

		//Make sure we have a large enough area
		if (width > 2 && height > 2)
		{
			//Draw the graph
			int currenthistoryindex = details->historyindex;

			if (details->charttype == AGENTTYPE_GRAPH_CHARTTYPE_FILL)
			{
				for (int currentx = width; currentx >= offset_left; currentx--)
				{
					currenty = offset_bottom - (height*details->valuehistory[currenthistoryindex]);
					MoveToEx(di->buffer, currentx, offset_bottom, NULL);
					LineTo(di->buffer, currentx, currenty);
					currenthistoryindex--;
					if (currenthistoryindex < 0) currenthistoryindex = AGENTTYPE_GRAPH_HISTORYLENGTH - 1;
				}
			}
			else if (details->charttype == AGENTTYPE_GRAPH_CHARTTYPE_LINE)
			{
				currenty = offset_bottom - (height*details->valuehistory[currenthistoryindex]);
				for (int currentx = width; currentx >= offset_left; currentx--)
				{
					MoveToEx(di->buffer, currentx, currenty, NULL);
					currenty = offset_bottom - (height*details->valuehistory[currenthistoryindex]);
					LineTo(di->buffer, currentx-1, currenty);
					currenthistoryindex--;
					if (currenthistoryindex < 0) currenthistoryindex = AGENTTYPE_GRAPH_HISTORYLENGTH - 1;
				}
			}
		}

		//Cleanup
		SelectObject(di->buffer, oldobj);
		DeleteObject(hpen);

		break;

	case NOTIFY_SAVE_AGENT:
		//Write existance
		config_write(config_get_control_setagent_c(a->controlptr, a->agentaction, a->agenttypeptr->agenttypename, agenttype_graph_charttypes[details->charttype]));

		//Save all child agents, if necessary
		for (int i = 0; i < AGENTTYPE_GRAPH_AGENTCOUNT; i++) agent_notify(details->agents[i], NOTIFY_SAVE_AGENT, NULL);

		break;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//agenttype_compoundtext_getdata
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void *agenttype_compoundtext_getdata(agent *a, int datatype)
{
	if (datatype == DATAFETCH_VALUE_TEXT)
	{
		//Get the details
		agenttype_compoundtext_details *details;
		details = (agenttype_compoundtext_details *) a->agentdetails;

		//Copy the characters of the text to the final output string
		int charindex = 0;
		char *currentoutput = details->finaltext;
		int agentindex = 0;
		while (details->text[charindex] != '\0')
		{
			//If we hit a $
			if (details->text[charindex] == '$')
			{
				//If we have an agent
				if (agentindex < AGENTTYPE_COMPOUNDTEXT_MAXAGENTS && details->agents[agentindex] != NULL)
				{
					char *agenttext = (char *) agent_getdata(details->agents[agentindex], DATAFETCH_VALUE_TEXT);
					strcpy(currentoutput, agenttext);
					currentoutput += strlen(agenttext);
				}
				else
				{
					strcpy(currentoutput, "[?]");
					currentoutput += 3;
				}
				agentindex++;
			}
			else
			{
				currentoutput[0] = details->text[charindex];
				currentoutput += 1;
			}

			charindex++;
		}

		//End the string
		currentoutput[0] = '\0';

		return details->finaltext;
	}
	else if (datatype == DATAFETCH_SUBAGENTS_NAMES_ARRAY)
	{
		return agenttype_compoundtext_agentdescriptions;
	}
	else if (datatype == DATAFETCH_SUBAGENTS_POINTERS_ARRAY)
	{
		agenttype_compoundtext_details *details;
		details = (agenttype_compoundtext_details *) a->agentdetails;
		return details->agents;
	}
	else if (datatype == DATAFETCH_SUBAGENTS_TYPES_ARRAY)
	{
		return (void *) agenttype_compoundtext_agenttypes;
	}
	else if (datatype == DATAFETCH_SUBAGENTS_COUNT)
	{
		return (void *) &agenttype_compoundtext_subagentcount;
	}

	return NULL;
}