#include <windows.h>
#include <stdio.h>
#include <bncs_string.h>
#include <bncs_config.h>
#include "CARSD.h"

#define MAIN_PANEL	1
#define MAIN_PANEL_NAME "p1.bncs_ui"

#define LEVEL_ENABLED_IMG	"/images/enabledtick.png"		// Name of image to load when a routing level is enabled
#define LEVEL_DISABLED_IMG	"/images/disabledtick.png"		// Name of image to load when a routing level is disabled
#define	TAKE_LOW	"WN_TakeLow"							// Name of stylesheet to load when want dark red TAKE button
#define	TAKE_HIGH	"WN_TakeHigh"							// Name of stylesheet to load when want brightTAKE_LOW red TAKE button
#define SOURCE_SELECTED		"WNSourceSelected"
#define SOURCE_DESELECTED	"WNSourceDeselected"

#define TIMER_SETUP	1
#define TAKE_ENABLED_TIMER 2
#define TAKE_FLASHER 3

#define SOURCEDATABASE 0
#define DESTDATABASE 1

// +++++++++++++++++++++++++++++++++++++++++++ //
// Make our class visible to the outside world //
// +++++++++++++++++++++++++++++++++++++++++++ //
EXPORT_BNCS_SCRIPT( CARSD )


// ++++++++++++++++++++++++++++++++++++++++++++ //
// Constructor - equivalent to ApplCore STARTUP //
// ++++++++++++++++++++++++++++++++++++++++++++ //
CARSD::CARSD( bncs_client_callback * parent, const char * path ) : bncs_script_helper( parent, path )
{
	// Ensure we have some "safe" default values
	car_names = 0;
	car_xpts = 0;
	car_sdsdi = 0;
	car_audio = 0;

	// Show our panel using an id of MAIN_PANEL
	panelShow(MAIN_PANEL, MAIN_PANEL_NAME);

	// Extract the classroom= parameter from the workstation file
	ws_sl = getWorkstationSetting("classroom").upper();

	// Extract the device id's for central routers
	getDev("CAR Package Names", &car_names);		// CAR SDI Router package names
	getDev("CAR Package XPTS", &car_xpts);			// CAR SDI Package contents
	getDev("CAR SDI Router", &car_sdsdi);			// CAR SDI router
	getDev("CAR Audio Router", &car_audio);			// CAR Audio router driver id

	// Set up the router parameters in the source selection buttons
	textPut("device", car_names, MAIN_PANEL, 10);				// Group selection device numbers
	textPut("device.map", car_names, MAIN_PANEL, 10);

	textPut("device", car_names, MAIN_PANEL, 11);				// Source selection device numbers
	textPut("device.map", car_names, MAIN_PANEL, 11);
	
	// Because the control does not respond to command index=xx as described by the help file we have to kludge
	textPut("button.001=released", MAIN_PANEL, 10);				// Select Group page 1 by pretending to push the button

	// Extract the area specific parameters from "wn_academy.xml"
	myVals = bncs_config("wn_academy." + ws_sl + ".car_sd_os_first");		// First SD OS destination
	myFirstSDIOS = myVals.attr("value").toInt();

	myVals = bncs_config("wn_academy." + ws_sl + ".car_sd_os_last");		// Last SD OS destination
	myLastSDIOS = myVals.attr("value").toInt();

	myVals = bncs_config("wn_academy." + ws_sl + ".car_audio_os_first");	// First Audio OS destination
	myFirstAudioOS = myVals.attr("value").toInt();

	myVals = bncs_config("wn_academy." + ws_sl + ".car_audio_os_last");		// Last Audio OS destination
	myLastAudioOS = myVals.attr("value").toInt();

	// Get the package number for the routing process
	myVals = bncs_config("wn_academy." + ws_sl + ".sd_fixedpkg_os1");
	dest_pkg1 = myVals.attr("value").toInt();

	// Setup defaults for source and destination indicies and selected flags
	source_selected = false;
	source_index = 0;
	dest_selected = false;
	dest_index = 0;

	// Set up the level enabled properties
	videoEnabled = true;
	audioEnabled = true;
	textPut("pixmap", LEVEL_ENABLED_IMG, MAIN_PANEL, "VideoEnable");
	textPut("pixmap", LEVEL_ENABLED_IMG, MAIN_PANEL, "AudioEnable");
	takeEnabled = false;
	controlDisable(MAIN_PANEL, "TAKE");

	// Find current state of OS routes
	routerRegister(car_sdsdi, myFirstSDIOS, myLastSDIOS);					// SDI OS destinations
	routerPoll(car_sdsdi, myFirstSDIOS, myLastSDIOS);
	routerRegister(car_audio, myFirstAudioOS, myLastAudioOS);				// Audio OS destinations
	routerPoll(car_audio, myFirstAudioOS, myLastAudioOS);
}

