/*
PROGRAM NAME	wn_matrix.DLL

USED BY			BBC Academy Wood Norton BNCS installation.

VERSION			1.0
DATE			21st February 2015
PROGRAMMER		Andy Woodhouse

Overview
========
This DLL implements a scripted matrix selection panel that supports up to 12 banks of 30 sources per bank.

The panel supports several target panel sizes:
	1)	900 pixels wide for use on a 1024 wide panel that has a 124 pixel wide selector panel to left or right of main display
	2)	1024 pixels wide for full width application on a 1024 by 768 touchscreen.
	3)	1280 pixels for full width application on a 1280 by 1024 touchscreen.


Requirements: 
	Database 5 and Database 6 of the selected instance hold the names for the groups.

The user of the script panel may adjust a number of parameters in this component. The parameters that can be set are:

	instance					Name of the instance to use for this selection set
	host_size					Target panel size for 1:1 presentation. Values 900, 1024, 1280
	pageoffset					
	mapdb						Database to use for mapping of buttons. Default is 8
	mapoffset					Offset to add to the page number for button mapping
	groupmapoffset				Offset to add to the group page number for for button mapping 
	namesdb						Database used to get names. Default is 0 (sources)
	stylesheet.group.selected	The styleesheet name to use for the active button of the group selection buttons.
	stylesheet.group.deselected	The stylesheet name to use for the inactive buttons of the group selection buttons.
	stylesheet.item.selected	The stylesheet name to use for the selected item of the item selection buttons.
	stylesheet.item.deselected	The stylesheet name to use for the deselected items of the item selection buttons.
	colour.background			The colour id string for the background label.

	Return notifications:
	index						Index of the currently selected button.
	select						The index of the button to highlight, or -1 to remove highlights
*/


#include <windows.h>
#include <stdio.h>
#include <bncs_string.h>
#include <bncs_config.h>
#include "wn_matrix.h"

#define MYPANEL	1
#define GRPSELECT		"WNGroupSelected"			// Default statesheet for group button selected
#define GRPDESELECT		"WNGroupDeselected"			// Default statesheet for group button, not selected
#define ITEMSELECT		"WNSourceSelected"			// Default statesheet for item selected button. 
#define ITEMDESELECT	"WNSourceDeselected"		// Default statesheet for item deselected button.
#define BASECOLOUR		"#505050"					// Default colour for background panel.

// Make our class visible to the outside world
EXPORT_BNCS_SCRIPT(wn_matrix)


// +++++++++++++++++++++++++++++++++++++++++++++ //
// Constructor - equivalent to ApplCore STARTUP. //
// +++++++++++++++++++++++++++++++++++++++++++++ //
wn_matrix::wn_matrix(bncs_client_callback *parent, const char *path) : bncs_script_helper(parent, path)
{
	// Initialise variables to defaults. These will be over-ridden by the customisation from the visual editor.
	panel_name = "";
	panel_displayed = false;
	host_size = "900";
	m_instance = "select_900.bncs_ui";
	m_pageoffset = "0";						// Page offset - added to page. Used when we select destination database
	m_mapdb = "8";							// Database to use for mapping of buttons. Default is 8
	m_mapoffset = "0";						// Offset to add to the page number for button mapping
	m_groupmapoffset = "250";				// Offset to add to the group page number for button mapping
	m_namesdb = "0";						// Database used to get names.Default is 0 (sources)
	grp_selected_state = GRPSELECT;			// The statesheet name used for the group buttons, selected group.
	grp_deselected_state = GRPDESELECT;		// The statesheet name used for the group buttons, unselected state.
	item_selected_state = ITEMSELECT;		// The statesheet name used for the selected item button.
	item_deselected_state = ITEMDESELECT;	// The statesheet name used for the deselected item buttons.
	background_colour = BASECOLOUR;			// The statesheet used to set the colour of the background panel.
	myIndex = -1;							// Index of the currently selected button. -1 indicates no selection
	group_page = 0;							// Page number of the current group. Range 1 to 12 for valid selections

	panelShow(MYPANEL, panel_name);
	panel_displayed = true;
	// Send customisation data to the panel
	textPut("instance", m_instance, MYPANEL, "GrpSelect");
	textPut("instance.map", m_instance, MYPANEL, "GrpSelect");
	textPut("instance", m_instance, MYPANEL, "ItemSelect");
	textPut("instance.map", m_instance, MYPANEL, "ItemSelect");
	textPut("stylesheet.selected", grp_selected_state, MYPANEL, "GrpSelect");
	textPut("stylesheet.deselected", grp_deselected_state, MYPANEL, "GrpSelect");
	textPut("stylesheet.selected", item_selected_state, MYPANEL, "ItemSelect");
	textPut("stylesheet.deselected", item_deselected_state, MYPANEL, "ItemSelect");
	textPut("pageoffset", m_pageoffset, MYPANEL, "ItemSelect");

	// Select bank 1 of the matrix. The bank will be displayed by the revertive generated by the button push.
	textPut("button.1=released", MYPANEL, "GrpSelect");
	group_page = 1;
	
}


