/*
PROGRAM NAME	wn_matrix.DLL

USED BY			BBC Academy Wood Norton BNCS installation.

VERSION			1.0
DATE			3rd March 2015
PROGRAMMER		Andy Woodhouse

Overview
========



*/


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

#define MAIN_PANEL	1
#define MAIN_PANEL_NAME "wn_dest.bncs_ui"

#define TIMER_SETUP	1

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


// ++++++++++++++++++++++++++++++++++++++++++++ //
// Constructor - equivalent to ApplCore STARTUP //
// ++++++++++++++++++++++++++++++++++++++++++++ //
wn_dest::wn_dest(bncs_client_callback *parent, const char *path) : bncs_script_helper(parent, path)
{
	// Initialise our variables
	m_destination_id = "0";		// Default destination id value for this scripted control component
	m_package_name_id = "0";	// The BNCS driver ID for the package names
	m_package_xpts_id = "0";	// The BNCS driver ID for the package crosspoints
	m_video_matrix_id = "0";	// The BNCS driver ID for the video level
	m_audio_matrix_id = "0";	// The BNCS driver ID for the audio level
	m_index = "0";				// This is a value sent by the host. A value of -1 clears any selection on the button of this component
	m_buttonstyle = "";			// The style sheet for the button appearance.
	m_buttonhigh = "";			// The statesheet for the button when "active"
	m_buttonlow = "";			// The statesheet for the button when "deselected".
	m_videotallystate = "";		// The statesheet for the video tally display.
	m_audiotallystate = "";		// The statesheet for the audio tally display.
	action_on_release = true;	// Set button action to "On Release" 

	video_revertive_reg = false;// Not registered for a video revertive
	audio_revertive_reg = false;// Not registered for an audio revertive
	button_active = false;		// Set button initial condition as deselected
	m_video_id = 0;				// The video destination
	m_audio_id = 0;				// The audio destination
	names_rtr = 0;				// The BNCS driver id for the package names
	xpts_rtr = 0;				// The BNCS driver id for the package crosspoints
	video_rtr = 0;				// The BNCS driver id for the video router
	audio_rtr = 0;				// The BNCS driver id for the audio router

	// Show our mini-panel.
	panelShow(MAIN_PANEL, MAIN_PANEL_NAME);

}

// destructor - equivalent to ApplCore CLOSEDOWN
wn_dest::~wn_dest()
{
}


// ++++++++++++++++++++++++++++++++++++++++++++++ //
// All button pushes and notifications come here. //
// ++++++++++++++++++++++++++++++++++++++++++++++ //
void wn_dest::buttonCallback(buttonNotify *b)
{
	// Be very defensive - just in case we forgot to cancel a notification from the background or tally displays
	if(b->panel() == MAIN_PANEL)
	{
		if (b->id() == "Button")
		{
			bncs_string myMessage = bncs_string("select=%1").arg(m_destination_id);

			// Need to decide if we action on press or release
			if ((b->value() == "pressed") && (action_on_release == false))
			{
				// Do the actions for button activated
				textPut("statesheet", m_buttonhigh, MAIN_PANEL, "Button");
				button_active = true;
				hostNotify(myMessage);
				return;
			}
			
			if ((b->value() == "released") && (action_on_release == true))
			{
				// Do the actions for button activated
				textPut("statesheet", m_buttonhigh, MAIN_PANEL, "Button");
				button_active = true;
				hostNotify(myMessage);
				return;
			}

		}
	}	// End of if(b->panel() == MAIN_PANEL)
}


// +++++++++++++++++++++++++ //
// All revertives come here. //
// +++++++++++++++++++++++++ //
int wn_dest::revertiveCallback( revertiveNotify * r )
{
	if (r->device() == audio_rtr)
	{
		// Only expect data from a single crosspoint.
		if (r->index() == m_audio_id)	// Very defensive programming
		{
			textPut("text", r->sInfo(), MAIN_PANEL, "Aud");
		}
		return 0;
	}

	if (r->device() == video_rtr)
	{
		// Only expect data from a single crosspoint.
		if (r->index() == m_video_id)	// Very defensive programming
		{
			textPut("text", r->sInfo(), MAIN_PANEL, "Vid");
		}
		return 0;
	}

	return 0;
}


