/*
PROGRAM NAME	StA_TX_Auto.dll

USED BY			BBC Academy Studio A/CTA installation


Version	Date		Programmer				Comments
======= ====		==========				========
1.0		07-12-2014	Andy Woodhouse			Initial Release
1.1		14-08-2016	Andy Woodhouse			Changed number of controlled relays to match new GPI hardware installation.

TARGET SCREEN SIZE	1024 x 768
PANEL SIZE			800 x 600	(Automatic panel)

Overview
========
StA_TX_Auto is a BNCS version 4.5 automatic panel that is used to control the switching of the GPI relays used in
studio rehearse/transmit state.

It monitors the allocation of the Studio A floor, Studio C Floor and Studio C Mini-News studio, controlling the light states
in Studio A when the Studio A floor is controlled by Gallery C. A similar automatic running in Studio C controls the relays 
that operate studio C floor indicators and News Studio Indicators.

The galleries can indicate three possible operational states via slots in the tally infodriver:

Value	State		Properties
=====	=====		==========
  0		Off			Comms System disabled, blue lights off, red lights off.
  1		Rehearse	Comms System enabled, blue lights on, red lights off.
  2		Transmit	Comms System enabled, blue lights off, red lights on.

There are three further slots in the "Tally Infodriver" that are used to drive indicators on this panel. Each slot indicates
which gallery is the controlling agency for the associated area. The control values are:

Value	Meaning
=====	=======
0		Area control is unallocated
1		Area is controlled by Gallery A
2		Area is controlled by Gallery C

The infodriver slots that use the above codes and the associated areas are:

Slot	Area
====	====
4		Studio A and B floor (also red and blue lights in studio galleries
5		Studio C floor
6		Studio C News Studio

Setting the area allocations is done by another panel.

*/


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

#define PNL_MAIN	1

#define TIMER_SETUP	1

// ============= //
// Local defines //
// ============= //

// Define the infodriver slots
#define STA_STATE_SLOT 1		// Mode of Gallery A
#define STC_STATE_SLOT 3		// Mode of Gallery C
#define STA_FLOOR_MODE 4		// Control mode for Studio A+B floor
#define STC_FLOOR_MODE 5		// Control mode for Studio C floor
#define STC_NEWS_MODE 6			// Control mode for Studio C Mini News Studio

// Define the value indications in the area control slots
#define STATE_OFF 0
#define STATE_REH 1
#define STATE_TX 2

// Define the control indications
#define CNTL_NONE 0
#define CNTL_STA 1
#define CNTL_STC 2

// Define the destinations to monitor for output to line
#define STA_VID_LINE 97		// SDI destination for Studio Line output
#define STA_AUD_LINE 62		// Audio destination for Studio Line Output

// A series of defines for GPI output offsets from the first used contact
#define GPI_STA_REH_SSR 0
#define GPI_STA_REH_DRAKE 1
#define GPI_STA_REH_REDLT_REC 2
#define GPI_STA_REH_SNDDSK 3
#define GPI_STA_REH_SP1 4
#define GPI_STA_TX_SSR 5
#define GPI_STA_TX_DRAKE 6
#define GPI_STA_TX_REDLT_REC 7
#define GPI_STA_TX_SNDDSK 8
#define GPI_STA_TX_SP1 9


// ********************************************************** //
// Use a macro to make our class visible to the outside world //
// ********************************************************** //
EXPORT_BNCS_SCRIPT( StA_TX_Auto )

