/*
PROGRAM NAME	SCR_OS.dll

USED BY			BBC Academy Studio A/CTA installation

VERSION			1.0
DATE			10th January 2015
PROGRAMMER		Andy Woodhouse

TARGET SCREEN SIZE	1024 x 768
PANEL SIZE	1024 x 668

Overview
========
SCR_OS is a BNCS version 4.5 panel used in the Sound Control Room to select audio sources to the 10 outside source lines.
One day, someone will discover what happened to the cabling for circuits 5 to 10! In the meantime, control is available
for those sources, and they may be pluggable via the CTA jackfield.

The user can select the source and destination selections in either order. Once both a source and a destination are selected,
a TAKE button is activated. The take function remains active for up to 15 seconds after which teh take button is disabled,
but the source and destination remain selected.

The package destinations that feed the OS lines are defined in the cpp code by #define statements.

*/


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

#define MAIN_PANEL	1
#define UI_PANEL_NAME "SCR_OS.bncs_ui"
#define CTA_BNCS_PKG_NAME "CTA BNCS Packager"
#define CTA_AUDIO_ROUTER "CTA Audio Router"
#define DEST_DESELECT "WNSourceDeselected"
#define DEST_SELECT "WNSourceSelected"
#define TAKEHIGH	"WN_TakeHigh"
#define TAKELOW		"WN_TakeLow"
#define TAKE_TIMER	1
#define TAKE_TIMEOUT 15
#define OS1_PKG 191
#define OS10_PKG OS1_PKG + 9
#define OS1_AUDIO_DEST 12
#define OS10_AUDIO_DEST OS1_AUDIO_DEST + 9

// Macro to make our class visible to the outside world
EXPORT_BNCS_SCRIPT( SCR_OS )

// ++++++++++++++++++++++++++++++++++++++++++++ //
// Constructor - equivalent to ApplCore STARTUP //
// ++++++++++++++++++++++++++++++++++++++++++++ //
SCR_OS::SCR_OS( bncs_client_callback * parent, const char * path ) : bncs_script_helper( parent, path )
{
	// Set local variables to initial "safe" conditions
	source_pkg = 0;
	dest_pkg = 0;
	take_count = 0;
	active_dest = 0;

	source_selected = false;
	dest_selected = false;
	take_enabled = false;
	timer_counter_enabled = false;

	// Show user interface panel
	panelShow(MAIN_PANEL, UI_PANEL_NAME);

	// Disable the TAKE button
	controlDisable(MAIN_PANEL, "Take");

	// Select first group in source selector panel
	textPut("button.001=released", MAIN_PANEL, "GrpSelect");
	
	// Get the needed driver ID's from instances.xml
	getDev(CTA_BNCS_PKG_NAME, &cta_bncs_pkg_id);
	getDev(CTA_AUDIO_ROUTER, &cta_audio_id);

	// Register for revertives from the audio router, and poll for initial state
	routerRegister(cta_audio_id, OS1_AUDIO_DEST, OS10_AUDIO_DEST);
	routerPoll(cta_audio_id, OS1_AUDIO_DEST, OS10_AUDIO_DEST);
}

// +++++++++++++++++++++++++++++++++++++++++++++ //
// Destructor - equivalent to ApplCore CLOSEDOWN //
// +++++++++++++++++++++++++++++++++++++++++++++ //
SCR_OS::~SCR_OS()
{
	routerUnregister(cta_audio_id);
}

// all button pushes and notifications come here
void SCR_OS::buttonCallback( buttonNotify *b )
{
	if( b->panel() == MAIN_PANEL )
	{
		if (b->id() == "GrpSelect")
		{
			textPut("page", b->value(), MAIN_PANEL, "SourceSel");	// Transfer page number of new group to source selector
			source_pkg = 0;
			source_selected = 0;
			take_check();				// Manage the enable/disable of the take button and take timeout process
			return;
		}

		if (b->id() == "SourceSel")
		{
			if (b->value() >= 0)
			{
				source_pkg = b->value();	// Extract the source index, and assign to our variable
				source_selected = true;		// Set a flag
				take_check();				// Manage the enable/disable of the take button and take timeout process
			}
			else	// Usually get a negative index code when change the displayed page, so kill source selection 
			{
				source_pkg = 0;
				source_selected = false;
				take_check();
			}

			return;
		}

		if (b->id().startsWith("OS_"))		// Is this a destination select button? All destination button names start "OS_"
		{
			// We can extract the button number from the name as the button name is of the form "OS_9"
			if (dest_selected)
			{
				// If there is already an active button, need to clear the secetion state
				textPut("statesheet", DEST_DESELECT, MAIN_PANEL, bncs_string("OS_") + bncs_string(active_dest));
			}
			active_dest = b->id().firstInt();		// Extract the numeric index as our new destination.
			dest_pkg = OS1_PKG + active_dest - 1;	// Update the active destination package.
			textPut("statesheet", DEST_SELECT, MAIN_PANEL, bncs_string("OS_") + bncs_string(active_dest));
			dest_selected = true;					// Note that we have an active destination
			take_check();							// See if the TAKE button should be active

			return;
		}


		if (b->id() == "Take")
		{
			// Can only get to here if there is both a source and a destination selected.
			routerCrosspoint(cta_bncs_pkg_id, source_pkg, dest_pkg, "2");	// Switch the audio level only.
			textPut("index=0", MAIN_PANEL, "SourceSel");					// Clear selected source
			textPut("statesheet", DEST_DESELECT, MAIN_PANEL, bncs_string("OS_") + bncs_string(active_dest));
			source_pkg = 0;
			dest_pkg = 0;
			source_selected = false;
			dest_selected = false;
			take_check();

			return;
		}

		if (b->id() == "Clear")
		{
			clear_all();
			return;
		}

	}	// End if (b->id() == MAIN_PANEL) ....

}	// End of function buttonCallback(...


