/*
* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:   
* This file was autogenerated by rpcgen, but should be modified by the developer.
* Make sure you don't use the -component_mod flag in future or this file will be overwritten.
* Fri Sep 19 13:14:28 2003
* System Includes
*
*/




#include <stdio.h>
#include <unistd.h>
#include <errno.h>


/****************************************************************************************
 * 
 * Local Includes
 * 
 ***************************************************************************************/
#include "CSvcMobster.h"
#include "mobster.h"
#include "../ThreadLibrary/CAThread.h"
#include "mobster_client_server_protocol.h"


/****************************************************************************************
 * 
 * Definitions
 * 
 ***************************************************************************************/
#ifndef WIN32
#define ADDRESS_INTEGER          s_addr
#define closesocket(x)           (shutdown(x,SHUT_RDWR),close(x))
#endif

/****************************************************************************************
 * 
 * File-scope variables
 * 
 ***************************************************************************************/
static CComponentManager<CSMobster> *iComponentManager;
static CAThread *iServerThread;
static int iServerListenSocket, iStopRequestedFlag;

/****************************************************************************************
 * 
 * Implementation
 * 
 ***************************************************************************************/


/****************************************************************************************
 * 
 * PUBLIC: GetInstanceKeyFromArgs
 * 
 ***************************************************************************************/
