#include <windows.h>
#include <stdio.h>
#include <bncs_string.h>
#include <bncs_config.h>
#include "B4_CAR.h"

#define MAIN_PANEL	1
#define MAIN_PANEL_NAME "B4_CAR.bncs_ui"

#define LEVEL_ENABLED	"/images/SmallTick.png"
#define LEVEL_DISABLED	"/images/SmallCross.png"

#define TAKE_TIMER 1
#define TAKE_TIMER_PERIOD	1000		// 1 second period
#define INIT_TAKE_COUNT		10			// Run for 10 seconds
#define TAKE_HIGH	"WN_TakeHigh"		// Statesheet for state 1 of button
#define TAKE_LOW	"WN_TakeLow"		// Statesheet for state 2 of button

// Macro to make our class visible to the outside world
EXPORT_BNCS_SCRIPT( B4_CAR )

// ++++++++++++++++++++++++++++++++++++++++++++ //
// Constructor - equivalent to ApplCore STARTUP //
// ++++++++++++++++++++++++++++++++++++++++++++ //
B4_CAR::B4_CAR(bncs_client_callback *parent, const char *path) : bncs_script_helper(parent, path)
{
	// Initialise variables to "safe" values.
	package_names_id = 0;
	package_xpts_id = 0;
	video_id = 0;
	audio_id = 0;
	dest_btn = 0;
	source_pkg = 0;
	dest_pkg = 0;
	undo_video_source = 0;
	undo_audio_source = 0;
	undo_video_dest = 0;
	undo_audio_dest = 0;

	for (int i = 0; i < 5; i++) dest_vid[i] = 0;
	for (int i = 0; i < 5; i++) dest_aud[i] = 0;
	for (int i = 0; i < 5; i++) current_vid_src[i] = 0;
	for (int i = 0; i < 5; i++) current_aud_src[i] = 0;

	video_enabled = true;			// Start with video level switching enabled
	audio_enabled = true;			// Start with audio level switching enabled
	take_enabled = false;			// Initial condition for the TAKE button is disabled
	take_timeout_enabled = false;	// Initial condition for the take_timeout counter is disabled
	undo_enabled = false;			// Initial condition for the UNDO button is disabled


	// Extract the video and audio driver id's from instances.xml
	getDev("B4 Package Names", &package_names_id);
	getDev("B4 Package XPTS", &package_xpts_id);
	getDev("B4 SDI Router", &video_id);
	getDev("B4 Audio Router", &audio_id);
	
	// show a panel from file p1.bncs_ui and we'll know it as our panel PNL_MAIN
	panelShow(MAIN_PANEL, MAIN_PANEL_NAME);

	// Set the TAKE and UNDO buttons to disabled
	controlDisable(MAIN_PANEL, "Take");
	controlDisable(MAIN_PANEL, "Undo");

	// Get the video and audio IDs for the 4 destination buttons.
	for (int i = 1; i <= 4; i++)
	{
		textGet("video_id", MAIN_PANEL, bncs_string("Dest_%1").arg(i), destvideo_id);
		textGet("audio_id", MAIN_PANEL, bncs_string("Dest_%1").arg(i), destaudio_id);
		dest_vid[i] = destvideo_id.toInt();
		dest_aud[i] = destaudio_id.toInt();
	}

	// Register for revertives (needed for UNDO)
	routerRegister(video_id, dest_vid[1], dest_vid[1]);			// Initial single registration
	routerRegister(video_id, dest_vid[2], dest_vid[2], true);	// Add destination 2
	routerRegister(video_id, dest_vid[3], dest_vid[3], true);	// Add destination 3
	routerRegister(video_id, dest_vid[4], dest_vid[4], true);	// Add destination 4

	routerRegister(audio_id, dest_aud[1], dest_aud[1]);			// Initial single registration
	routerRegister(audio_id, dest_aud[2], dest_aud[2], true);	// Add destination 2
	routerRegister(audio_id, dest_aud[3], dest_aud[3], true);	// Add destination 3
	routerRegister(audio_id, dest_aud[4], dest_aud[4], true);	// Add destination 4

	// Poll for the initial state
	routerPoll(video_id, dest_vid[1], dest_vid[1]);
	routerPoll(video_id, dest_vid[2], dest_vid[2]);
	routerPoll(video_id, dest_vid[3], dest_vid[3]);
	routerPoll(video_id, dest_vid[4], dest_vid[4]);

	routerPoll(audio_id, dest_aud[1], dest_aud[1]);
	routerPoll(audio_id, dest_aud[2], dest_aud[2]);
	routerPoll(audio_id, dest_aud[3], dest_aud[3]);
	routerPoll(audio_id, dest_aud[4], dest_aud[4]);
	
}	// End of Constructor


