/*
   Copyright (C) 1997-2001 Id Software, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

   See the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

 */

#include "tv_local.h"

#include "tv_cmds.h"

#include "tv_upstream.h"

static char *TV_ConnstateToString( connstate_t state )
{
	switch( state )
	{
	case CA_UNINITIALIZED:
		return "Unitialized";
	case CA_DISCONNECTED:
		return "Disconnected";
	case CA_CONNECTING:
		return "Connecting";
	case CA_HANDSHAKE:
		return "Handshake";
	case CA_CONNECTED:
		return "Connected";
	case CA_LOADING:
		return "Loading";
	case CA_ACTIVE:
		return "Active";
	default:
		return "Unknown";
	}
}

//=================
// TV_Upstream_Status_f
//=================
static void TV_Upstream_Status_f( void )
{
	int i;
	qboolean none;
	client_t *client;
	upstream_t *upstream;

	if( !TV_UpstreamForText( Cmd_Argv( 1 ), &upstream ) )
	{
		Com_Printf( "No such connection\n" );
		return;
	}

	if( upstream )
	{
		Com_Printf( "%s: %s\n", upstream->name, NET_AddressToString( &upstream->serveraddress ) );
		Com_Printf( "Connection: %s\n", TV_ConnstateToString( upstream->state ) );
		Com_Printf( "Relay: %s\n", TV_ConnstateToString( upstream->relay.state ) );
	}
	else
	{
		Com_Printf( "lobby\n" );
	}

	Com_Printf( "Downstream connections:\n" );
	none = qtrue;
	for( i = 0; i < tv_maxclients->integer; i++ )
	{
		client = &tvs.clients[i];

		if( client->state == CS_FREE || client->state == CS_ZOMBIE )
			continue;
		if( !client->relay )
		{
			if( upstream )
				continue;
		}
		else
		{
			if( client->relay->upstream != upstream )
				continue;
		}

		Com_Printf( "%3i: %s (%s)\n", i, client->name, NET_AddressToString( &client->netchan.remoteAddress ) );
		none = qfalse;
	}
	if( none )
		Com_Printf( "- No downstream connections\n" );
}

//=================
// TV_Status
//=================
static void TV_Status( void )
{
	int i;
	qboolean none;
	client_t *client;

	Com_Printf( "Upstream connections:\n" );
	none = qtrue;
	for( i = 0; i < tvs.numupstreams; i++ )
	{
		if( !tvs.upstreams[i] )
			continue;

		Com_Printf( "%3i: %22s: %s\n", i+1, NET_AddressToString( &tvs.upstreams[i]->serveraddress ),
		            tvs.upstreams[i]->name );
		none = qfalse;
	}
	if( none )
		Com_Printf( "- No upstream connections\n" );

	Com_Printf( "Downstream connections:\n" );
	none = qtrue;
	for( i = 0; i < tv_maxclients->integer; i++ )
	{
		client = &tvs.clients[i];

		if( client->state == CS_FREE || client->state == CS_ZOMBIE )
			continue;

		Com_Printf( "%3i: %s (%s): %s %s\n", i, client->name, NET_AddressToString( &client->netchan.remoteAddress ),
			( client->relay ? client->relay->upstream->name : "lobby" ), client->mv ? "MV" : "" );
		none = qfalse;
	}
	if( none )
		Com_Printf( "- No downstream connections\n" );
}

//=================
// TV_Status_f
//=================
static void TV_Status_f( void )
{
	if( Cmd_Argc() == 1 )
	{
		TV_Status();
	}
	else if( Cmd_Argc() == 2 )
	{
		TV_Upstream_Status_f();
	}
	else
	{
		Com_Printf( "Usage: status [upstream]\n" );
	}
}

//=================
// TV_Cmd_f
//=================
static void TV_Cmd_f( void )
{
	upstream_t *upstream;

	if( Cmd_Argc() != 3 )
	{
		Com_Printf( "Usage: cmd <server> <command>\n" );
		return;
	}

	if( !TV_UpstreamForText( Cmd_Argv( 1 ), &upstream ) )
	{
		Com_Printf( "No such upstream\n" );
		return;
	}

	if( !upstream )
	{
		Com_Printf( "Can't send commands to lobby\n" );
		return;
	}

	if( !Cmd_Argv( 2 )[0] )
	{
		Com_Printf( "Empty command\n" );
		return;
	}

	TV_Upstream_AddReliableCommand( upstream, Cmd_Argv( 2 ) );
}

/*
   ================
   TV_Disconnect_f
   ================
 */
static void TV_Disconnect_f( void )
{
	upstream_t *upstream;

	if( Cmd_Argc() != 2 )
	{
		Com_Printf( "Usage: disconnect <server>\n" );
		return;
	}

	if( !TV_UpstreamForText( Cmd_Argv( 1 ), &upstream ) )
	{
		Com_Printf( "No such upstream\n" );
		return;
	}

	if( !upstream )
	{
		Com_Printf( "Can't disconnect lobby\n" );
		return;
	}

	//	TV_Upstream_Disconnect( upstream, "Disconnected by adminstrator" );
	TV_Upstream_Shutdown( upstream, "Disconnected by adminstrator" );
}

