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


#define PNL_MONITOR_FOLLOW 1

#define TIMER_PACKAGE_PARK 1

#define PACKAGE_PARK_DELAY 200		//200ms delay between package park and RC

const int DATABASE_PACKAGER_SOURCE(0);
const int DATABASE_PACKAGER_DEST(7);


// this nasty little macro to make our class visible to the outside world
EXPORT_BNCS_SCRIPT( monitorFollow )

// constructor - equivalent to ApplCore STARTUP
monitorFollow::monitorFollow( bncs_client_callback * parent, const char * path ) : bncs_script_helper( parent, path )
{

	m_intPackageRouterDevice = 0;
	m_intPackageRouterIndexDest = 0;

	m_source = 0;
	m_bMonFollowReady = false;


	m_monFollowing=false;
	m_bPackagerInstance = false;
	m_sSourcePackageName = "";
	m_intSourcePackageID = 0;

	m_bWaitingForPARKrevertive = false;
	m_bEnableOnLoad = false;

	m_sTally = "";

	//These 3 params are used if we have been asked to follow a dest
	m_bDestFollow = false;
	m_intDestFollowIndex = 0;
	m_intDestFollowDevice = 0;

	// show a panel from file p1.bncs_ui and we'll know it as our panel 1
	panelShow( PNL_MONITOR_FOLLOW , "p1.bncs_ui" );
	controlDisable( PNL_MONITOR_FOLLOW , "button" );

	//init();
}

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

// all button pushes and notifications come here
void monitorFollow::buttonCallback( buttonNotify *b )
{
	//Toggle our state
	m_monFollowing = !m_monFollowing;

	if( m_monFollowing )
	{
		textPut( "statesheet", "mon_following", PNL_MONITOR_FOLLOW , "button" );
	}
	else 
	{
		textPut( "statesheet", "mon_notfollowing", PNL_MONITOR_FOLLOW , "button" );
	}

	setFollow();
}

// all revertives come here
int monitorFollow::revertiveCallback( revertiveNotify * r )
{
	r->dump("monitorFollow::revertiveCallback()");
	bool bSetTally = false;
	//Check if the revertive is from the packager
	if(r->device() == m_intPackageRouterDevice)
	{
		if(r->index() == m_intPackageRouterIndexDest)
		{
			bncs_string sIndex = bncs_stringlist(r->sInfo(), ',').getNamedParam("index");
		
			if(sIndex.upper() == "PARK")
			{
				m_bPackageParked = true;
				m_intSourcePackageID = 0;
				m_sSourcePackageName = "";
				bSetTally = true;
				if(m_bWaitingForPARKrevertive == true)
				{
					timerStop(TIMER_PACKAGE_PARK);
					setFollow();
				}
			}
			else
			{
				m_bPackageParked = false;
				m_intSourcePackageID = sIndex.toInt();

				//Get the Source package name#
				bncs_string sPackagerName = "";
				routerName(m_intPackageRouterDevice, DATABASE_PACKAGER_SOURCE, m_intSourcePackageID, sPackagerName);
				if(sPackagerName != "")
				{
					m_sSourcePackageName = sPackagerName;
				}
				bSetTally = true;
			}
		}
	}
	//Check if its from our direct route
	else if(r->device() == m_direct.device)
	{
		if(r->index() == m_direct.index_dest)
		{
			m_direct.index_source = r->info();
			m_direct.source_name = r->sInfo();
		}

	}
	else if(r->device() == m_tieline.device)
	{
		if(r->index() == m_tieline.index_dest)
		{
			m_tieline.index_source = r->info();
			m_tieline.source_name = r->sInfo();
		}
	}
	else
	{
		map<int,tieline>::iterator it = m_mTielines.find(r->device());

		//Have we found the device in our tieline list
		if(it != m_mTielines.end())
		{
			tieline* t = &it->second;
			if(r->index() == t->index_dest)
			{
				t->index_source = r->info();
				t->source_name = r->sInfo();
			}
		}

	}

	if(m_bDestFollow)
	{
		//Only do this once each time we are set
		m_bDestFollow = false;
		if(r->device() == m_intDestFollowDevice && r->index() == m_intDestFollowIndex)
		{
			//If we are a packager instance treat it like this
			if(m_bPackagerInstance)
			{
				bncs_string sIndex = bncs_stringlist(r->sInfo(), ',').getNamedParam("index");
				if(sIndex == "PARK")
				{
					//???? How are we supposed to follow a parked dest
				}
				else
				{
					m_source = sIndex.toInt();
					setFollow();
				}

			}
			//Else if it is a routed instance treat it like this
			else
			{
				m_source = r->info();
				setFollow();
			}
		}
	}
	tally();

/*
	if ( r->device() == m_device)
	{
		bncs_string s;
		s = r->sInfo();
		s.replace( "|", " " );
		textPut( "text", s, PNL_MONITOR_FOLLOW , "tally" );
	}
	else if ( r->device() == m_intPackageRouterDevice )
	{
		if ( r->index() == m_intPackageMonitorDestination)
		{
			bncs_string strIndex ,s1,s2,destPackageMonTally;
			strIndex = r->sInfo();
			strIndex.split(',',s1,s2);
			s1.split('=',s2,destPackageMonTally);
//			debug("monitorFollow::revertiveCallback destPackageMonTally=%1", destPackageMonTally );
			m_blnPackageParked = destPackageMonTally=="0"?true:false;
//			debug("monitorFollow::revertiveCallback m_blnPackageParked=%1", m_blnPackageParked );
		}
	}
*/
	return 0;
}