int CSvcMobster::GetInstanceKeyFromArgs( int aMethod, void *aArgs )
{
	int rv;
	int *ia;
	TUUAddress *ua;

	switch( aMethod ) {

	case DSTR_REMOVEDEVICE:
	case GETDEVICEINFO:
	case GETDEVICELOG:
	case STOPDEVICE:
		ia = (int*)aArgs;
		rv = *ia;
		break;

	case SETREMOTEUUADDRESS:
		ua = (TUUAddress*)aArgs;
		rv = ua->iDeviceID;
		break;

	default:
		rv = ERR_INVALID_METHOD;
		break;
	}
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC: SetError
 * 
 ***************************************************************************************/
int CSvcMobster::SetError( int aMethod, void *aArgs, int aError )
{
	int rv;
	TComponentList *cl;
	TDeviceDesc *dd;
	TVarData *vd;

	switch( aMethod ) {

	case LIST_DEVICES:
		cl = (TComponentList*)aArgs;
		if( aError != ERR_NONE ) {
			if( cl->TComponentList_val != NULL ) {
				free(cl->TComponentList_val);
				cl->TComponentList_val = NULL;
			}
			cl->TComponentList_len = 0;
		}
		rv = ERR_NONE;
		break;

	case GETDEVICEINFO:
		dd = (TDeviceDesc*)aArgs;
		dd->iMTID = aError;
		rv = ERR_NONE;
		break;

	case GETDEVICELOG:
		vd = (TVarData*)aArgs;
		if( aError != ERR_NONE ) {
			if( vd->TVarData_val != NULL ) {
				free(vd->TVarData_val);
				vd->TVarData_val = NULL;
			}
			vd->TVarData_len = 0;
		}
		rv = ERR_NONE;
		break;

	default:
		rv = ERR_INVALID_METHOD;
		break;
	}
	return rv;
}


/****************************************************************************************
 * 
 * PUBLIC: StartRPCService
 * 
 ***************************************************************************************/
int CSvcMobster::StartRPCService( CComponentManager<CSMobster> *aComponentManager, TChannelAddress *aArg )
{
	TThreadError terr;
	int err;
	struct sockaddr_in local_address;

	// prologue
	assert( iComponentManager == NULL );
	assert( iServerThread == NULL );

	// create a socket to listen for connections
	iServerListenSocket = socket( AF_INET, SOCK_STREAM, 0 );
	if( iServerListenSocket == -1 ) {
		return ERR_CREATE_SOCKET_FAILED;
	}

	// bind the specified port (ignore address)
	local_address.sin_family = AF_INET;
	local_address.sin_addr.ADDRESS_INTEGER = INADDR_ANY;
	local_address.sin_port = htons(aArg->iPort);
	err = bind( iServerListenSocket, (struct sockaddr *)&local_address, sizeof(local_address) );
	if( err == -1 ) {
		closesocket( iServerListenSocket );
		return ERR_BIND_FAILED;
	}

	// listen
	err = listen( iServerListenSocket, 5 );
	if( err == -1 ) {
		closesocket( iServerListenSocket );
		return ERR_LISTEN_FAILED;
	}

	// ok the socket is all setup to do an accept - so we pass over to the other thread
	// and let the main call continue
	iStopRequestedFlag = 0;
	iServerThread = new CAThread( "CSvcMobster::iServerThread" );
	assert( iServerThread != NULL );
	terr = iServerThread->StartThread( (void*)CSvcMobster::WaitForConnectionsFromTerminalEmulations, NULL, &err ); 
	if( terr != TE_NONE ) {
		closesocket( iServerListenSocket );
		delete iServerThread;
		iServerThread = NULL;
		return ERR_CREATE_SERVER_THREAD_FAILED;
	}

	// done 
	iComponentManager = aComponentManager;
	return ERR_NONE;
}


/****************************************************************************************
 * 
 * PUBLIC: StopRPCService
 * 
 ***************************************************************************************/
int CSvcMobster::StopRPCService()
{
	TThreadError terr;

	// close the socket to stop the thread
	iStopRequestedFlag = 1;
	closesocket( iServerListenSocket );
	terr = iServerThread->WaitForThread( -1 );
	if( terr != TE_NONE ) {
		fprintf( stderr, "WARNING: CSvcMobster::StopRPCService - WaitForThread returned %d.\n", terr );
	}

	// clean up the thread
	if( iServerThread != NULL ) {
		delete iServerThread;
		iServerThread = NULL;
	}

	// reset the component manager (though not responsible for delete)
	iComponentManager = NULL;
	return ERR_NONE;
}


/****************************************************************************************
 * 
 * PRIVATE: WaitForConnectionsFromTerminalEmulations
 * 
 ***************************************************************************************/
void CSvcMobster::WaitForConnectionsFromTerminalEmulations( void )
{
	int client_sockfd;

	// repeat until StopRPCService is called (or an error occurs)
	while( 1 ) {

		// wait for a connection
		client_sockfd = accept( iServerListenSocket, NULL, NULL );
		if( client_sockfd == -1 ) {
		  if( ((errno != 0) && (errno != EINVAL)) || ((errno == EINVAL) && (iStopRequestedFlag == 0)) ) {
		    fprintf( stderr, "WARNING: Accept has failed with errno = %d.\n", errno );
		  }
		  break;
		}

		// we have a socket -- now it has to do the c/s protocol so that we know which 
		// MT to connect it to -- but if there is a problem then this could block 
		// forever -- so I'm going to create a thread per connection to deal with this. 
		// These threads are responsible for cleaning themselves up.
#ifdef WIN32
		{
		  HANDLE client_thread;
		  client_thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)CSvcMobster::ConnectClientToMobileTermination, (void*)client_sockfd, 0, NULL );
		  if( client_thread == NULL ) {
		    fprintf( stderr, "WARNING: Failed to create the client thread to connect to MobileTermination (%d).\n", GetLastError() );
		  }
		  client_thread = NULL;
#else
		{
		  int err;
		  pthread_t client_thread;
		  err = pthread_create( &client_thread, NULL, (void*(*)(void*))(CSvcMobster::ConnectClientToMobileTermination), (void*)client_sockfd );
		  if( err != 0 ) {
		    fprintf( stderr, "WARNING: Failed to create the client thread to connect to MobileTermination (%d).\n", errno );
		  }
		  client_thread = 0;
		}
#endif

		// this is all we have to do
	}

	// done
	return;
}


/****************************************************************************************
 * 
 * PRIVATE: ConnectClientToMobileTermination
 * 
 ***************************************************************************************/
void CSvcMobster::ConnectClientToMobileTermination( int aSock )
{
	int mtid, err;
	CSMobster *mt;

	// do the csprotocol across the socket to get the config info
	mtid = client_server_protocol( aSock );
	if( mtid < 0 ) {
		fprintf( stderr, "WARNING: Invalid MTID received (%d).\n", mtid );
		closesocket( aSock );
		return;
	}

	// try and get the instance
	mt = iComponentManager->GetInstance( mtid );
	if( mt == NULL ) {
		fprintf( stderr, "WARNING: Client requested non-existant device %d.\n", mtid );
		closesocket( aSock );
		return;
	}

	// pass the new socket to the mt
	err = mt->settesocket( aSock );
	if( err != ERR_NONE ) {
		fprintf( stderr, "WARNING: Failed to set the socket on device %d (%d).\n", mtid, err );
		closesocket( aSock );
		return;
	}

	// all done -- thread exit
#ifdef WIN32
	ExitThread( 0 );
#else
	pthread_exit( 0 );
#endif
}