// +++++++++++++++++++++++++++++++++++++++++++++ //
// Destructor - equivalent to ApplCore CLOSEDOWN //
// +++++++++++++++++++++++++++++++++++++++++++++ //
CARSD::~CARSD()
{
	// Do some tidy up processing
	if (takeEnabled)						// If take mode enabled, stop the timers.
	{
		timerStop(TAKE_FLASHER);
		timerStop(TAKE_ENABLED_TIMER);
	}

	routerUnregister(car_sdsdi);			// Unregister for devices for revertives
	routerUnregister(car_audio);
}

// All button pushes and notifications come here
void CARSD::buttonCallback( buttonNotify *b )
{
	if( b->panel() == 1 )
	{
		if (b->id() == "10")		// Is this the group selection button
		{
			// Yes - come here with new button index in b->value(). Set page number of unit selector control
			textPut("page", b->value(), MAIN_PANEL, 11);
			return;
		}	// End of source group selection processing

		if (b->id() == "11")		// Source selection button
		{
			if (b->value() > 0)
			{
				source_selected = true;
				source_index = b->value().toInt();
				checkTake();						// Do we need to enable the TAKE button?
			}
			else
			{
				source_selected = false;
				source_index = 0;
				// If TAKE is enabled - deselect it
				if (takeEnabled)
				{
					takeEnabled = false;
					textPut("stylesheet", TAKE_LOW, MAIN_PANEL, "TAKE");
					timerStop(TAKE_FLASHER);
					timerStop(TAKE_ENABLED_TIMER);
					controlDisable(MAIN_PANEL, "TAKE");
				}
			}

			return;
		}	// End of Source Selection button processing

		if (b->id() == "OS1")
		{
			if (dest_selected)	textPut("statesheet", SOURCE_DESELECTED, MAIN_PANEL, selected_os);	// Clear old highlight
			selected_os = b->id();												// Define new button
			dest_selected = true;
			dest_index = dest_pkg1;												// Note the active destination package number
			textPut("statesheet", SOURCE_SELECTED, MAIN_PANEL, selected_os);	// Highlight the new active destination
			checkTake();

			return;
		}

		if (b->id() == "OS2")
		{
			if (dest_selected)	textPut("statesheet", SOURCE_DESELECTED, MAIN_PANEL, selected_os);		// Clear old highlight
			selected_os = b->id();														// Define new button
			dest_selected = true;
			dest_index = dest_pkg1 + 1;
			textPut("statesheet", SOURCE_SELECTED, MAIN_PANEL, selected_os);
			checkTake();

			return;
		}

		if (b->id() == "OS3")
		{
			if (dest_selected)	textPut("statesheet", SOURCE_DESELECTED, MAIN_PANEL, selected_os);		// Clear old highlight
			selected_os = b->id();														// Define new button
			dest_selected = true;
			dest_index = dest_pkg1 + 2;
			textPut("statesheet", SOURCE_SELECTED, MAIN_PANEL, selected_os);
			checkTake();

			return;
		}

		if (b->id() == "VideoEnable")
		{
			if (videoEnabled)
				textPut("pixmap", LEVEL_DISABLED_IMG, MAIN_PANEL, "VideoEnable");
			else
				textPut("pixmap", LEVEL_ENABLED_IMG, MAIN_PANEL, "VideoEnable");
			videoEnabled = !videoEnabled;		// Toggle the flag state

			return;
		}

		if (b->id() == "AudioEnable")
		{
			if (audioEnabled)
				textPut("pixmap", LEVEL_DISABLED_IMG, MAIN_PANEL, "AudioEnable");
			else
				textPut("pixmap", LEVEL_ENABLED_IMG, MAIN_PANEL, "AudioEnable");
			audioEnabled = !audioEnabled;		// Toggle the flag state

			return;
		}

		if (b->id() == "CANCEL")
		{
			if (source_selected)					// Is there an active source?
			{
				textPut("index=0", MAIN_PANEL, 11);
				source_selected = false;
				source_index = 0;
			}

			if (dest_selected)
			{
				textPut("statesheet", SOURCE_DESELECTED, MAIN_PANEL, selected_os);		// Clear highlight
				selected_os = "";
				dest_selected = false;
				dest_index = 0;
			}

			// Check if TAKE button enabled - if so cancel it
			if (takeEnabled)
			{
				takeEnabled = false;
				textPut("stylesheet", TAKE_LOW, MAIN_PANEL, "TAKE");
				timerStop(TAKE_FLASHER);
				timerStop(TAKE_ENABLED_TIMER);
				controlDisable(MAIN_PANEL, "TAKE");
			}

			return;
		}

		if (b->id() == "TAKE")
		{

			// Process the actions for the TAKE button. Note that the take button can only generate an event when both a source and
			// a destination have already been activated. Two package numbers are known, one for the source and one for the destination.
			// Each package has both a name and a content element. The content element is the significant one for the TAKE action.
			// The content elements are pairs of comma delimited values such as "0231=17,19". 
			// The first number is the SDI level crosspoint element, and the second number is the audio level.
			// If the element is not required (eg a mute video) then the element has a "-" in place of the number - eg "0127=34,-"
			source_sdi = 0;							// Set defaults for crosspoints
			source_audio = 0;
			dest_sdi = 0;
			dest_audio = 0;

			timerStop(TAKE_FLASHER);				// Stop the flasher timer
			timerStop(TAKE_ENABLED_TIMER);			// Stop the timeout timer
			textPut("stylesheet", TAKE_LOW, MAIN_PANEL, "TAKE");
			controlDisable(MAIN_PANEL, "TAKE");		// Disable the TAKE button

			// Get the source string from the relevant dev_xxx.ini file, and split into sdi and audio elements
			routerName(car_xpts, SOURCEDATABASE, source_index, srctext);	// Get the source comma delimited pair
			if (srctext.split(',', sp1, sp2) > -1)
			{
				if (sp1 != "-") source_sdi = sp1.toInt();		// Extract SDI source index, default value 0 set above for no crosspoint
				if (sp2 != "-") source_audio = sp2.toInt();		// Extract Audio source index.
			}

			// Repeat process for destination contents
			routerName(car_xpts, DESTDATABASE, dest_index, dsttext);		// Get the destination comma delimited pair
			if (dsttext.split(',', dp1, dp2) > -1)
			{
				if (dp1 != "-") dest_sdi = dp1.toInt();			// Extract SDI source index, default value 0 set above for no crosspoint
				if (dp2 != "-") dest_audio = dp2.toInt();		// Extract Audio source index.
			}

			// Make the crosspoints. Only need to issue a crosspoint command if the level is enabled, and both source and destination indicies
			// are greater than 0
			if ((videoEnabled) && (source_sdi != 0) && (dest_sdi != 0))
			{
				routerCrosspoint(car_sdsdi, source_sdi, dest_sdi);
			}

			if ((audioEnabled) && (source_audio != 0) && (dest_audio != 0))
			{
				routerCrosspoint(car_audio, source_audio, dest_audio);
			}

			// Clear the sources, destinations, and flags
			textPut("index=0", MAIN_PANEL, 11);			// Clear source info
			source_selected = false;
			source_index = 0;
			textPut("statesheet", SOURCE_DESELECTED, MAIN_PANEL, selected_os);		// Clear destination highlight
			selected_os = "";
			dest_selected = false;
			dest_index = 0;
			takeEnabled = false;

			return;
		}	// End of TAKE button processing

	}	// End of if (b->panel() == 1) ....
}		// End of button callback processing