// +++++++++++++++++++++++++++++++++++++++++ //
// All database name changes come back here. //
// +++++++++++++++++++++++++++++++++++++++++ //
void wn_dest::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_dest::parentCallback(parentNotify *p)
{
	if( p->command() == "return" )
	{
		if( p->value() == "all" )
		{	
			// Return the Persisting values for use by bncs_vis_ed
			bncs_stringlist sl;
			
			sl << bncs_string("destination_id=%1").arg(m_destination_id);
			sl << bncs_string("package_name_id=%1").arg(m_package_name_id);
			sl << bncs_string("package_xpts_id=%1").arg(m_package_xpts_id);
			sl << bncs_string("video_matrix_id=%1").arg(m_video_matrix_id);
			sl << bncs_string("audio_matrix_id=%1").arg(m_audio_matrix_id);
			sl << bncs_string("buttonaction=%1").arg(action_on_release ? "release" : "press");
			sl << bncs_string("buttonstyle=%1").arg(m_buttonstyle);
			sl << bncs_string("buttonhigh=%1").arg(m_buttonhigh);
			sl << bncs_string("buttonlow=%1").arg(m_buttonlow);
			sl << bncs_string("videotallystate=%1").arg(m_videotallystate);
			sl << bncs_string("audiotallystate=%1").arg(m_audiotallystate);

			return sl.toString( '\n' );
		}

		else if (p->value() == "destination_id")
		{	
			// Specific value being asked for by a textGet
			return(bncs_string("%1=%2").arg(p->value()).arg(m_destination_id));
		}

		else if (p->value() == "video_id")
		{
			// Host wants to know the video level id.
			return(bncs_string("%1=%2").arg(p->value()).arg(m_video_id));
		}

		else if (p->value() == "audio_id")
		{
			// Host wants to know the audio level id.
			return(bncs_string("%1=%2").arg(p->value()).arg(m_audio_id));
		}
	}	// end of 'if (p->command() == "return")'



	else if (p->command() == "index")
	{
		// Host is setting our "index" property. If the supplied value is -1, we must clear the button state if it is active.
		// If the index > 0 and the index is not our destination_id, we also clear the button.
		// If the index is our destination_id we set the button active and emit a code to indicate selection.

		if (p->value() == "-1")
		{
			// Clear the button selection if it is active
			if (button_active)
			{
				textPut("statesheet", m_buttonlow, MAIN_PANEL, "Button");
				button_active = false;
				return "";
			}
		}

		if (p->value() != m_destination_id)
		{
			// Clear the button selection if it is active
			if (button_active)
			{
				textPut("statesheet", m_buttonlow, MAIN_PANEL, "Button");
				button_active = false;
				return "";
			}
		}

		if (p->value() == m_destination_id)
		{
			bncs_string myMessage = bncs_string("select=%1").arg(m_destination_id);

			// Action as if our button has been pressed/released
			textPut("statesheet", m_buttonhigh, MAIN_PANEL, "Button");
			button_active = true;
			hostNotify(myMessage);
			return "";
		}

		return "";
	}

	// >>>>>>>>>>>>>>>>>>>>>>>>>> //
	// Change or set destination? //
	else if ((p->command() == "destination_id") && (p->value() != m_destination_id))
	{
		m_destination_id = p->value();

		// Use the new data to put a name on the button.
		if (names_rtr != 0)
		{
			bncs_string dest_name;
			routerName(names_rtr, 1, m_destination_id.toInt(), dest_name);		// Get the name from database 1
			textPut("text", dest_name, MAIN_PANEL, "Button");			// Put value on the button
		}

		// Action the instance-change here
		// extract new video and audio destinations, register for revertives (non-zero video or audio)
		m_video_id = 0;						// Clear existing video and audio destination data.
		m_audio_id = 0;

		if (video_revertive_reg)			// If video revertives were registered - cancel them
		{
			routerUnregister(video_rtr);
			video_revertive_reg = false;
		}

		if (audio_revertive_reg)			// If audio revertives were registered - cancel them
		{
			routerUnregister(audio_rtr);
			audio_revertive_reg = false;
		}

		textPut("text", "", MAIN_PANEL, "Vid");		// Clear the tally displays
		textPut("text", "", MAIN_PANEL, "Aud");

		// Call a function to extract video and audio data
		extract_av_data();

	}

	// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
	// Change or set package names driver id. //
	else if ((p->command() == "package_name_id") && (p->value() != m_package_name_id))
	{
		// Our BNCS package names driver id is being set/changed.
		m_package_name_id = p->value();

		names_rtr = m_package_name_id.toInt();

		if (m_destination_id.toInt() != 0)
		{
			bncs_string dest_name;
			routerName(names_rtr, 1, m_destination_id.toInt(), dest_name);		// Get the name from database 1
			textPut("text", dest_name, MAIN_PANEL, "Button");			// Put value on the button
		}
	}


	// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
	// Change or set package crosspoints driver id. //
	else if ((p->command() == "package_xpts_id") && (p->value() != m_package_xpts_id))
	{
		// Our BNCS package crosspoints driver id is being set/changed.
		m_package_xpts_id = p->value();

		// Action the instance-change here. This will need us to unregister any existing revertives
		m_video_id = 0;						// Clear existing video and audio destination data.
		m_audio_id = 0;

		if (video_revertive_reg)			// If video revertives were registered - cancel them
		{
			routerUnregister(video_rtr);
			video_revertive_reg = false;
		}

		if (audio_revertive_reg)			// If audio revertives were registered - cancel them
		{
			routerUnregister(audio_rtr);
			audio_revertive_reg = false;
		}

		xpts_rtr = m_package_xpts_id.toInt();	// Convert the driver identifier to an integer.

		extract_av_data();
	}

	// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
	else if ((p->command() == "video_matrix_id") && (p->value() != m_video_matrix_id))
	{
		// Our BNCS video level driver id is being set/changed.
		m_video_matrix_id = p->value();

		// Action the instance-change here. This will need us to unregister any existing revertives
		if (video_rtr != 0)
		{
			if (video_revertive_reg)			// If video revertives were registered - cancel them
			{
				routerUnregister(video_rtr);
				video_revertive_reg = false;
			}
			video_rtr = 0;
		}

		video_rtr = m_video_matrix_id.toInt();

		extract_av_data();
		
	}	// End of 'else if ((p->command() == "video_matrix_id") ...'


	// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
	else if ((p->command() == "audio_matrix_id") && (p->value() != m_audio_matrix_id))
	{
		// Our BNCS audio level driver id is being set/changed.
		m_audio_matrix_id = p->value();

		// Action the instance-change here. This will need us to unregister any existing revertives
		if (audio_rtr != 0)
		{
			if (audio_revertive_reg)			// If video revertives were registered - cancel them
			{
				routerUnregister(audio_rtr);
				audio_revertive_reg = false;
			}
			audio_rtr = 0;
		}

		audio_rtr = m_audio_matrix_id.toInt();

		extract_av_data();

	}	// End of 'else if ((p->command() == "audio_matrix_id") ....'


	// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
	else if (p->command() == "buttonaction")	// Set button actions to be on "press" or "release"
	{
		if ((p->value() == "press") || (p->value() = "release"))
		{
			// Only action if valid action values sent.
			if ((p->value() == "press") && (action_on_release == true))
			{
				action_on_release = false;
				return "";
			}
			if ((p->value() == "release") && (action_on_release == false))
			{
				action_on_release = true;
				return "";
			}
		}
	}	// End of else if (p->command() == "buttonaction")	


	// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
	else if ((p->command() == "buttonstyle") && (p->value() != m_buttonstyle))
	{
		// Our parent wants to set the basic button style
		m_buttonstyle = p->value();
		textPut("stylesheet", m_buttonstyle, MAIN_PANEL, "Button");
	}


	// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
	else if ((p->command() == "buttonhigh") && (p->value() != m_buttonhigh))
	{
		// Our parent wants to set the button active style
		m_buttonhigh = p->value();
		if (button_active == true) textPut("statesheet", m_buttonhigh, MAIN_PANEL, "Button");
	}


	// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
	else if ((p->command() == "buttonlow") && (p->value() != m_buttonlow))
	{
		// Our parent wants to set the button inactive style
		m_buttonlow = p->value();
		if (button_active == false) textPut("statesheet", m_buttonlow, MAIN_PANEL, "Button");
	}


	// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
	else if ((p->command() == "videotallystate") && (p->value() != m_videotallystate))
	{
		// Our parent wants to set the video tally display statesheet
		m_videotallystate = p->value();
		textPut("statesheet", m_videotallystate, MAIN_PANEL, "Vid");
	}


	// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //
	else if ((p->command() == "audiotallystate") && (p->value() != m_audiotallystate))
	{
		// Our parent wants to set the audio tally display statesheet
		m_audiotallystate = p->value();
		textPut("statesheet", m_audiotallystate, MAIN_PANEL, "Aud");
	}


	// ***** CONNECTIONS EVENTS HELPER LIST *****
	else if( p->command() == "_events" )
	{	
		// Helper-list of everything in this component generated by hostNotify's.
		//
		// For this component, the only returned value is the destination index when the button is actioned.
		bncs_stringlist sl;

		sl << "select=*";
		
		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
		//
		// We only support setting the index - a value of -1 clearing the button selection if it is active.
		bncs_stringlist sl;
		
		sl << "index=[value]";
		
		return sl.toString( '\n' );
	}

	return "";
}