// all database name changes come back here
void monitorFollow::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 monitorFollow::parentCallback( parentNotify *p )
{
	if( p->command() == "instance" )
	{
		m_instance = p->value();
		init();
	}
	else if( p->command() == "source" )
	{
		m_source = p->value().toInt();
		
		setFollow();
	}
	else if( p->command() == "dest" )
	{
		//For a Dest Index find the source that is routed to it
		if( m_monFollowing && m_bMonFollowReady)
			setDestFollow(p->value().toInt());
	}
	else if(p->command() == "EnableOnLoad")
	{
		if(p->value().lower() == "true")
		{
			m_bEnableOnLoad = true;
			m_monFollowing = true;
			textPut( "statesheet", "mon_following", PNL_MONITOR_FOLLOW , "button" );
		}
	}
	else if( p->command() == "_events" )
	{
	}
	else if( p->command() == "_commands" )
	{
		bncs_stringlist sl;
		sl << "source=<source index>";
		sl << "dest=<dest_index,source_index>";
		return sl.toString( '\n' );
	}
	
	else if( p->command() == "return" )
	{
		bncs_stringlist sl;

		sl << bncs_string("EnableOnLoad=%1").arg(bncs_string(m_bEnableOnLoad==true?"true":"false"));

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

	return "";
}

// timer events come here
void monitorFollow::timerCallback( int id )
{
	if ( id == TIMER_PACKAGE_PARK )
	{
		m_bWaitingForPARKrevertive = false;
		setFollow();
	}
}


void monitorFollow::init()
{
	follow = NULL; //Set our function pointer to null

	m_bMonFollowReady = false;
	controlDisable( PNL_MONITOR_FOLLOW , "button" );

	bool bDestPackagerLoaded = false;//If we have loaded the packager type successfully
	bool bFollowTypeLoaded = false;	//If we have loaded the Follow type successfully
	bool bTypeDirectRouting = false;//If we are loading a DIRECT routing type, we need to do some extra work at the end

	bool bLoadingSucessful = true; // If any part of the load encounters a problem set this to false

	//set variables
	bncs_string opsPosition = getWorkstationSetting( "ops_position" );

	bncs_config c(bncs_string("ops_positions.%1").arg(opsPosition));

	c.childAttr("name");

	while (c.isChildValid())
	{
		bncs_string id = c.childAttr("id");
// 		if(id == "name")
// 		{
// 
// 		}
// 		else 
		if(id.startsWith("mon_follow_"))
		{
			bncs_string instance = c.childAttr("instance");
			
			bncs_string method = c.childAttr("method");
			if(method == "PACKAGE")
			{
				//Don't need to load anything else with this method!
				//We already know where we are package routing too
				
				//If this is that instance we are looking for then say if we have loaded successfully
				if(m_instance == instance)
				{
					follow = &monitorFollow::followPackage;
					destFollow = &monitorFollow::destFollowPackage;
					bFollowTypeLoaded = true;
					m_bPackagerInstance = true;
				}
			}
			else if(method == "DIRECT")
			{	
				//In the case of direct routing we need to get the SDI dest from the Packager
				bncs_string sSDIDestType = c.childAttr("dest_index");
				int intSdiSlaveDest = c.childAttr("dest_slave").toInt();
				debug("intSdiSlaveDest  = %1", intSdiSlaveDest   );
				int intRouterDevice = 0;
				getDev(instance,&intRouterDevice);

				if(sSDIDestType.length() == 0 || intRouterDevice <= 0)
				{
					bLoadingSucessful = false;
				}
				else if(m_instance == instance)
				{
					bTypeDirectRouting = true;
					m_direct.routeType = sSDIDestType;
					m_direct.device = intRouterDevice;
					m_intDestFollowDevice = m_direct.device;
					m_direct.index_slaveDest = intSdiSlaveDest;
				}

			}
			else if(method == "PACKAGE_TIELINE")
			{
				//In this case we will need to get enough info to make a route on a non package router on to the package router and then a package route

				int intRouterIndexDest = c.childAttr("dest_index").toInt();
				int intPackageRouterIndexSource =  c.childAttr("source_package").toInt();
				
				int intRouterDevice = 0;
				getDev(m_instance,&intRouterDevice);
				
				if(intRouterDevice > 0 && intRouterIndexDest > 0 && intPackageRouterIndexSource > 0)
				{

					//These are for the tally get as much info as we can about the tielines
					tieline t;
					t.device = intRouterDevice;
					t.index_dest = intRouterIndexDest;
					t.id_source_package = intPackageRouterIndexSource;

					//If this is that instance we are looking for then say if we have loaded successfully 
					if(m_instance == instance)
					{
						m_tieline = t;
						
						bFollowTypeLoaded = true;
						follow = &monitorFollow::followPackageTieline;
						destFollow = &monitorFollow::destFollowRouted;
						m_intDestFollowDevice = t.device;
					}
					else
					{
						m_mTielines[t.device] = t;
					}
				}
			}
		}
// 		else if(id == "mon_dest_tb")
// 		{
// 
// 		}
		else if(id == "mon_dest_packager")
		{
			bncs_string instance = c.childAttr("instance");
			m_intPackageRouterIndexDest = c.childAttr("dest_index").toInt();

			getDev(instance,&m_intPackageRouterDevice);

			if(m_intPackageRouterDevice > 0 && m_intPackageRouterIndexDest > 0)
				bDestPackagerLoaded = true;
			else
				bLoadingSucessful = false;

			if(m_bPackagerInstance)
				m_intDestFollowDevice = m_intPackageRouterDevice;

		}
// 		else if(id == "enable_port_probe")
// 		{
// 
// 		}

		c.nextChild();
	}


	//If our type is direct routing then we should be here
	//as for direct routing we need to know the mon_dest_packager info
	if(bTypeDirectRouting)
	{
		bncs_string sDBPackager = "";
		routerName(m_intPackageRouterDevice, DATABASE_PACKAGER_DEST, m_intPackageRouterIndexDest, sDBPackager);

		bncs_stringlist sl(sDBPackager,',');
		bncs_stringlist::iterator it;
		
		//Look through all the items in the returned list and see if any match what we are looking for
		m_direct.index_dest = sl.getNamedParam(m_direct.routeType).toInt();

		if(m_direct.index_dest > 0 )
		{
			follow = &monitorFollow::followDirect;
			destFollow = &monitorFollow::destFollowRouted;
			bFollowTypeLoaded = true;
		}

	}

	debug("Loading State:%1 FollowTypeLoaded:%2 DestPackagerLoaded:%3",(bLoadingSucessful==true?"true":"false"),(bFollowTypeLoaded==true?"true":"false"),(bDestPackagerLoaded==true?"true":"false"));

	//Check if these 3 states are true if they are it means that we have successfully loaded our mon follow info
	if(bLoadingSucessful == true && bFollowTypeLoaded == true && bDestPackagerLoaded == true)
	{
		//Clear registrations
		routerUnregister(0);
		infoUnregister(0);

		///For each tieline register for its revertive
		map<int,tieline>::iterator it;
		for(it = m_mTielines.begin(); it != m_mTielines.end(); ++it)
		{
			tieline t = it->second;

			routerRegister(t.device, t.index_dest, t.index_dest,true);
			routerPoll(t.device, t.index_dest,  t.index_dest);
		}
		

		//Register for the Direct routing if we have any
		if(m_direct.device > 0)
		{
			routerRegister(m_direct.device, m_direct.index_dest, m_direct.index_dest, true);
			routerPoll(m_direct.device, m_direct.index_dest, m_direct.index_dest);
		}

		//Register so we can figure out what to display on the tally

		//Register with our package dest
		infoRegister(m_intPackageRouterDevice, m_intPackageRouterIndexDest, m_intPackageRouterIndexDest, true);
		infoPoll(m_intPackageRouterDevice, m_intPackageRouterIndexDest, m_intPackageRouterIndexDest);


		m_bMonFollowReady = true;
		controlEnable( PNL_MONITOR_FOLLOW , "button" );
	}
	hostNotify(bncs_string("monFollowReady=%1").arg(m_bMonFollowReady==true?"true":"false"));
}

void monitorFollow::followPackage()
{
	//Make a route on the packager from our tieline to the mon packager dest
	if(m_intSourcePackageID != m_source)
		infoWrite(m_intPackageRouterDevice, bncs_string("index=%1").arg(m_source), m_intPackageRouterIndexDest);
}

void monitorFollow::followPackageTieline()
{

	//First make a route on our router to the package tieline source
	if(m_source != m_tieline.index_source)
		routerCrosspoint(m_tieline.device,m_source,m_tieline.index_dest);

	//Then make a route on the packager from our tieline to the mon packager dest
	if(m_intSourcePackageID != m_tieline.id_source_package)
		infoWrite(m_intPackageRouterDevice, bncs_string("index=%1").arg(m_tieline.id_source_package), m_intPackageRouterIndexDest);

}

void monitorFollow::followDirect()
{
	//We are making a router under the nose of the packager so set it to park first

	// park the package monitor destination
	if ( !m_bPackageParked && m_bWaitingForPARKrevertive != true)	
	{
		//debug("monitorFollow::processFollowRequest() Mon Follow - Packager (Park Packager first)");
		infoWrite(m_intPackageRouterDevice, "index=PARK", m_intPackageRouterIndexDest);
		timerStart(TIMER_PACKAGE_PARK, PACKAGE_PARK_DELAY);
		m_bWaitingForPARKrevertive = true;
	}
	// just make the route
	else 
	{
		m_bWaitingForPARKrevertive = false;
		//debug("monitorFollow::processFollowRequest() Mon Follow - Packager (Packager ALready Pardked)");
		if(m_source != m_direct.index_source)
		{
			routerCrosspoint( m_direct.device, m_source, m_direct.index_dest );
			routerCrosspoint( m_direct.device, m_source, m_direct.index_slaveDest );
		}

	}

}

void monitorFollow::tally()
{

	//We need quite a bit of information for the tally


	//First is the route on the packager a package route? or an SDI only route?

	//If this is false the route is a package route
	if(m_bPackageParked == false)
	{

		//Now we need to check if the source package is a tieline to another router
		bool bFoundTieline = false;
		if(m_tieline.id_source_package == m_intSourcePackageID)
		{
			setTally(m_tieline.source_name);
			hostNotify(bncs_string("sourceIndex=%1").arg(m_tieline.index_source));
			bFoundTieline = true;
		}
		else
		{

			map<int,tieline>::iterator it;
			
			for(it = m_mTielines.begin(); it != m_mTielines.end(); ++it)
			{
				if(it->second.id_source_package == m_intSourcePackageID)
				{
					setTally(it->second.source_name);
					hostNotify(bncs_string("sourceIndex=%1").arg(it->first));
					bFoundTieline = true;
					break;
				}
			}
		}

		//There is no tieline for this package, therefore we will use the source package name
		if(bFoundTieline == false)
		{
			setTally(m_sSourcePackageName);
			hostNotify(bncs_string("sourceIndex=%1").arg(m_intSourcePackageID));
		}
		
	}

	//If it is true the route is an SDI level route on the packager
	else
	{
		//If the package is not routed don't bother looking any further than the Source Index Name
		setTally(m_direct.source_name);
		hostNotify(bncs_string("sourceIndex=%1").arg(m_direct.index_source));
	}


}

void monitorFollow::setTally(bncs_string s)
{
	if(m_sTally != s)
	{
		textPut("text", s.replace('|',  ' '), PNL_MONITOR_FOLLOW, "tally");
		m_sTally = s;
	}

}

void monitorFollow::setDestFollow(int iDestIndex)
{
	m_bDestFollow = true;
	//if(m_intDestFollowIndex != iDestIndex)
	//{
		m_intDestFollowIndex = iDestIndex;
		(this->*destFollow)();	
	//}
}


void monitorFollow::destFollowRouted()
{
	routerRegister(m_intDestFollowDevice,m_intDestFollowIndex, m_intDestFollowIndex,true);
	routerPoll(m_intDestFollowDevice,m_intDestFollowIndex, m_intDestFollowIndex);
}

void monitorFollow::destFollowPackage()
{
	infoRegister(m_intDestFollowDevice,m_intDestFollowIndex, m_intDestFollowIndex,true);
	infoPoll(m_intDestFollowDevice,m_intDestFollowIndex, m_intDestFollowIndex);
}

void monitorFollow::setFollow()
{
	if( m_monFollowing && m_bMonFollowReady && m_source > 0)
		(this->*follow)();
}