/*
   ================
   TV_GenericConnect_f
   ================
 */
static void TV_GenericConnect_f( socket_type_t socket )
{
	netadr_t serveraddress;
	char *servername, *password;
	upstream_t *upstream;
	unsigned int delay;

	if( Cmd_Argc() < 2 )
	{
		Com_Printf( "Usage: %s <server> [password] [delay]\n", Cmd_Argv( 0 ) );
		return;
	}

	if( !NET_StringToAddress( Cmd_Argv( 1 ), &serveraddress ) )
	{
		Com_Printf( "Bad server address: %s\n", Cmd_Argv( 1 ) );
		return;
	}

	servername = TempCopyString( Cmd_Argv( 1 ) );
	password = ( Cmd_Argc() >= 3 ? TempCopyString( Cmd_Argv( 2 ) ) : NULL );
	delay = ( Cmd_Argc() >= 4 ? (unsigned)atoi( Cmd_Argv( 3 ) )*1000 : RELAY_GLOBAL_DELAY );

	if( TV_UpstreamForText( servername, &upstream ) )
	{
		if( upstream->state >= CA_CONNECTED && upstream->relay.delay == delay
			&& upstream->socket && upstream->socket->type == socket )
		{
			Com_Printf( "Already connected to %s\n", servername );
			goto exit;
		}

		TV_Upstream_Shutdown( upstream, "Disconnected by adminstrator" );
		upstream = NULL;
	}

	upstream = TV_Upstream_New( Cmd_Argv( 1 ), delay );
	assert( upstream );
	TV_Upstream_Connect( upstream, servername, password, socket, &serveraddress );

exit:
	Mem_TempFree( servername );
	if( password )
		Mem_TempFree( password );
}

/*
   ================
   TV_Connect_f
   ================
 */
static void TV_Connect_f( void )
{
	TV_GenericConnect_f( SOCKET_UDP );
}

/*
   ================
   TV_TCPConnect_f
   ================
 */
#ifdef TCP_SUPPORT
static void TV_TCPConnect_f( void )
{
	TV_GenericConnect_f( SOCKET_TCP );
}
#endif

//=================
// TV_PlayDemo_f
//=================
static void TV_PlayDemo_f( void )
{
	char *servername, *mode;
	upstream_t *upstream;
	unsigned int delay;
	qboolean randomize = qtrue;

	if( Cmd_Argc() < 2 )
	{
		Com_Printf( "Usage: %s <pattern|playlist> [ordered|random] [delay]\n", Cmd_Argv( 0 ) );
		return;
	}

	servername = TempCopyString( Cmd_Argv( 1 ) );
	mode = Cmd_Argv( 2 );
	delay = ( Cmd_Argc() >= 4 ? (unsigned)atoi( Cmd_Argv( 3 ) )*1000 : RELAY_MIN_DELAY );
	if( !Q_stricmp( mode, "ordered" ) )
		randomize = qfalse;

	if( TV_UpstreamForText( servername, &upstream ) )
	{
		if( upstream->state >= CA_CONNECTED && upstream->relay.delay == delay )
		{
			Com_Printf( "Already connected to %s\n", servername );
			goto exit;
		}

		TV_Upstream_Shutdown( upstream, "Disconnected by adminstrator" );
		upstream = NULL;
	}

	upstream = TV_Upstream_New( Cmd_Argv( 1 ), delay );
	assert( upstream );
	TV_Upstream_StartDemo( upstream, servername, randomize );

exit:
	Mem_TempFree( servername );
}

//=================
// List of commands
//=================
typedef struct
{
	char *name;
	void ( *func )( void );
} cmd_function_t;

static cmd_function_t cmdlist[] =
{
	{ "connect", TV_Connect_f },
#ifdef TCP_SUPPORT
	{ "tcpconnect", TV_TCPConnect_f },
#endif
	{ "disconnect", TV_Disconnect_f },
	{ "demo", TV_PlayDemo_f },
	{ "status", TV_Status_f },
	{ "cmd", TV_Cmd_f },
	{ NULL, NULL }
};

/*
   ================
   TV_AddCommands
   ================
 */
void TV_AddCommands( void )
{
	cmd_function_t *cmd;

	for( cmd = cmdlist; cmd->name; cmd++ )
		Cmd_AddCommand( cmd->name, cmd->func );
}

/*
   ================
   TV_RemoveCommands
   ================
 */
void TV_RemoveCommands( void )
{
	cmd_function_t *cmd;

	for( cmd = cmdlist; cmd->name; cmd++ )
		Cmd_RemoveCommand( cmd->name );
}