// +++++++++++++++++++++++++++++++++++++++++++++ //
// Destructor - equivalent to ApplCore CLOSEDOWN //
// +++++++++++++++++++++++++++++++++++++++++++++ //
B4_CAR::~B4_CAR()
{
}


// ++++++++++++++++++++++++++++++++++++++++++++++ //
// All button pushes and notifications come here. //
// ++++++++++++++++++++++++++++++++++++++++++++++ //
void B4_CAR::buttonCallback( buttonNotify *b )
{
	if( b->panel() == MAIN_PANEL)
	{
		if (b->id() == "Sources")			// Check for new source selected
		{
			if (b->command() == ("index"))
			{
				if (b->value().toInt() > 0)
				{
					source_pkg = b->value().toInt();
					// Check for source and destination both selected. Enable TAKE button as appropriate
				}
				else
				{
					source_pkg = 0;				// No source selected
				}
				clear_undo();					// If undo is enabled - disable it
				check_take_enable();			// Should the TAKE button be active?
				return;
			}
		}

		if (b->id().startsWith("Dest_"))	// Check for a destination button.
		{
			// Is there an already active destination - if so clear it.
			if (dest_btn > 0) textPut("index=-1", MAIN_PANEL, bncs_string("Dest_%1").arg(dest_btn));
			
			dest_btn = b->id().firstInt();		// Record the new destination button
			clear_undo();					// If undo is enabled - disable it
			check_take_enable();
			return;
		}

		if (b->id() == "videolevel")		// Check for video level enable/disable
		{
			video_enabled = video_enabled ? false : true;		// Toggle the flag state
			textPut("pixmap", video_enabled ? LEVEL_ENABLED : LEVEL_DISABLED, MAIN_PANEL, "videolevel");
			clear_undo();					// If undo is enabled - disable it
			return;
		}

		if (b->id() == "audiolevel")		// Check for audio level enable/disable
		{
			audio_enabled = audio_enabled ? false : true;		// Toggle the flag state
			textPut("pixmap", audio_enabled ? LEVEL_ENABLED : LEVEL_DISABLED, MAIN_PANEL, "audiolevel");
			clear_undo();					// If undo is enabled - disable it
			return;
		}

		if (b->id() == "Clear")
		{
			clear_selections();				// Invoke method to action the cleardown operation.
			return;
		}

		if (b->id() == "Take")
		{
			do_Take();
			return;
		}

		if (b->id() == "Undo")
		{
			do_Undo();
			return;
		}
	}
}


// +++++++++++++++++++++++++ //
// All revertives come here. //
// +++++++++++++++++++++++++ //
int B4_CAR::revertiveCallback( revertiveNotify * r )
{
	if (r->device() == video_id)
	{
		// We are rgistered for the 4 destinations so that undo can do it's thing
		for (int id = 1; id <= 4; id++)
		{
			if (r->index() == dest_vid[id])
			{
				current_vid_src[id] = r->info();	// Note the source index
				break;								// Stop the scan as we have found a match
			}
		}

		return 0;
	}

	if (r->device() == audio_id)
	{
		for (int id = 1; id <= 4; id++)
		{
			if (r->index() == dest_aud[id])
			{
				current_aud_src[id] = r->info();	// Note the source index
				break;								// Stop the scan as we have found a match
			}
		}

		return 0;

	}
	return 0;
}	// End of revertive processing


// +++++++++++++++++++++++++++++++++++++++++ //
// All database name changes come back here. //
// +++++++++++++++++++++++++++++++++++++++++ //
void B4_CAR::databaseCallback( revertiveNotify * r )
{
}


