/*************************************/
/* Written by David Yates            */
/* Copyright Atos 2007 */
/*************************************/
#if !defined(AFX_CCCLIENT_H__5BC0221D_E447_4DF8_9175_8C8AB169AAAE__INCLUDED_)
#define AFX_CCCLIENT_H__5BC0221D_E447_4DF8_9175_8C8AB169AAAE__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <windows.h>
#include <string>
#include <queue>
using namespace std;

#ifdef CCCLIENT_AS_DLL
	#ifdef DO_EXPORT_CCCLIENT
		#define EXPORT_CCCLIENT __declspec(dllexport) 
	#else
		#define EXPORT_CCCLIENT __declspec(dllimport) 
	#endif
#else
	#define EXPORT_CCCLIENT
#endif

#pragma warning( disable : 4786) 

#include "ccCheck.h"
#include "cc.h"

/** \class IccClientCallback
\brief Callback interface class for ccClient class

For clarity because this class must be inherited, the function names are prepended with ccc (for Colledia Control Client)
\sa ccClient
*/
class EXPORT_CCCLIENT IccClientCallback
{
protected:
	/** Connected event - usually generated by ccClient connect function */
	virtual void cccConnected( void ){};
	/** Disconnected event - possibly asynchronous if our host disappears, but also called on ccClient::disconnect()*/
	virtual void cccDisconnected( void ){};

	/** Infodriver revertive
	\param device Device number
	\param index Slot number
	\param value Slot contents
	*/
	virtual void cccInfoRevertive( int device, int index, const string & value ){};
	/** GRD revertive
	\param device Device number
	\param index destination number
	\param info Integer value of source
	\param name Source name (derived from local database)
	*/
	virtual void cccRtrRevertive( int device, int index, int info ){};
	/** GPI revertive
	\param device Device number
	\param index destination number
	\param state on/off state
	*/ 
	virtual void cccGpiRevertive( int device, int index, bool state ){};
	/** Database change notification
	\param device Device number
	\param index index number
	\param database database number (0-9)
	\param name New name
	*/
	virtual void cccDatabaseChange( int device, int index, int database ){};
	/** Device mode
	Sent regularly when a device broadcasts its state to the network
	\param workstation Workstation number hosting the device
	\param device Device number
	\param mode The current mode of the device
	*/
	virtual void cccDeviceMode( int workstation, int device, enum cc::redundancyState state ){}; 

	/** Direct driver message
	Message from a specific device instance to a specific client regardless of TXRX state of the driver
	\param workstation The workstation number issuing this command
	\param device The device number eminating the status message
	\param message The message!
	\param reference A number assigned by the client when issuing requests to keep track of requests. See: ccClient::deviceMessage()
	\note See registerForNetMessage - certain CSI configurations require explicit actions in this class to work
	*/
	virtual void cccDeviceMessage( int workstation, int device, const string & message, int reference ){};

	/** Poll Error
	Message from the bncs_shim telling us that a single poll message we'd sent out failed to solicit a response
	\param device The device number at fault
	\param index The index at fault
	*/
	virtual void cccPollError( int device, int slot ){};

	/** Serial Number Error
	Message from CSI telling us that a packet serial number error has been detected
	\param workstation The workstation we missed the message(s) from
	\param device The device number at fault
	*/
	virtual void cccSerialNumberError( int workstation, int device ){};

	/** Message from CSI telling us of a configuration change - clients should do whatever they see fit with this
	CSI itself won't update the configuration - if you want it saved then write something (a singleton) to do it
	*/
	virtual void cccConfigChangeMessage( const string & msg ){};

	friend class ccClient;
	friend class bncs_dll_client_shim;
};

class EXPORT_CCCLIENT ccClient : public ccCheck
{
public:

	ccClient( IccClientCallback * c = 0, const string & debugStringHeader = "ccClient" );
	virtual ~ccClient();
	long version( void );