// ++++++++++++++++++++++++ //
// All revertives come here //
// ++++++++++++++++++++++++ //
int SCR_OS::revertiveCallback( revertiveNotify * r )
{
	if (r->device() == cta_audio_id)
	{
		// Update the name on the mimic UMD. The Mimic names are of the form UMD_1, so need to offset to match matrix values
		textPut("text", r->sInfo(), MAIN_PANEL, bncs_string("UMD_" + bncs_string(r->index() - OS1_AUDIO_DEST + 1)));
		return 0;
	}
	// Default response is to return 0;
	return 0;
}

// all database name changes come back here
void SCR_OS::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 SCR_OS::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 SCR_OS::timerCallback( int id )
{
	switch( id )
	{
	case TAKE_TIMER:
		take_count--;		// Decrement the timeout count
		if (take_count != 0)
		{
			if (take_count & 1)		// Get the even/odd state of the count
				textPut("statesheet", TAKEHIGH, MAIN_PANEL, "Take");
			else
				textPut("statesheet", TAKELOW, MAIN_PANEL, "Take");
		}
		else
		{
			// take_count has reached zero
			textPut("statesheet", TAKEHIGH, MAIN_PANEL, "Take");	// Define the colour state of the take button
			timerStop(TAKE_TIMER);							// Stop the timer
			controlDisable(MAIN_PANEL, "Take");				// Disable the take button
			take_enabled = false;							// Update enable status
		}

		break;

	default:	// Unhandled timer event
		timerStop(id);
		break;
	}
}


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

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// function take_check() checks the source and destination selected flags to set/clear the take button and the //
// take timeout counter to the relevant states.                                                                //
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void SCR_OS::take_check(void)
{
	// If both a source and destination are selected, then the TAKE function should be initiated.
	// Otherwise we should disable the take button and disable the timeout counter
	if (source_selected && dest_selected)
	{
		if (take_enabled)
		{
			// TAKE button is already enabled, so re-initialise the timeout counter
			textPut("statesheet", "WN_TakeHigh", MAIN_PANEL, "Take");	// Set the initial colour of the TAKE button.
			take_count = TAKE_TIMEOUT;									// Reset the counter.
			timerStart(TAKE_TIMER, 1000);								// Re-initialise the timer.
			timer_counter_enabled = true;
		}
		else
		{
			// Need to enable the take button and initiate the take counter
			textPut("statesheet", "WN_TakeHigh", MAIN_PANEL, "Take");	// Set the initial colour of the TAKE button.
			take_count = TAKE_TIMEOUT;									// Reset the counter.
			timerStart(TAKE_TIMER, 1000);								// Re-initialise the timer.
			controlEnable(MAIN_PANEL, "Take");							// Enable the take control.
			take_enabled = true;										// Keep our flags up to date
			timer_counter_enabled = true;
		}
	}
	else	// Do not have conditions needed for TAKE to be active
	{
		// Do not have both source and destination selected, so make sure take button is disabled
		if (take_enabled)		// Is TAKE already enabled?
		{
			// Clear down the TAKE button, stop the take counter
			timerStop(TAKE_TIMER);
			take_enabled = false;
			timer_counter_enabled = false;
			controlDisable(MAIN_PANEL, "Take");
		}
	}
}	// End of function take_check()


// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// function clear_all() is called in response to the user pushing the CLEAR button. The function clears down any //
// source and destination selections as well as disabling the take button and timeout counter.                   //
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void SCR_OS::clear_all(void)
{
	// Clear any selction highlights then call take_check() to do rest of the work
	if (source_selected) textPut("index=0", MAIN_PANEL, "SourceSel");	// Clear selected source
	source_pkg = 0;
	source_selected = false;
	if (dest_selected) textPut("statesheet", DEST_DESELECT, MAIN_PANEL, bncs_string("OS_") + bncs_string(active_dest));
	dest_pkg = 0;
	dest_selected = false;
	take_check();
}