// +++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// All parent notifications come here. When this script  //
// is just one component of another dialog then our host //
// might want to tell us things.                         //
// +++++++++++++++++++++++++++++++++++++++++++++++++++++ //
bncs_string B4_CAR::parentCallback( parentNotify *p )
{
	if( p->command() == "return" )
	{
		if( p->value() == "all" )
		{	// Persisting values for bncs_vis_ed
			bncs_stringlist sl = "";
			
			return sl.toString( '\n' );
		}

	}
	
	// ***** CONNECTIONS EVENTS HELPER LIST *****
	else if( p->command() == "_events" )
	{	// Helper-list of everything in this component generated by hostNotify's
		bncs_stringlist sl = "";
		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 = "";
		return sl.toString( '\n' );
	}

	return "";
}


// +++++++++++++++++++++++ //
// Timer events come here. //
// +++++++++++++++++++++++ //
void B4_CAR::timerCallback( int id )
{
	switch( id )
	{
	case TAKE_TIMER:
		take_count--;		// Decrement the take counter
		if ((take_count & 1) == 0)
			textPut("statesheet", TAKE_HIGH, MAIN_PANEL, "Take");	// Counter value is even
		else
			textPut("statesheet", TAKE_LOW, MAIN_PANEL, "Take");	// Counter value is odd

		if (take_count == 0)
		{
			timerStop(TAKE_TIMER);
			take_timeout_enabled = false;			// Note that the timer is not running
			controlDisable(MAIN_PANEL, "Take");		// Disable the take button
			take_enabled = false;					// Set flag to correct state
		}
		break;

	default:	// Unhandled timer event
		timerStop(id);
		break;
	}
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////// Callbacks above - Methods below ///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////


void B4_CAR::extract_xpts(int pkg_id, int database, int *viddest, int *auddest)
{
	// pkg is the page number to process, databases is 0 for sources, 1 for destinations
	// viddest and auddest are the addresses for the results.
	bncs_string xptlist, p1, p2;

	routerName(package_xpts_id, database, pkg_id, xptlist);	// Read comma delimited string
	xptlist.split(',', p1, p2);								// Divide into two parts
	if (p1 == "-") *viddest = 0;
	else *viddest = p1.toInt();
	if (p2 == "-") *auddest = 0;
	else *auddest = p2.toInt();
}


// +++++++++++++++++++++++++++++++++++++++++++++++++++ //
// function clear_selections() is called to action the //
// cleardown of all selected sources and destinations. //
// +++++++++++++++++++++++++++++++++++++++++++++++++++ //
void B4_CAR::clear_selections(void)
{
	// Is Take enabled - if so disable and stop timer
	if (take_enabled == true)
	{
		controlDisable(MAIN_PANEL, "Take");
		textPut("statesheet", TAKE_HIGH, MAIN_PANEL, "Take");
		take_enabled = false;
	}
	if (take_timeout_enabled)
	{
		timerStop(TAKE_TIMER);
		take_timeout_enabled = false;
	}

	// If UNDO is enabled - disable it
	if (undo_enabled == true)
	{
		controlDisable(MAIN_PANEL, "Undo");
		undo_enabled = false;
		undo_video_source = 0;
		undo_audio_source = 0;
		undo_video_dest = 0;
		undo_audio_dest = 0;
	}

	// If a source is selected - deselect it
	if (source_pkg > 0)
	{
		source_pkg = -1;
		textPut("select=-1", MAIN_PANEL, "Sources");
	}

	// If a destination is enabled - deselect it
	if (dest_btn > 0)
	{
		textPut("index=-1", MAIN_PANEL, bncs_string("Dest_%1").arg(dest_btn));
		dest_btn = 0;
	}

}	// End of clear_selections()


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// function check_take_enabled() is called to check if there  //
// are both a source and a destination selected. If a valid   //
// source and destination exist, the take button is enabled,  //
// and the take timeout counter is initiated. If valid source //
// and destination are not available, the function ensures    //
// that the take button is disabled, the counter stopped etc. //
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void B4_CAR::check_take_enable(void)
{
	if ((dest_btn != 0) && (source_pkg > 0))
	{
		// TAKE should be enabled, and it may already be enabled.
		if (take_enabled == false)
		{
			controlEnable(MAIN_PANEL, "Take");
			take_enabled = true;
		}

		if (take_timeout_enabled == false) take_timeout_enabled = true;		// Make sure we have take tiimeout system active
		
		textPut("statesheet", TAKE_HIGH, MAIN_PANEL, "Take");				// Make sure take button is correct initial state
		timerStart(TAKE_TIMER, TAKE_TIMER_PERIOD);							// Start the timer
		take_count = INIT_TAKE_COUNT;										// Set counter to 10
	}
	else
	{
		// TAKE should be disabled
		if (take_enabled == true)
		{
			controlDisable(MAIN_PANEL, "Take");
			take_enabled = false;
		}

		if (take_timeout_enabled == true)
		{
			timerStop(MAIN_PANEL);
			take_timeout_enabled = false;
		}
	}
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// function do_Take() is called to execute the commands
// needed to action a take, and set up the undo data.
// ++++++++++++++++++++++++++++++++++++++++++++++++++++
void B4_CAR::do_Take(void)
{
	int source_video = 0, source_audio = 0;

	// Only get to here when the take button is enabled, which requires
	// both a valid source selection and a valid destination selection.

	// Start process by disabling the TAKE button, and timeout counter.
	timerStop(TAKE_TIMER);										// Stop the timer
	take_timeout_enabled = false;
	controlDisable(MAIN_PANEL, "Take");							// Disable the Take button
	textPut("statesheet", TAKE_HIGH, MAIN_PANEL, "Take");		// Leave colours correct for next use
	take_enabled = false;

	// Prepare data for the UNDO button
	undo_video_source = current_vid_src[dest_btn];
	undo_audio_source = current_aud_src[dest_btn];
	undo_video_dest = dest_vid[dest_btn];
	undo_audio_dest = dest_aud[dest_btn];

	// Look up the source video and audio elements
	extract_xpts(source_pkg, 0, &source_video, &source_audio);

	// Make the crosspoints
	if ((video_enabled) && (source_video > 0) && (dest_vid[dest_btn] > 0))
		routerCrosspoint(video_id, source_video, dest_vid[dest_btn]);

	if ((audio_enabled) && (source_audio > 0) && (dest_aud[dest_btn] > 0))
		routerCrosspoint(audio_id, source_audio, dest_aud[dest_btn]);

	// Clear source and destination selections
	textPut("select=-1", MAIN_PANEL, "Sources");
	source_pkg = -1;
	textPut("index=-1", MAIN_PANEL, bncs_string("Dest_%1").arg(dest_btn));
	dest_btn = 0;

	// Enable the UNDO button
	controlEnable(MAIN_PANEL, "Undo");
	undo_enabled = true;
}


// +++++++++++++++++++++++++++++++++++++++++++
// function do_Undo() is called to execute the
// commands needed to do an UNDO operation
// +++++++++++++++++++++++++++++++++++++++++++
void B4_CAR::do_Undo(void)
{
	// Only get here is the UNDO button was enabled.
	undo_enabled = false;					// Clear the flag
	controlDisable(MAIN_PANEL, "Undo");		// Disable the UNDO button

	// Restore the video and audio levels - assuming non-zero contents and level is enabled
	if ((video_enabled) && (undo_video_source != 0) && (undo_video_dest != 0))
		routerCrosspoint(video_id, undo_video_source, undo_video_dest);
	if ((audio_enabled) && (undo_audio_source != 0) && (undo_audio_dest != 0))
		routerCrosspoint(audio_id, undo_audio_source, undo_audio_dest);
	// Clear working data stores
	undo_video_source = 0;
	undo_audio_source = 0;
	undo_video_dest = 0;
	undo_audio_dest = 0;
}

// function clear_undo(void) is called to switch off the undo facility
void B4_CAR::clear_undo(void)
{
	if (undo_enabled == true)
	{
		controlDisable(MAIN_PANEL, "Undo");
		undo_enabled = false;
		undo_video_source = 0;
		undo_audio_source = 0;
		undo_video_dest = 0;
		undo_audio_dest = 0;
	}
}