// ++++++++++++++++++++++++ //
// All revertives come here //
// ++++++++++++++++++++++++ //
int CARSD::revertiveCallback( revertiveNotify * r )
{
	if (r->device() == car_sdsdi)		// CAR SD-SDI Revertives
	{
		textPut("text", r->sInfo(), MAIN_PANEL, 21 + r->index() - myFirstSDIOS);
	}
	
	if (r->device() == car_audio)		// CAR Audio Revertives
	{
		textPut("text", r->sInfo(), MAIN_PANEL, 31 + r->index() - myFirstAudioOS);
	}
	
	return 0;
}

// ++++++++++++++++++++++++++++++++++++++++ //
// All database name changes come back here //
// ++++++++++++++++++++++++++++++++++++++++ //
void CARSD::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 CARSD::parentCallback( parentNotify *p )
{
	if( p->command() == "return" )
	{
		if( p->value() == "all" )
		{	// Persisting values for bncs_vis_ed
			bncs_stringlist sl;
			
			sl << bncs_string( "myParam=%1" ).arg( m_myParam );
			
			return sl.toString( '\n' );
		}

		else if( p->value() == "myParam" )
		{	// Specific value being asked for by a textGet
			return( bncs_string( "%1=%2" ).arg( p->value() ).arg( m_myParam ) );
		}

	}
	else if( p->command() == "instance" && p->value() != m_instance )
	{	// Our instance is being set/changed
		m_instance = p->value();
		//Do something instance-change related here
	}

	else if( p->command() == "myParam" )
	{	// Persisted value or 'Command' being set here
		m_myParam = p->value();
	}

	// ***** CONNECTIONS EVENTS HELPER LIST *****
	else if( p->command() == "_events" )
	{	// Helper-list of everything in this component generated by hostNotify's
		bncs_stringlist sl;

		sl << "notify=*";		
		
		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 << "myParam=[value]";
		
		return sl.toString( '\n' );
	}

	return "";
}