// ++++++++++++++++++++++++++++++++++++++++++++++++ //
// The constructor - equivalent to ApplCore STARTUP //
// ++++++++++++++++++++++++++++++++++++++++++++++++ //
StA_TX_Auto::StA_TX_Auto( bncs_client_callback * parent, const char * path ) : bncs_script_helper( parent, path )
{
	// Initialise local variables to ensure known states at startup
	currentState_A = -1;			// Holds the current Gallery A Off/Reh/TX status
	newState_A = 0;
	currentState_C = -1;			// Holds the current Gallery C Off/Reh/TX status
	newState_C = 0;
	tallyInfodriver = 0;			// Holds the BNCS driver ID for the tally infodriver
	sdiRouterdriver = 0;			// Holds the BNCS driver ID for the sdi router
	audioRouterdriver = 0;			// Holds the BNCS driver ID for the audio router
	GPIdriver = 0;					// Holds the driver ID of GPI driver. Read from wn_accademy, StA_TX_Auto segment.
	GPIcontact_1 = 0;				// Holds the offset to the first GPI output contact.
	StA_Floor = 0;					// Holds the current control mode for Studio A floor
	StC_Floor = 0;					// Holds the current control mode for Studio C floor
	StC_News = 0;					// Holds the current control mode for News Studio C

	tallyReg = false;				// Flags that show if the drivers have been registered for revertives
	SDIReg = false;
	audioReg = false;

	offmode = "WN_Studio_OFF";
	rehmode = "WN_Studio_REH";
	txmode = "WN_Studio_TX";

	// Show a panel defined by file p1.bncs_ui We'll refer to it as PNL_MAIN
	panelShow( PNL_MAIN, "p1.bncs_ui" );

	// Start to collect information about the various drivers and slots
	getDev("CTA Tally Infodriver", &tallyInfodriver);				// Tally Infodriver holds many items
	infoRegister(tallyInfodriver, STA_STATE_SLOT, STC_NEWS_MODE);	// Register for range of slots
	tallyReg = true;												// Keep a note that the infodriver is registered

	getDev("CTA SDI Router", &sdiRouterdriver);
	routerRegister(sdiRouterdriver, STA_VID_LINE, STA_VID_LINE);
	SDIReg = true;

	getDev("CTA Audio Router", &audioRouterdriver);
	routerRegister(audioRouterdriver, STA_AUD_LINE, STA_AUD_LINE);
	SDIReg = true;

	// Read the First GPI contact details.
	getDev("Studio A Blue Lights", &GPIdriver, &GPIcontact_1);

	// Poll the drivers to estabish initial conditions
	routerPoll(sdiRouterdriver, STA_VID_LINE, STA_VID_LINE);		// Check current line output settings for video
	routerPoll(audioRouterdriver, STA_AUD_LINE, STA_AUD_LINE);		// and audio
	infoPoll(tallyInfodriver, STA_STATE_SLOT, STC_NEWS_MODE);		// Get the Mode and controlling agent slots

}

// destructor - equivalent to ApplCore CLOSEDOWN
StA_TX_Auto::~StA_TX_Auto()
{
	if (tallyReg) infoUnregister(tallyInfodriver);
	if (SDIReg) routerUnregister(sdiRouterdriver);
	if (audioReg) routerUnregister(audioRouterdriver);
}

// all button pushes and notifications come here
void StA_TX_Auto::buttonCallback( buttonNotify *b )
{
	/*
	if( b->panel() == 1 )
	{
		
	}
	*/
}


