/**	\author    coidgen 0.7.4
	\version    (0.0 build 1)
	\date      
	\brief     
*/



#include "example_client.h"




namespace coid {

struct COID_TABLE_example_0_0 {
	opcd (COID_TABLE_example_0_0::*disconnect)();
	opcd (COID_TABLE_example_0_0::*read1__0)( FOO_STRUCT & list );
	opcd (COID_TABLE_example_0_0::*read2__1)( FOO_STRUCT * & list );
	opcd (COID_TABLE_example_0_0::*send__2)( int & ref, const charstr & command, binstreambuf & reply ) const;
	opcd (COID_TABLE_example_0_0::*remap_fnc__3)( REMAP_EXAMPLE_clientside & remapped_arg );
	opcd (COID_TABLE_example_0_0::*get_ptr__4)( FOO_STRUCT ** f );
};

}	/// namespace coid



#define ___COID_CONNECTWITHIN_TRY		try {
#define ___COID_CONNECTWITHIN_CATCH	} catch( opcd ___coid_err ) {return ___coid_err;}\
										catch(...) { return ersFE_EXCEPTION "client method exception"; }
#define ___COID_CONNECT_TRY		try {
#define ___COID_CONNECT_CATCH		} catch( opcd ___coid_err ) {delete _coid_bstream; return ___coid_err;}\
									catch( ThreadException & ) { delete _coid_bstream; throw; }\
									catch(...) { delete _coid_bstream; return ersFE_EXCEPTION "client method exception"; }





const version COID_version_example_0_0( "", 0, 0, 1 );

static COID_NICK g_coid_nick;

static COID_TABLE_example_0_0 * coid_interprocess_vtbl = NULL;
static COID_TABLE_example_0_0 * coid_remote_vtbl = NULL;
static comm_mutex coid_table_mx;

struct TABLE_REMOVER_example_0_0 {
	~TABLE_REMOVER_example_0_0() {delete coid_interprocess_vtbl; delete coid_remote_vtbl;}
};
static TABLE_REMOVER_example_0_0	example_0_0_table_trash;




namespace coid {

enum {	/// command ids, format is: COID_classname_fncname_fncid
	COID_example_read1_0 = 0,
	COID_example_read2_1 = 1,
	COID_example_send_2 = 2,
	COID_example_remap_fnc_3 = 3,
	COID_example_get_ptr_4 = 4
};




static uint COID_method_flags_example[5] = {
	0x70100,
	0x70100,
	0x70101,
	0x70000,
	0x10000
};




/***************************************************************************
example_localclient
***************************************************************************/

class example_localclient : public COID_CLIENT
{
public:
	example_localclient( netstream * b, binstream * b2 ) : COID_CLIENT(b, b2) {}
	void coid_throw();