// +++++++++++++++++++++++ //
// Timer events come here. //
// +++++++++++++++++++++++ //
void CARSD::timerCallback( int id )
{
	switch (id)
	{
	case TIMER_SETUP:
		timerStop(id);
		break;

	case TAKE_ENABLED_TIMER:
		if (takeEnabled)
		{
			timerStop(TAKE_ENABLED_TIMER);			// Stop the TAKE timeout counter
			timerStop(TAKE_FLASHER);				// Stop the take state toggle time
			textPut("statesheet", TAKE_HIGH, MAIN_PANEL, "TAKE");	
			controlDisable(MAIN_PANEL, "TAKE");
			takeEnabled = false;
		}
		break;

	case TAKE_FLASHER:
		if (takeState)
			textPut("statesheet", TAKE_LOW, MAIN_PANEL, "TAKE");
		else
			textPut("statesheet", TAKE_HIGH, MAIN_PANEL, "TAKE");

		takeState = !takeState;
		break;

	default:	// Unhandled timer event
		timerStop(id);
		break;
	}
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////// Callbacks above - Methods below ///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// checkTake() is called to check if the TAKE button should be enabled. //
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void CARSD::checkTake(void)
{
	// Enable the take button if there is both a source and a destination enabled
	if (source_selected && dest_selected)
	{
		controlEnable(MAIN_PANEL, "TAKE");
		timerStart(TAKE_ENABLED_TIMER, 10000);		// Enable the timer for 10 seconds
		timerStart(TAKE_FLASHER, 1000);				// Enable the high/low state flash timer
		textPut("statesheet", TAKE_LOW, MAIN_PANEL, "TAKE");
		takeEnabled = true;
		takeState = false;
	}
}