// timer events come here
void wn_dest::timerCallback( int id )
{
	switch( id )
	{
	case TIMER_SETUP:
		timerStop(id);
		break;

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


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

// function extract_av_data() examines the information provided by the various router and destination id parameters
// and if these are sufficiently valid, it extracts the audio and video crosspoints into the global variables
// m_video_id and m_audio_id, and re-registers for revertives and polls the destinations.
void wn_dest::extract_av_data(void)
{
	// 
	int dest_index = 0;
	bool outcome = false;

	dest_index = m_destination_id.toInt();		// Convert the destination_id value from string to integer

	if ((dest_index != 0) && (xpts_rtr != 0) && (video_rtr != 0) && (audio_rtr != 0))
	{
		// Have sufficient data to extract the destination crosspoint data
		bncs_string xpt_data, p1, p2;

		routerName(xpts_rtr, 1, dest_index, xpt_data);		// Read the comma delimited string
		xpt_data.split(',', p1, p2);						// Split into two parts
		m_video_id = p1.firstInt();
		m_audio_id = p2.firstInt();
		outcome = true;
	}

	// Register for revertives and poll
	if (m_audio_id != 0)	// See if the audio crosspoint is non-zero. If so register for revertives and poll
	{
		routerRegister(audio_rtr, m_audio_id, m_audio_id);
		audio_revertive_reg = true;
		routerPoll(audio_rtr, m_audio_id, m_audio_id);
	}

	if (m_video_id != 0)	// See if the video crosspoint is non-zero. If so register for revertives and poll
	{
		routerRegister(video_rtr, m_video_id, m_video_id);
		video_revertive_reg = true;
		routerPoll(video_rtr, m_video_id, m_video_id);
	}

	return;
}