// ++++++++++++++++++++++++ //
// All Revertives come here //
// ++++++++++++++++++++++++ //
int StA_TX_Auto::revertiveCallback( revertiveNotify * r )
{
	// We expect revrtives from a single video router desintaion, a single audio router destination, and 6 infodriver slots.

	// Check for revertives from the video router. Only interested in the line output destination.
	if ((r->device() == sdiRouterdriver) && (r->index() == STA_VID_LINE))
	{
		textPut("text", r->info(), PNL_MAIN, "vls_id");
		textPut("text", r->sInfo(), PNL_MAIN, "vls_name");
		return 0;
	}

	// Check for revertives from the audio router. Only interested in the line output destination.
	if ((r->device() == audioRouterdriver) && (r->index() == STA_AUD_LINE))
	{
		textPut("text", r->info(), PNL_MAIN, "als_id");
		textPut("text", r->sInfo(), PNL_MAIN, "als_name");
		return 0;
	}

	// Is this a revertive from our range of slots in the Infodriver?
	if ((r->device() == tallyInfodriver) && (r->index() >= STA_STATE_SLOT) && (r->index() <= STC_NEWS_MODE))
	{
		switch (r->index())
		{
			case STA_STATE_SLOT:		// Gallery A Off/Rehearse/TX status value
				newState_A = r->sInfo().toInt();	// Get the new state
				if (currentState_A != newState_A)	// Look for a change in status
				{
					// The required actions depend on the new state
					switch (newState_A)
					{
						case STATE_OFF:
							clearTX_REHgpi();		// Call method that clears down all gpi contacts
							break;

						case STATE_REH:
							setREHgpi();			// Set the GPI contacts to Rehearse mode
							break;

						case STATE_TX:
							setTXgpi();				// Set the GPI contacts needed for TX status
							break;
					}

					if (StA_Floor == CNTL_STA) update_A_Floor_Tally(newState_A);
					if (StC_Floor == CNTL_STA) update_C_Floor_Tally(newState_A);
					if (StC_News == CNTL_STA) update_C_News_Tally(newState_A);

					currentState_A = newState_A;	// Note the new state for future revertives
				}
				break;

			case 2:						// Would be Gallery B if ever re-developed for new gallery
				break;

			case STC_STATE_SLOT:		// We must tally some data on the automatic panel for info.
				newState_C = r->sInfo().toInt();	// Read the new value of studio C state
				if (currentState_C != newState_C)	// Check for  change
				{
					switch (StC_Floor)				// Switch based on control mode
					{
						case CNTL_NONE:
							update_C_Floor_Tally(CNTL_NONE);
							break;
						case CNTL_STA:
							update_C_Floor_Tally(currentState_A);
							break;
						case CNTL_STC:
							update_C_Floor_Tally(newState_C);
							break;
					}

					switch (StC_News)				// Switch based on control mode
					{
					case CNTL_NONE:
						update_C_News_Tally(CNTL_NONE);
						break;
					case CNTL_STA:
						update_C_News_Tally(currentState_A);
						break;
					case CNTL_STC:
						update_C_News_Tally(newState_C);
						break;
					}

					// If StA floor is controlled by StC, we need to switch the CTA realys that manage the red and blue lights
					// and update our panel tally
					if (StA_Floor == CNTL_STC)
					{
						StA_Floor_StC_Control(newState_C);
						update_A_Floor_Tally(newState_C);
					}
				}
				currentState_C = newState_C;		// Note the updated value
				break;

			case STA_FLOOR_MODE:		// Manage change of control to Reh/Tx lights for Studio A and Studio B floors
				StA_Floor = r->sInfo().toInt();
				switch (StA_Floor)
				{
				case (CNTL_NONE) :
					Update_A_Floor_Relays(CNTL_NONE);			// Make sure Solid State Relay controls are correct for new control
					update_A_Floor_Tally(CNTL_NONE);			// Update tally display on automatic panel
					break;
				case (CNTL_STA) :
					Update_A_Floor_Relays(currentState_A);		// Update relays
					update_A_Floor_Tally(currentState_A);		// Update tally display
					break;
				case (CNTL_STC) :
					Update_A_Floor_Relays(currentState_C);		// Update relays
					update_A_Floor_Tally(currentState_C);		// Update tally display
					break;
				}
				break;	// break for case STA_FLOOR_MODE

			case STC_FLOOR_MODE:		// Who controls the Reh/Tx lights for Studio C floor
				StC_Floor = r->sInfo().toInt();
				switch (StC_Floor)				// Switch based on control mode
				{
				case CNTL_NONE:
					update_C_Floor_Tally(CNTL_NONE);
					break;
				case CNTL_STA:
					update_C_Floor_Tally(currentState_A);
					break;
				case CNTL_STC:
					update_C_Floor_Tally(currentState_C);
					break;
				}
				break;	// break for case STC_FLOOR_MODE

			case STC_NEWS_MODE:			// Who controls the Reh/Tx lights for Mini News Studio
				StC_News = r->sInfo().toInt();
				switch (StC_News)				// Switch based on control mode
				{
				case CNTL_NONE:
					update_C_News_Tally(CNTL_NONE);
					break;
				case CNTL_STA:
					update_C_News_Tally(currentState_A);
					break;
				case CNTL_STC:
					update_C_News_Tally(currentState_C);
					break;
				}
			break;	// Break fo switch(r->index()...

		}	// End of switch(r->index()......

		return 0;
	}	// End of if (r->device() == ......

	// A catch all
	return 0;
}

// all database name changes come back here
void StA_TX_Auto::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 StA_TX_Auto::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 StA_TX_Auto::timerCallback( int id )
{
	switch( id )
	{
	case TIMER_SETUP:
		timerStop(id);
		break;

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


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

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// Update_A_Floor_Relays is used when control of Studio A floor changes //
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void StA_TX_Auto::Update_A_Floor_Relays(int A_TX_State)
{
	// The required actions depend on new state
	switch (A_TX_State)
	{
	case STATE_OFF:
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_SSR, false);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_SSR, false);	// Solid state relay, red lights
		break;

	case STATE_REH:
		gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_REH_SSR, false);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_SSR, false);	// Solid state relay, red lights
		break;

	case STATE_TX:
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_SSR, false);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_TX_SSR, false);	// Solid state relay, red lights
		break;
	}
}


// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// Sets the GPI relay states needed for Transmission mode active //
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void StA_TX_Auto::setTXgpi(void)
{
	// Updated this code when FXTION GPTI crate installed.
	// There are 10 GPI output contacts, 5 associated with REH, 5 with TX
	// The Solid State Relay (SSR) controls should only be set/reset if Studio A+B floor is controlled by Gallery A

	if (StA_Floor == CNTL_STA)
	{
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_SSR, false);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_TX_SSR, false);	// Solid state relay, red lights
	}

	// These contacts are always switched, even if Gallery A does not own the studio floor
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_REH_DRAKE, false);		// See if we can now switch this off with newest DRAKE configs
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_REDLT_REC, false);	// Digital Rapids Red light recorder
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_SNDDSK, false);		// In case ever have sound desk that needs this
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_SP1, false);			// Spare REH 1
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_TX_DRAKE, false);			// DRAKE TX - inhib LS TB etc
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_TX_REDLT_REC, false);		// Digital Rapids Red light recorder
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_TX_SNDDSK, false);		// Studio A sound desk TX
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_TX_SP1, false);			// Spare TX 1
}


// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
// Sets the GPI relay states needed for Rehearse mode active //
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
void StA_TX_Auto::setREHgpi(void)
{
	// Updated this code when FXTION GPTI crate installed.
	// There are 10 GPI output contacts, 5 associated with REH, 5 with TX
	// The Solid State Relay (SSR) controls should only be set/reset if Studio A+B floor is controlled by Gallery A

	if (StA_Floor == CNTL_STA)
	{
		gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_REH_SSR, false);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_SSR, false);	// Solid state relay, red lights
	}

	// These contacts are always switched
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_REH_DRAKE, false);		// Signal to DRAKE comms crate Rehearse input 
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_REH_REDLT_REC, false);	// Digital Rapids Red light recorder
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_REH_SNDDSK, false);		// In case ever have sound desk that needs this
	gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_REH_SP1, false);			// Spare REH 1
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_DRAKE, false);		// Signal to DRAKE comms crate TX input
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_REDLT_REC, false);	// Digital Rapids Red light recorder
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_SNDDSK, false);		// Studio A sound desk TX
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_SP1, false);			// Spare TX 1
}


// ++++++++++++++++++++++++++++++++++++++++++++++++ //
// Clear all relays to off to set the OFF condition //
// ++++++++++++++++++++++++++++++++++++++++++++++++ //
void StA_TX_Auto::clearTX_REHgpi(void)
{
	// Updated this code when FXTION GPTI crate installed.
	// There are 10 GPI output contacts, 5 associated with REH, 5 with TX
	// The Solid State Relay (SSR) controls should only be set/reset if Studio A+B floor is controlled by Gallery A

	if (StA_Floor == CNTL_STA)
	{
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_SSR, false);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_SSR, false);	// Solid state relay, red lights
	}

	// These contacts are always switched
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_DRAKE, false);		// DRAKE Reh comms signal
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_REDLT_REC, false);	// Digital Rapids Red light recorder
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_SNDDSK, false);		// In case ever have sound desk that needs this
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_SP1, false);			// Spare REH 1
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_DRAKE, false);		// DRAKE TX comms signal
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_REDLT_REC, false);	// Digital Rapids Red light recorder
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_SNDDSK, false);		// Studio A sound desk TX
	gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_SP1, false);			// Spare TX 1
}


//
void StA_TX_Auto::StA_Floor_StC_Control(int sw)
{
	switch (sw)
	{
	case STATE_OFF:
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_SSR, false);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_SSR, false);	// Solid state relay, red lights
		break;
	case STATE_REH:
		gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_REH_SSR, false);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_TX_SSR, false);	// Solid state relay, red lights
		break;
	case STATE_TX:
		gpiSwitch(GPIdriver, false, GPIcontact_1 + GPI_STA_REH_SSR, false);	// Solid state relay, blue lights
		gpiSwitch(GPIdriver, true, GPIcontact_1 + GPI_STA_TX_SSR, false);	// Solid state relay, red lights
		break;
	}
}


void StA_TX_Auto::update_C_Floor_Tally(int sw)
{
	switch (sw)
	{
	case STATE_OFF:
		textPut("statesheet", offmode, PNL_MAIN, "StC_Floor");
		break;
	case STATE_REH:
		textPut("statesheet", rehmode, PNL_MAIN, "StC_Floor");
		break;
	case STATE_TX:
		textPut("statesheet", txmode, PNL_MAIN, "StC_Floor");
		break;
	}
}

void StA_TX_Auto::update_C_News_Tally(int sw)
{
	switch (sw)
	{
	case STATE_OFF:
		textPut("statesheet", offmode, PNL_MAIN, "StC_News");
		break;
	case STATE_REH:
		textPut("statesheet", rehmode, PNL_MAIN, "StC_News");
		break;
	case STATE_TX:
		textPut("statesheet", txmode, PNL_MAIN, "StC_News");
		break;
	}
}

void StA_TX_Auto::update_A_Floor_Tally(int sw)
{
	switch (sw)
	{
	case STATE_OFF:
		textPut("statesheet", offmode, PNL_MAIN, "StA_Floor");
		break;
	case STATE_REH:
		textPut("statesheet", rehmode, PNL_MAIN, "StA_Floor");
		break;
	case STATE_TX:
		textPut("statesheet", txmode, PNL_MAIN, "StA_Floor");
		break;
	}
}


