//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//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;
	}

}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//agenttype_mixer_notify
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void agenttype_mixer_notify_xp (agent *a, int notifytype, void *messagedata)
{
	
	//Get the agent details
	AgentType_Mixer_XP * details = static_cast<AgentType_Mixer_XP *>(a->agentdetails);

	//Variables
	double *value_double = NULL;
	bool *value_bool = NULL;
	MIXERCONTROLDETAILS_UNSIGNED    mixer_setcontrol_value_double;
	MIXERCONTROLDETAILS_BOOLEAN     mixer_setcontrol_value_bool;

	switch (notifytype)
	{
		case NOTIFY_CHANGE:
		{
			//Set up the values
			if (a->agenttypeptr->format & CONTROL_FORMAT_SCALE)
			{
				value_double = (double *) messagedata;
				details->m_mixer_controldetails.paDetails = &mixer_setcontrol_value_double;
			}
			else if (a->agenttypeptr->format & CONTROL_FORMAT_BOOL)
			{
				value_bool = (bool *) messagedata;
				details->m_mixer_controldetails.paDetails = &mixer_setcontrol_value_bool;
			}

			//Retrieve the details
			if (MMSYSERR_NOERROR != mixerGetControlDetails((HMIXEROBJ) details->m_mixer_handle, &details->m_mixer_controldetails, MIXER_GETCONTROLDETAILSF_VALUE)) return;

			//Set the value
			if (a->agenttypeptr->format & CONTROL_FORMAT_SCALE)
			{
				mixer_setcontrol_value_double.dwValue = (ULONG) (*value_double * 65535);
			}
			else if (a->agenttypeptr->format & CONTROL_FORMAT_BOOL)
			{
				mixer_setcontrol_value_bool.fValue = *value_bool;
			}

			//Reload the details
			MMRESULT const res = mixerSetControlDetails((HMIXEROBJ) details->m_mixer_handle, &details->m_mixer_controldetails, MIXER_SETCONTROLDETAILSF_VALUE);
			if (MMSYSERR_NOERROR != res) return;

			break;
		}
		case NOTIFY_SAVE_AGENT:
			//Write existance
			char temp[30];
			sprintf(temp, "%d %d %d", (int)details->m_device, (int)details->m_line, (int)details->m_control);
			config_write(config_get_control_setagent_c(a->controlptr, a->agentaction, a->agenttypeptr->agenttypename, temp));
			break;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//agenttype_systemmonitor_notify
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void agenttype_systemmonitor_notify(agent *a, int notifytype, void *messagedata)
{
	//Get the agent details
	agenttype_systemmonitor_details *details = (agenttype_systemmonitor_details *) a->agentdetails;

	switch(notifytype)
	{
		case NOTIFY_CHANGE:
			control_notify(a->controlptr, NOTIFY_NEEDUPDATE, NULL);
			break;

		case NOTIFY_SAVE_AGENT:
			//Write existance
			config_write(config_get_control_setagent_c(a->controlptr, a->agentaction, a->agenttypeptr->agenttypename, agenttype_systemmonitor_types[details->monitor_type]));
			break;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//agenttype_networkmonitor_notify
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void agenttype_networkmonitor_notify(agent *a, int notifytype, void *messagedata)
{
	//Get the agent details
	agenttype_networkmonitor_details *details = (agenttype_networkmonitor_details *) a->agentdetails;

	switch(notifytype)
	{
		case NOTIFY_CHANGE:
			control_notify(a->controlptr, NOTIFY_NEEDUPDATE, NULL);
			break;

		case NOTIFY_SAVE_AGENT:
			//Write existance
			config_write(config_get_control_setagent_c(a->controlptr, a->agentaction, a->agenttypeptr->agenttypename, agenttype_networkmonitor_interface_numbers[details->monitor_interface_number]));
			config_write(config_get_control_setagentprop_i(a->controlptr,a->agentaction, "MonitorType",&details->monitor_types));
			break;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//agenttype_winamp_notify
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void agenttype_winamppoller_notify(agent *a, int notifytype, void *messagedata)
{
	//Get the agent details
	agenttype_winamp_details *details = (agenttype_winamp_details *) a->agentdetails;

	long length_seconds;
	long length_milliseconds;
	long newposition_milliseconds;
	struct slider_data
	{
		int event;
		double value;
	} *msgdata;

	switch (notifytype)
	{
	case NOTIFY_CHANGE:

		//Get the window
		agenttype_winamp_getwinampwindow();
		if (!winamp_hwnd) return;

		//Get the length of the track
		length_seconds = (long) SendMessage(winamp_hwnd, 0x400, 1, 105);
		if (length_seconds == -1) return;
		length_milliseconds = length_seconds * 1000;

		//Get the percentage in
		msgdata = (struct slider_data *)messagedata;
		newposition_milliseconds = (long) (length_milliseconds * msgdata->value);

		//Set the milliseconds
		SendMessage(winamp_hwnd, WM_USER, newposition_milliseconds, 106);

		break;
	case NOTIFY_SAVE_AGENT:
		config_write(config_get_control_setagent_c(a->controlptr, a->agentaction, a->agenttypeptr->agenttypename, agenttype_winamp_pollingnames[details->commandcode]));
		break;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//agenttype_system_notify
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void agenttype_system_notify(agent *a, int notifytype, void *messagedata)
{
	//Get the agent details
	agenttype_system_details *details = (agenttype_system_details *) a->agentdetails;

	switch (notifytype)
	{
	case NOTIFY_CHANGE:
		//Send the message
		PostMessage(
			plugin_hwnd_blackbox,
			agenttype_system_touples[details->commandindex].msg,
			agenttype_system_touples[details->commandindex].wparam,
			0);
		break;

	case NOTIFY_SAVE_AGENT:
		//Write existance
		config_write(config_get_control_setagent_c(a->controlptr, a->agentaction, a->agenttypeptr->agenttypename, agenttype_system_touples[details->commandindex].name));
		break;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//agenttype_compoundtext_notify
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void agenttype_compoundtext_notify(agent *a, int notifytype, void *messagedata)
{
	//Get the agent details
	agenttype_compoundtext_details *details;
	details = (agenttype_compoundtext_details *) a->agentdetails;

	switch(notifytype)
	{
		//case NOTIFY_CHANGE:
			//Eventually, we'll have to update the text every time an
			//update is made. But this isn't necessary now.

		case NOTIFY_SAVE_AGENT:
			//Write existance
			config_write(config_get_control_setagent_c(a->controlptr, a->agentaction, a->agenttypeptr->agenttypename, details->text));
			
			//Save all child agents, if necessary
			for (int i = 0; i < AGENTTYPE_COMPOUNDTEXT_MAXAGENTS; i++) agent_notify(details->agents[i], NOTIFY_SAVE_AGENT, NULL);

			break;
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//agenttype_bitmap_notify
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void agenttype_bitmap_notify(agent *a, int notifytype, void *messagedata)
{
	//Get the agent details
	agenttype_bitmap_details *details = (agenttype_bitmap_details *) a->agentdetails;

	styledrawinfo *di;
	int xpos, ypos;

	switch(notifytype)
	{
		case NOTIFY_DRAW:
			di = (styledrawinfo *) messagedata;

			if (details->is_icon)
			{
				HICON load_sysicon(wchar_t *filepath, int size);
				HICON hIcon = (HICON)LoadImage(plugin_instance_plugin, details->absolute_path, IMAGE_ICON, details->width, details->height, LR_LOADFROMFILE);
				// this one can retrieve the standard system icons also:
				if (NULL == hIcon) hIcon = load_sysicon(details->absolute_path, 32);

				if (NULL != hIcon)
				{
					switch (details->halign)
					{
					case 0:
						xpos = ((di->rect.right - di->rect.left) / 2) - (details->width / 2);
						break;
					case 1:
						xpos = 2;
						break;
					case 2:
						xpos = (di->rect.right -  2) - (details->width);
						break;
					}
					switch (details->valign)
					{
					case 0:
						ypos = ((di->rect.bottom - di->rect.top) / 2) - (details->height / 2);
						break;
					case 1:
						ypos = 2;
						break;
					case 2:
						ypos = (di->rect.bottom - 2) - (details->height);
						break;
					}

					drawIcon (xpos, ypos, details->width, (HICON) hIcon, di->buffer, di->apply_sat_hue);
					//DrawIconEx(di->buffer, xpos, ypos, (HICON) hIcon, details->width, details->height, 0, NULL, DI_NORMAL);
					DestroyIcon(hIcon);
				}
			}
			else
			{
			  	pImage = new Gdiplus::Image(details->absolute_path);

				if (NULL != pImage)
				{
					details->width = pImage->GetWidth() * ((double)details->scale / 100);
					details->height = pImage->GetHeight() * ((double)details->scale / 100);

					switch (details->halign)
					{
					case 0:
						xpos = ((di->rect.right - di->rect.left) / 2) - (details->width / 2);
						break;
					case 1:
						xpos = 2;
						break;
					case 2:
						xpos = (di->rect.right -  2) - (details->width);
						break;
					}
					switch (details->valign)
					{
					case 0:
						ypos = ((di->rect.bottom - di->rect.top) / 2) - (details->height / 2);
						break;
					case 1:
						ypos = 2;
						break;
					case 2:
						ypos = (di->rect.bottom - 2) - (details->height);
						break;
					}

					imageAttr = new Gdiplus::ImageAttributes();
					imageAttr->SetColorKey(RGB(255,0,255), RGB(255,0,255));

					graphics = new Gdiplus::Graphics(di->buffer);
					graphics->DrawImage(pImage, Gdiplus::Rect(xpos, ypos, details->width, details->height),
                                                            0, 0, pImage->GetWidth(), pImage->GetHeight(),
                                                            Gdiplus::UnitPixel, imageAttr, NULL, NULL);

					delete imageAttr;
					delete graphics;
					delete pImage;
				}
			}
			break;

		case NOTIFY_SAVE_AGENT:
			//Write existance
			config_write(config_get_control_setagent_c(a->controlptr, a->agentaction, a->agenttypeptr->agenttypename, details->filename));
			//Save properties
			if (details->is_icon) config_write(config_get_control_setagentprop_i(a->controlptr, a->agentaction, L"Size", &details->width));
			else if (details->filename) config_write(config_get_control_setagentprop_i(a->controlptr, a->agentaction, L"Scale", &details->scale));
			config_write(config_get_control_setagentprop_c(a->controlptr, a->agentaction, L"VAlign", image_valigns[details->valign]));
			config_write(config_get_control_setagentprop_c(a->controlptr, a->agentaction, L"HAlign", image_haligns[details->halign]));
			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;
	}
}