// ++++++++++++++++++++++++++++++++++++++++++++++ //
// Destructor - equivalent to ApplCore CLOSEDOWN. //
// ++++++++++++++++++++++++++++++++++++++++++++++ //
wn_matrix::~wn_matrix()
{
}


// ++++++++++++++++++++++++++++++++++++++++++++++ //
// All button pushes and notifications come here. //
// ++++++++++++++++++++++++++++++++++++++++++++++ //
void wn_matrix::buttonCallback( buttonNotify *b )
{
	if (b->panel() == MYPANEL)
	{
		// Check for a Group Selection button press
		if (b->id() == "GrpSelect")
		{
			if (b->command() == "index")							// Be defensive, in case there are other notifications
			{
				group_page = b->value().toInt();
				textPut("index=-1", MYPANEL, "ItemSelect");			// Deselect any active item button
				myIndex = -1;										// Set the local "no selection" value
				textPut("page", b->value(), MYPANEL, "ItemSelect");	// Change the page of displayed data 
			}
			return;
		}	// End of if (b->id() == "GrpSelect")

		if (b->id() == "ItemSelect")								// Be defensive, in case there are other notifications
		{
			if (b->command() == "index")
			{
				myIndex = b->value().toInt();
				hostNotify("index=" + b->value());					// Tell our host the new index
			}
		}
	}
}

// +++++++++++++++++++++++++ //
// All revertives come here. //
// +++++++++++++++++++++++++ //
int wn_matrix::revertiveCallback( revertiveNotify * r )
{
	return 0;
}


// +++++++++++++++++++++++++++++++++++++++++ //
// All database name changes come back here. //
// +++++++++++++++++++++++++++++++++++++++++ //
void wn_matrix::databaseCallback( revertiveNotify * r )
{
}


// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// All parent notifications come here i.e. when this script is just one 
// component of another dialog then our host might want to tell us things
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
bncs_string wn_matrix::parentCallback( parentNotify *p )
{
	if( p->command() == "return" )
	{
		if( p->value() == "all" )
		{	// Persisting values for bncs_vis_ed
			bncs_stringlist sl = "";
			// Create the list of values
			sl << bncs_string("host_size=%1").arg(host_size);
			sl << bncs_string("mapdb=%1").arg(m_mapdb);
			sl << bncs_string("namesdb=%1").arg(m_namesdb);
			sl << bncs_string("mapoffset=%1").arg(m_mapoffset);
			sl << bncs_string("groupmapoffset=%1").arg(m_groupmapoffset);
			sl << bncs_string("statesheet.group.selected=%1").arg(grp_selected_state);
			sl << bncs_string("statesheet.group.deselected=%1").arg(grp_deselected_state);
			sl << bncs_string("statesheet.item.selected=%1").arg(item_selected_state);
			sl << bncs_string("statesheet.item.deselected=%1").arg(item_deselected_state);
			sl << bncs_string("colour.background=%1").arg(background_colour);
			return sl.toString( '\n' );
		}

	}	// End of ( p->command() == "return" )

	else if(p->command() == "instance" && p->value() != m_instance)
	{	
		// Our instance is being set or changed
		m_instance = p->value();					// Save the new instance value
		
		// Need to move the instance data into the group and item selector name grids.
		textPut("instance", m_instance, MYPANEL, "GrpSelect");
		textPut("instance.map", m_instance, MYPANEL, "GrpSelect");
		textPut("instance", m_instance, MYPANEL, "ItemSelect");
		textPut("instance.map", m_instance, MYPANEL, "ItemSelect");
		return "";
	}

	else if (p->command() == "host_size")
	{
		// Validate the size options
		if ( !((p->value() == "900") || (p->value() == "1024") || (p->value() == "1280")) )
			return bncs_string("Bad_Size");// No valid size - so return an error string
		
		// Use the valid size data to construct the file name.
		host_size = p->value();
		panel_name = "select_" + p->value() + ".bncs_ui";
		if (panel_displayed) panelDestroy(MYPANEL);		// Remove existing panel
		panelShow(MYPANEL, panel_name);					// Show the new one of the desired size
		// Send customisation data to the panel
		textPut("instance", m_instance, MYPANEL, "GrpSelect");
		textPut("instance.map", m_instance, MYPANEL, "GrpSelect");
		textPut("instance", m_instance, MYPANEL, "ItemSelect");
		textPut("instance.map", m_instance, MYPANEL, "ItemSelect");
		textPut("stylesheet.selected", grp_selected_state, MYPANEL, "GrpSelect");
		textPut("stylesheet.deselected", grp_deselected_state, MYPANEL, "GrpSelect");
		textPut("stylesheet.selected", item_selected_state, MYPANEL, "ItemSelect");
		textPut("stylesheet.deselected", item_deselected_state, MYPANEL, "ItemSelect");
		textPut("pageoffset", m_pageoffset, MYPANEL, "ItemSelect");

		// Select bank 1 of the matrix. The bank will be displayed by the revertive generated by the button push.
		textPut("button.1=released", MYPANEL, "GrpSelect");
		group_page = 1;
		return "";
	}	// End of else if (p->command() == "host_size")

	else if (p->command() == "mapdb")
	{
		// Changing the mapping database
		m_mapdb = p->value();			// Keep a record
		textPut("mapdb", m_mapdb, MYPANEL, "ItemSelect");
		return "";
	}

	else if (p->command() == "namesdb")
	{
		// Change to the names database used for button names. Update the group in sympathy to code used for items
		m_namesdb = p->value();			// Keep a record
		textPut("namesdb", m_namesdb, MYPANEL, "ItemSelect");
		if ((m_namesdb == "0") || (m_namesdb == "2"))	textPut("namesdb=4", MYPANEL, "GrpSelect");	// Sources selected
		if ((m_namesdb == "1") || (m_namesdb == "3"))	textPut("namesdb=5", MYPANEL, "GrpSelect");	// Destinations selected
		return "";
	}

	else if (p->command() == "mapoffset")
	{
		// Change the offset used for the item block.
		m_mapoffset = p->value();									// Keep a record of the new value.
		textPut("mapoffset", m_mapoffset, MYPANEL, "UnitSelect");
		return "";
	}

	else if (p->command() == "groupmapoffset")
	{
		// Change the map page offset used for the group block.
		m_groupmapoffset = p->value();								// Keep a record of the new value.
		textPut("mapoffset", m_groupmapoffset, MYPANEL, "GrpSelect");
		return "";
	}

	else if (p->command() == "statesheet")
	{
		// Host is sending a new stateshet name for either the group of the item name grids.
		// Takes the form of "statesheet.group.selected=alarm_active". For this example:=
		// p->command() equals "statesheet"
		// p->sub(0)    equals "group"
		// p->sub(2)    equals "selected"
		// p->value()   equals "alarm_active"
		// p->subs()	equals 2
		if (p->subs() == 2)
		{
			if (p->sub(0) == "group")
			{
				if (p->sub(1) == "selected")
				{
					// statesheet.group.selected=
					grp_selected_state = p->value();
					textPut("stylesheet.selected", grp_selected_state, MYPANEL, "GrpSelect");
					return "";
				}

				if (p->sub(1) == "deselected")
				{
					// statesheet.group.deselected=
					grp_deselected_state = p->value();
					textPut("stylesheet.deselected", grp_deselected_state, MYPANEL, "GrpSelect");
					return "";
				}
			}	// End of if (p->sub(0) == "group")

			if (p->sub(0) == "item")
			{
				if (p->sub(1) == "selected")
				{
					// statesheet.item.selected=
					item_selected_state = p->value();
					textPut("stylesheet.selected", item_selected_state, MYPANEL, "ItemSelect");
					return "";
				}

				if (p->sub(1) == "deselected")
				{
					// statesheet.item.deselected=
					item_deselected_state = p->value();
					textPut("stylesheet.deselected", item_deselected_state, MYPANEL, "ItemSelect");
					return "";
				}
			}	// End of if (p->sub(0) == "item"
		}		// End of if (p->subs() == 2)
	}			// End of if (p->command() == "statesheet"

	else if (p->command() == "colour")
	{
		if (p->sub(0) == "background")
		{
			background_colour = p->value();
			textPut("colour.background", background_colour, MYPANEL, "BG_1");
			return "";
		}
	}

	else if (p->command() == "select")
	{
		// Set the current highlighted selection. If the value is positive, and the index is currently mapped,
		// the button is highlighted. If the provided value equals -1 the highlighting is removed.
		myIndex = p->value().toInt();							// Keep a copy of the value (as an integer).
		textPut("index", p->value(), MYPANEL, "ItemSelect");	// Ask the name grid to do the highlighting.
		if (myIndex == -1) hostNotify("index=-1");				// Report the "clear selection" value to the connections engine.
		return "";
	}


	// ++++++++++++++++++++++++++++++++++++++++++ //
	// ***** CONNECTIONS EVENTS HELPER LIST ***** //
	// ++++++++++++++++++++++++++++++++++++++++++ //
	else if( p->command() == "_events" )
	{	
		// Helper-list of everything in this component generated by hostNotify's
		bncs_stringlist sl;

		sl << "index=*";		
		return sl.toString( '\n' );
	}


	// ++++++++++++++++++++++++++++++++++++++++++++ //
	// ***** CONNECTIONS COMMANDS HELPER LIST ***** //
	// ++++++++++++++++++++++++++++++++++++++++++++ //
	else if( p->command() == "_commands" )
	{	
		// Helper-list of any commands/parameters you might want to set at run-time
		bncs_stringlist sl;
		
		sl << "index=[value]";
		sl << "select=[value]";
		
		return sl.toString( '\n' );
	}

	return "";
}

// timer events come here
void wn_matrix::timerCallback( int id )
{
	switch( id )
	{
	
	default:	// Unhandled timer event
		timerStop(id);
		break;
	}
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////// Callbacks above - Methods below ///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////