	// router commands
	void routerCrosspoint( int device, int index, int info, const string & mask = ",---", bool now = false );
	void routerPoll( int device, int start, int end, bool now = false );
	void routerQuery( int device, int start, int end, bool now = false );
	void routerLock( int device, int start, int end, bool lock, bool now = false );
	void routerFile( int device );
	// infodriver commands
	void infoWrite( int device, int index, const string & value, bool now = false  );
	void infoPoll( int device, int start, int end, bool now = false );
	void infoQuery( int device, int start, int end, bool now = false );
	void infoLock( int device, int start, int end, bool lock, bool now = false );
//	void infoFile( int device, const string & value, const string & filename, bool append );	// NEEDS INVESTIGATION!!
//	void infoDirectory( );																		// NEEDS INVESTIGATION!!
	// gpi commands
	void gpiSwitch( int device, int index, bool state, bool now = false  );
	void gpiFile( int device );
	void gpiLock( int device, int start, int end, bool lock, bool now = false );
	void gpiPoll( int device, int start, int end, bool now = false );
	void gpiQuery( int device, int start, int end, bool now = false );

	// redundancy commands
	void deviceMessage( int workstation, int device, const string & message, int reference );

	// profile
	void deviceProfile( int driver, int & version, int & sources, int & dests );

	// database commands
	bool databaseName( int device, int index, int database, string & out );
	int databaseIndex( int device, int database, const string & name );
	bool databaseModify(int device, int index, int database, const string & name, bool tallyDump = false, bool includeHandle=true);

	// registrations
	void revertiveRegister( int device, int start, int end, bool overwrite = true, int iType = cc::IP );
	void revertiveDeregister( int device, int start=0 , int end =0 );
	void revertiveDeregisterAll( void );
	
	// cache commands
	bool cacheValue( int device, int index, string & out );
	int cacheIndex( int device, int start, int end, const string & value );
	void cacheEnable( bool );
	bool cacheTest( int device, int start, int end );
	void cacheFlush( int device );
	bool isCacheEnabled( void );

	// class connection commands
	bool connect( enum cc::ipcType ipc = cc::wm, long ipcOptions=0 );
	bool isConnected( void );
	bool disconnect( void );

	void writeEnable( bool );
	bool isWriteEnabled( void );

	void setCloseHostOnExit( bool close = false );
	bool counters( unsigned int & tx, unsigned int & rx );
	void resetCounters( void );

	void setCallback( IccClientCallback * callback );
	bool getClientRegistrationExtent( int device, int & min, int & max );
	void sendBufferedCommands( void );
	
	void registerForNetMessage( void );
	void deregisterForNetMessage( void );

	void registerForConfigChangeMessage( void );
	void deregisterForConfigChangeMessage( void );
	void sendConfigChangeMessage( const string & msg );

	void flushDatabaseCache( int device = 0 );
private:
	IccClientCallback *m_callback;
	HWND m_hWndMessage;
	HWND m_hCSI;
	enum cc::ipcType m_ipc;
	long m_ipcOptions;
	int m_workstation;
	bool m_writeEnabled;
	bool m_closeHostOnExit;
	unsigned int m_txCounter;
	unsigned int m_rxCounter;
	unsigned int m_lastTxCounter;
	unsigned int m_lastRxCounter;
	unsigned int m_maxDatabaseIndex;
	unsigned int m_maxDatabases;

#pragma warning (disable: 4251 )
	queue< string > revertiveQueue;
#pragma warning (default: 4251 )
	static LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
	//LRESULT BNCSSendMessage( UINT uMsg, WPARAM wParam, LPARAM lParam, int iReturnDataLen, int iDataType );
	LRESULT BNCSSendMessage( UINT uMsg, WPARAM wParam, LPARAM lParam, int dataLen, int expectedReturnDataLen, int iDataType );
	void deviceQuery( char type, int device, int start, int end, bool now );
	void devicePoll( char type, int device, int start, int end, bool now );
	void deviceLock( char type, int device, int start, int end, bool lock, bool now = false );	
	void deviceFile( char type, int device );
};

#endif // !defined(AFX_CCCLIENT_H__5BC0221D_E447_4DF8_9175_8C8AB169AAAE__INCLUDED_)