	opcd read1( FOO_STRUCT & list );
	opcd read2( FOO_STRUCT & list );
	opcd send( int & ref, const charstr & command, binstreambuf & reply ) const;
	opcd remap_fnc( REMAP_EXAMPLE_clientside & remapped_arg );
// 	opcd get_ptr( FOO_STRUCT ** f );
};



/// fake function:
void example_localclient::coid_throw() {throw ersFE_EXCEPTION;}


typedef void (example_localclient::*coid_fnc)();

#define COID_CLIENT_CREATE_HOOK(M,i,t,tc)\
	if( COID_method_flags_example[i] & coid_stream_flags ) ((coid_fnc *) table)[t] = (coid_fnc) (tc) &example_localclient::M;\
	else ((coid_fnc *) table)[t] = (coid_fnc) &example_localclient::coid_throw;

#define COID_CLIENT_CREATE_DISCONNECT_HOOK(t)\
	((coid_fnc *) table)[t] = (coid_fnc) &example_localclient::disconnect;





void inline coid_init_table( uint coid_stream_flags, COID_TABLE_example_0_0 *& table )
{
	table = new COID_TABLE_example_0_0;

	ulong coid_table_index = 0;
	COID_CLIENT_CREATE_DISCONNECT_HOOK( coid_table_index++ )
	COID_CLIENT_CREATE_HOOK( read1, 0, coid_table_index++, opcd (example_localclient::*)( FOO_STRUCT &  ) )
	((coid_fnc *) table)[coid_table_index++] = (coid_fnc) &example_localclient::coid_throw;  // 'read2' is not called through this table
	COID_CLIENT_CREATE_HOOK( send, 2, coid_table_index++, opcd (example_localclient::*)( int & , const charstr & , binstreambuf &  ) const )
	((coid_fnc *) table)[coid_table_index++] = (coid_fnc) &example_localclient::coid_throw;  // 'remap_fnc' is not called through this table
	((coid_fnc *) table)[coid_table_index++] = (coid_fnc) &example_localclient::coid_throw;  // 'get_ptr' is direct only
}






opcd example_localclient::read1( FOO_STRUCT & list )
{
	*_coid_bstream << (ulong) COID_example_read1_0;
	*_coid_bstream << _coid_inst_id;

	_coid_bstream->flush();
	_coid_err_code = _coid_bstream->get_error();
	if( _coid_err_code ) {
		_coid_bstream->acknowledge();
		return _coid_err_code;
	}
	*_coid_bstream >> list;

	_coid_bstream->acknowledge();

	return _coid_err_code;
}



opcd example_localclient::read2( FOO_STRUCT & list )
{
	*_coid_bstream << (ulong) COID_example_read2_1;
	*_coid_bstream << _coid_inst_id;

	_coid_bstream->flush();
	_coid_err_code = _coid_bstream->get_error();
	if( _coid_err_code ) {
		_coid_bstream->acknowledge();
		return _coid_err_code;
	}
	*_coid_bstream >> list;

	_coid_bstream->acknowledge();

	return _coid_err_code;
}



opcd example_localclient::send( int & ref, const charstr & command, binstreambuf & reply ) const
{
	*_coid_bstream << (ulong) COID_example_send_2;
	*_coid_bstream << _coid_inst_id;
	*_coid_bstream << ref;
	*_coid_bstream << command;

	_coid_bstream->flush();
	_coid_err_code = _coid_bstream->get_error();
	if( _coid_err_code ) {
		_coid_bstream->acknowledge();
		return _coid_err_code;
	}
	*_coid_bstream >> ref;
	*_coid_bstream >> reply;

	_coid_bstream->acknowledge();

	return _coid_err_code;
}



opcd example_localclient::remap_fnc( REMAP_EXAMPLE_clientside & remapped_arg )
{
	*_coid_bstream << (ulong) COID_example_remap_fnc_3;
	*_coid_bstream << _coid_inst_id;

	_coid_bstream->flush();
	_coid_err_code = _coid_bstream->get_error();
	if( _coid_err_code ) {
		_coid_bstream->acknowledge();
		return _coid_err_code;
	}
	*_coid_bstream >> remapped_arg;

	_coid_bstream->acknowledge();

	return _coid_err_code;
}

// method 'get_ptr' is direct only



/***************************************************************************
example_client
***************************************************************************/

example_client::example_client() : _me(NULL), _vtbl(NULL), _conn_data(0)
{
	netSubsystem::instance();
}
example_client::~example_client()
{
	disconnect();
}
example_client::example_client( const example_client & c ) {throw ersFE_EXCEPTION;}
example_client & example_client::operator = ( const example_client & c ) {throw ersFE_EXCEPTION; return *this;}

void example_client::destroy_me()
{
	if( get_connection_type() != ConnectFlags::fACCESS_MODE_DIRECT ) delete _me;
	_me = NULL;
}

opcd example_client::disconnect()
{
	if( ! is_connected() )
		return 0;
	DASSERT( _mx_reg.is_valid() );
	comm_mutex & lowlevelmx = _mx_reg._mxc->_mx;
	bool last = false;
	opcd coid_err;
	try {
		extendedGUARD_lock( _mx_reg );
		DASSERT( _me );
		coid_err = ((get_connection_type() == ConnectFlags::fACCESS_MODE_DIRECT ? (COID_TABLE_example_0_0 *) this : _me)->*(_vtbl->disconnect)) ();
		destroy_me();
		if( _mx_reg.counter_fast() == 1 ) {
			_mx_reg.disable_fast();
			lowlevelmx.unlock();
			__coid__mxg.eject();
			last = true;
		}
	}
	catch( opcd e ) {
		destroy_me(); coid_err = e;
	}

	if( last ) {
#ifdef _DEBUG
		const int max = 0xFFFF;
#else
		const int max = 10;
#endif
		int i=0;
		for( ; i<max; i++ ) {
			if( lowlevelmx.try_lock() ) {lowlevelmx.unlock(); break;}
			sysMilliSecondSleep( 10 );
		}
		if( i == max ) throw ersFE_EXCEPTION;
	}

	_mx_reg.destroy_all();
	_vtbl = NULL;
	set_connection_type( 0 );
	return coid_err;
}

opcd example_client::get_last_error() const
{
	if( ! is_connected() ) return 0;
	if( get_connection_type() == ConnectFlags::fACCESS_MODE_REMOTE ) return ((COID_CLIENT *) _me)->get_last_error();
	else if( get_connection_type() == ConnectFlags::fACCESS_MODE_INTERPROCESS ) return ((COID_CLIENT *) _me)->get_last_error();
	return 0;
}

void example_client::set_last_error( opcd e )
{
	if( ! is_connected() ) return;
	if( get_connection_type() == ConnectFlags::fACCESS_MODE_REMOTE ) ((COID_CLIENT *) _me)->set_last_error( e );
	else if( get_connection_type() == ConnectFlags::fACCESS_MODE_INTERPROCESS ) ((COID_CLIENT *) _me)->set_last_error( e );
}

inline bool example_client::setup_members( uint coid_stream_flags, netstream * bstream, binstream * bstream2, comm_mutex_reg * mx_reg )
{
	disconnect();
	set_connection_type( coid_stream_flags );

	if( coid_stream_flags == ConnectFlags::fACCESS_MODE_DIRECT ) {	/// direct
		if( ! bstream2 ) bstream2 = bstream;
		*bstream2 >> (Tptr<void> &) _vtbl;
		*bstream2 >> (Tptr<void> &) _me;
		comm_mutex_reg::refmutex * _rmx;
		*bstream2 >> (Tptr<void> &) _rmx;
		_mx_reg.init( *_rmx );
		bstream2->acknowledge();
		delete bstream;
	}
	else if( coid_stream_flags == ConnectFlags::fACCESS_MODE_INTERPROCESS ) {	/// interprocess
		if( ! bstream2 ) bstream2 = bstream;
		if( coid_interprocess_vtbl ) _vtbl = coid_interprocess_vtbl;
		else {
			MXGUARD( coid_table_mx );
			if( ! coid_interprocess_vtbl )
				coid_init_table( coid_stream_flags, coid_interprocess_vtbl );
			_vtbl = coid_interprocess_vtbl;
		}
		_me = (COID_TABLE_example_0_0 *) new example_localclient( bstream, bstream2 );
		bstream2->acknowledge();
		_mx_reg.init( mx_reg );
		_mx_reg._p1 = bstream;
	}
	else if( coid_stream_flags == ConnectFlags::fACCESS_MODE_REMOTE ) {	/// remote
		if( ! bstream2 ) bstream2 = bstream;
		if( coid_remote_vtbl ) _vtbl = coid_remote_vtbl;
		else {
			MXGUARD( coid_table_mx );
			if( ! coid_remote_vtbl )
				coid_init_table( coid_stream_flags, coid_remote_vtbl );
			_vtbl = coid_remote_vtbl;
		}
		_me = (COID_TABLE_example_0_0 *) new example_localclient( bstream, bstream2 );
		bstream2->acknowledge();
		_mx_reg.init( mx_reg );
		_mx_reg._p1 = bstream;
	}
	else throw ersFE_EXCEPTION;

	return true;
}

void example_client::setup_stream( binstream & b, uint coid_flags, uint coid_obj_id )
{
	if( coid_flags & ConnectFlags::fWITHIN_CHANNEL ) {
		coid_flags &= ~ConnectFlags::fWITHIN_CHANNEL;
		b << StdProtocolMethod::CONNECT;
	}
	uchar conchar = StdProtocolMethod::CONNECT_CHAR;
	b << conchar << version(COID_VERSION);
	b << g_coid_nick << coid_flags << COID_version_example_0_0 << "example";
	b << (uint) 5 << (Tptr<void>) COID_method_flags_example << coid_obj_id << sysGetPid() << (Tptr<void>) this;
}








opcd example_client::connect(  const charstr & name, const password & pwd, const char * coid_address, uint coid_flags )
{
	_addr.reset();
	extendedGUARD_reg __coid__mxg( _mx_reg, false );
	if( _mx_reg.is_valid() ) {
		if( get_connection_type() == ConnectFlags::fACCESS_MODE_DIRECT ) {	/// direct
			return ersFE_ALREADY_CONNECTED;
		}
		else if( get_connection_type() == ConnectFlags::fACCESS_MODE_INTERPROCESS ) {	/// interprocess
			__coid__mxg.lock();
			if( ((COID_CLIENT *)_me)->is_stream_open() )
				return ersFE_ALREADY_CONNECTED;
		}
		else if( get_connection_type() == ConnectFlags::fACCESS_MODE_REMOTE ) {	/// remote
			__coid__mxg.lock();
			if( ((COID_CLIENT *)_me)->is_stream_open() )
				return ersFE_ALREADY_CONNECTED;
		}
	}

	netstream * _coid_bstream = COID_CLIENT::create_stream( "example", coid_address, _addr, (coid_flags & ConnectFlags::fTUNNEL) != 0 );
	if( ! _coid_bstream ) return ersFE_UNREACHABLE;
	coid_flags &= ~ConnectFlags::fTUNNEL;
	setup_stream( *_coid_bstream, coid_flags, UMAX );
	___COID_CONNECT_TRY
	*_coid_bstream << name;
	*_coid_bstream << pwd;

	_coid_bstream->flush();
	opcd _coid_err_code = _coid_bstream->get_error();
	if( _coid_err_code ) {
		_coid_bstream->acknowledge();
		delete _coid_bstream;
		return _coid_err_code;
	}

	uint coid_stream_flags;
	*_coid_bstream >> coid_stream_flags;
	RASSERT( coid_stream_flags & ConnectFlags::xACCESS_MODE );
	setup_members( coid_stream_flags, _coid_bstream, _coid_bstream );

	return _coid_err_code;

	___COID_CONNECT_CATCH
}








opcd example_client::connect_within(  comm_mutex_reg & coid_channel, const charstr & name, const password & pwd )
{
	if( ! coid_channel.is_set() ) return ersFE_CHANNEL;
	_addr.reset();
	extendedGUARD_reg __coid__mxg( coid_channel, true );

	netstream * _coid_bstream = (netstream *) ((comm_mutex_custom_reg<netstream, void*> &) coid_channel)._p1;
	if( ! _coid_bstream ) return ersFE_UNREACHABLE;
	/// we must avoid direct access mode
	setup_stream( *_coid_bstream, ConnectFlags::fACCESS_MODE_REMOTE | ConnectFlags::fACCESS_MODE_INTERPROCESS | ConnectFlags::fWITHIN_CHANNEL, UMAX );
	___COID_CONNECTWITHIN_TRY
	*_coid_bstream << name;
	*_coid_bstream << pwd;

	_coid_bstream->flush();
	opcd _coid_err_code = _coid_bstream->get_error();
	if( _coid_err_code ) {
		_coid_bstream->acknowledge();
		return _coid_err_code;
	}

	uint coid_stream_flags;
	*_coid_bstream >> coid_stream_flags;
	RASSERT( coid_stream_flags & ConnectFlags::xACCESS_MODE );
	setup_members( coid_stream_flags, _coid_bstream, _coid_bstream, &coid_channel );

	return _coid_err_code;

	___COID_CONNECTWITHIN_CATCH
}






opcd example_client::connect_shared( uint coid_obj_id,  const char * coid_address, uint coid_flags  )
{
	_addr.reset();
	extendedGUARD_reg __coid__mxg( _mx_reg, false );
	if( _mx_reg.is_valid() ) {
		if( get_connection_type() == ConnectFlags::fACCESS_MODE_DIRECT ) {	/// direct
			return ersFE_ALREADY_CONNECTED;
		}
		else if( get_connection_type() == ConnectFlags::fACCESS_MODE_INTERPROCESS ) {	/// interprocess
			__coid__mxg.lock();
			if( ((COID_CLIENT *)_me)->is_stream_open() )
				return ersFE_ALREADY_CONNECTED;
		}
		else if( get_connection_type() == ConnectFlags::fACCESS_MODE_REMOTE ) {	/// remote
			__coid__mxg.lock();
			if( ((COID_CLIENT *)_me)->is_stream_open() )
				return ersFE_ALREADY_CONNECTED;
		}
	}

	netstream * _coid_bstream = COID_CLIENT::create_stream( "example", coid_address, _addr, (coid_flags & ConnectFlags::fTUNNEL) != 0 );
	if( ! _coid_bstream ) return ersFE_UNREACHABLE;
	coid_flags &= ~ConnectFlags::fTUNNEL;
	setup_stream( *_coid_bstream, coid_flags, coid_obj_id );
	___COID_CONNECT_TRY

	_coid_bstream->flush();
	opcd _coid_err_code = _coid_bstream->get_error();
	if( _coid_err_code ) {
		_coid_bstream->acknowledge();
		delete _coid_bstream;
		return _coid_err_code;
	}

	uint coid_stream_flags;
	*_coid_bstream >> coid_stream_flags;
	RASSERT( coid_stream_flags & ConnectFlags::xACCESS_MODE );
	setup_members( coid_stream_flags, _coid_bstream, _coid_bstream );

	return _coid_err_code;

	___COID_CONNECT_CATCH
}









opcd example_client::read1( FOO_STRUCT & list )
{
	opcd _coid_err_code;
#ifndef example_NOTHROW
	try {
#endif
		extendedGUARD_lock( _mx_reg );
		if( ! _me ) return ersFE_DISCONNECTED;
		_coid_err_code = (_me->*(_vtbl->read1__0)) (list);
#ifndef example_NOTHROW
	}
	catch( opcd e ) {_coid_err_code = e;}
#endif
	return _coid_err_code;
}

opcd example_client::read2( FOO_STRUCT & list )
{
	opcd _coid_err_code;
#ifndef example_NOTHROW
	try {
#endif
		extendedGUARD_lock( _mx_reg );
		if( ! _me ) return ersFE_DISCONNECTED;
		if( get_connection_type() != ConnectFlags::fACCESS_MODE_DIRECT )
			_coid_err_code = ((example_localclient *) _me)->read2 (list);
		else {
			FOO_STRUCT * coidfetch_list;
			_coid_err_code = (_me->*(_vtbl->read2__1)) (coidfetch_list);
			if( ! _coid_err_code ) {
				list = *coidfetch_list;
			}
		}
#ifndef example_NOTHROW
	}
	catch( opcd e ) {_coid_err_code = e;}
#endif
	return _coid_err_code;
}

opcd example_client::send( int & ref, const charstr & command, binstreambuf & reply ) const
{
	opcd _coid_err_code;
#ifndef example_NOTHROW
	try {
#endif
		extendedGUARD_lock( _mx_reg );
		if( ! _me ) return ersFE_DISCONNECTED;
		_coid_err_code = (_me->*(_vtbl->send__2)) (ref, command, reply);
#ifndef example_NOTHROW
	}
	catch( opcd e ) {_coid_err_code = e;}
#endif
	return _coid_err_code;
}

opcd example_client::remap_fnc( REMAP_EXAMPLE_clientside & remapped_arg )
{
	opcd _coid_err_code;
#ifndef example_NOTHROW
	try {
#endif
		extendedGUARD_lock( _mx_reg );
		if( ! _me ) return ersFE_DISCONNECTED;
		if( get_connection_type() != ConnectFlags::fACCESS_MODE_DIRECT )
			_coid_err_code = ((example_localclient *) _me)->remap_fnc (remapped_arg);
		else {
			COID_CLIENT_EXESTREAM_ARGS args( (void *) _me, COID_example_remap_fnc_3 );
			_mexestream.reset();
			_mexestream.set_fnc( (mexestream::mexestream_fnc) (_vtbl->remap_fnc__3), &args );
			mexestream * _coid_bstream = &_mexestream;
		
			_coid_bstream->flush();
			_coid_err_code = _coid_bstream->get_error();
			if( _coid_err_code ) {
				_coid_bstream->acknowledge();
				return _coid_err_code;
			}
			*_coid_bstream >> remapped_arg;
			_coid_bstream->acknowledge();
		}
#ifndef example_NOTHROW
	}
	catch( opcd e ) {_coid_err_code = e;}
#endif
	return _coid_err_code;
}

opcd example_client::get_ptr( FOO_STRUCT ** f )
{
	opcd _coid_err_code;
#ifndef example_NOTHROW
	try {
#endif
		extendedGUARD_lock( _mx_reg );
		if( ! _me ) return ersFE_DISCONNECTED;
		_coid_err_code = (_me->*(_vtbl->get_ptr__4)) (f);
#ifndef example_NOTHROW
	}
	catch( opcd e ) {_coid_err_code = e;}
#endif
	return _coid_err_code;
}



}	/// namespace coid