// **********************************************************************
//
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#include <OB/CORBA.h>
#include <OB/Util.h>

#include <ReceiverBase_impl.h>

#include <stdlib.h>
#include <stdio.h>

#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif

#ifdef HAVE_WINSOCK_H
#   include <winsock.h>
#else
#   include <netdb.h>
#endif

#ifdef HAVE_NO_GETHOSTNAME_PROTOTYPE
 
extern "C"
{
    int gethostname(char*,  int);
}
 
#endif

#ifndef MAXHOSTNAMELEN
#   define MAXHOSTNAMELEN 1024
#endif

// ----------------------------------------------------------------------
// Handle common errors
// ----------------------------------------------------------------------

void
ReceiverBase_impl::registerAgain(const CORBA_COMM_FAILURE& ex)
{
    OBPrintException(ex);

    print("*** Communication failure with the server");
    print("*** Trying to register with the server again in 1 second...");

#ifdef WIN32
    Sleep(1000);
#else
    sleep(1);
#endif

    _register();
}

void
ReceiverBase_impl::unknownNick(const char* nick)
{ 
    const char* msg = "*** Unknown nick name `";
    
    CORBA_String_var message =
	CORBA_string_alloc(strlen(msg) + strlen(nick) + 1);
    
    strcpy(message.inout(), msg);
    strcat(message.inout(), nick);
    strcat(message.inout(), "'");
    
    print(message);
}

void
ReceiverBase_impl::nickExists(const char* nick)
{ 
    const char* msg1 = "*** Nick name `";
    const char* msg2 = "' already exists";
    
    CORBA_String_var message =
	CORBA_string_alloc(strlen(msg1) + strlen(nick) + strlen(msg2));
    
    strcpy(message.inout(), msg1);
    strcat(message.inout(), nick);
    strcat(message.inout(), msg2);
    
    print(message);
}

// ----------------------------------------------------------------------
// Register with the server
// ----------------------------------------------------------------------
 
void
ReceiverBase_impl::_register()
{
    Receiver_var self = _this();

    while(true)
    {
        try
        {
            server_ -> registerWithCheck(self, id_, host_, nick_);
            return;
        }
        catch(const CORBA_COMM_FAILURE& ex)
        {
	    OBPrintException(ex);

            print("*** Can't register with the server");
            print("*** Retry in 1 second...");

#ifdef WIN32
	    Sleep(1000);
#else
	    sleep(1);
#endif
        }
	catch(const Broadcaster::ReceiverUnreachable&)
	{
	    print("*** Chat server can't call back to chat client");
	    print("*** (Usually caused by a firewall or "
		  "name server problem)");
            print("*** Retry in 1 second...");

#ifdef WIN32
	    Sleep(1000);
#else
	    sleep(1);
#endif
	}
	catch(const Broadcaster::NickExists&)
	{
	    nickExists(nick_);

	    CORBA_String_var s1 = CORBA_string_alloc(strlen(nick_) + 1);
	    strcpy(s1.inout(), nick_);
	    strcat(s1.inout(), "_");
	    nick_ = s1;

            const char* msg1 = "*** Trying to use `";
            const char* msg2 = "' instead";
	    CORBA_String_var s2 =
		CORBA_string_alloc(strlen(msg1) + strlen(nick_) +
				   strlen(msg2));

	    strcpy(s2.inout(), msg1);
	    strcat(s2.inout(), nick_);
	    strcat(s2.inout(), msg2);

	    print(s2);
	}
	catch(const Broadcaster::AlreadyRegistered&)
	{
	    print("*** Warning: client was already registered");
	    print("*** Warning: unregistering and registering client now");

	    try
	    {
		Receiver_var self = _this();
		server_ -> unregister(self);
	    }
	    catch(const Broadcaster::UnknownReceiver&)
	    {
	    }
	}
    }
}

// ----------------------------------------------------------------------
// Receiver constructor and desctructor
// ----------------------------------------------------------------------

ReceiverBase_impl::ReceiverBase_impl(Broadcaster_ptr server)
    : server_(Broadcaster::_duplicate(server))
{
    //
    // Get ID
    //
#ifdef WIN32
    const char* env = getenv("USERNAME");
#else
    const char* env = getenv("LOGNAME");
#endif

    if(env)
    {
	id_ = env;
    }
    else
    {
#ifdef WIN32
	id_ = (const char*)"<unknown>";
#else
	const char* id = getlogin();

	if(!id)
	    id = "<unknown>";

	id_ = id;
#endif
    }

    //
    // Get nick
    //
    if(getenv("CHATNICK"))
	nick_ = CORBA_string_dup(getenv("CHATNICK"));
    else
	nick_ = id_;

    //
    // Try to get canonical hostname
    //
    char host[MAXHOSTNAMELEN];
    gethostname(host, MAXHOSTNAMELEN);

    hostent* hostData = gethostbyname(host);
    if(hostData)
	host_ = CORBA_string_dup(hostData -> h_name);
    else
	host_ = CORBA_string_dup(host);
}

// ----------------------------------------------------------------------
// Parse input
// ----------------------------------------------------------------------

void
ReceiverBase_impl::parse(const char* input)
{
    if(strlen(input) == 0)
	return;

    if(*input != '/')
    {
	//
	// No command, "say" it
	//
	CORBA_String_var message =
	    CORBA_string_alloc(1 + strlen(nick_) + 2 + strlen(input));
	
	strcpy(message.inout(), "<");
	strcat(message.inout(), nick_);
	strcat(message.inout(), "> ");
	strcat(message.inout(), input);
	
	while(true)
	{
	    try
	    {
		server_ -> say(message);
		return;
	    }
	    catch(const CORBA_COMM_FAILURE& ex)
	    {
		registerAgain(ex);
	    }
	}
    }
    else
    {
	//
	// Command, execute it
	//
	execute(input + 1);
    }
}

// ----------------------------------------------------------------------
// Execute commands
// ----------------------------------------------------------------------

void
ReceiverBase_impl::execute(const char* cmds)
{
    CORBA_String_var cmd = CORBA_string_dup(cmds);
    static const char* tok = " \t";
    char* s = strtok(cmd.inout(), tok);

    if(!strcmp(s, "nick"))
    {
	char* nick = strtok(0, tok);

	if(nick)
	    cmdNick(nick);
	else
	    print("*** Nick name is missing");
    }
    else if(!strcmp(s, "who"))
    {
	cmdWho();
    }
    else if(!strcmp(s, "whois"))
    {
	char* nick = strtok(0, tok);

	if(nick)
	    cmdWhois(nick);
	else
	    print("*** Nick name is missing");
    }
    else if(!strcmp(s, "msg"))
    {
	char* nick = strtok(0, tok);

	if(nick)
	{
	    char* msg = strtok(0, "\n");
	    if(msg)
		cmdMsg(nick,  msg);
	    else
		print("*** Message is missing");
	}
	else
	    print("*** Nick name is missing");
    }
    else if(!strcmp(s, "about"))
	cmdAbout();
    else if(!strcmp(s, "help"))
	cmdHelp();
    else if(!strcmp(s, "quit"))
	cmdQuit();
    else
    {
	const char* msg = "*** Unknown command `";

	CORBA_String_var message =
	    CORBA_string_alloc(strlen(msg) + strlen(s) + 1);

	strcpy(message.inout(), msg);
	strcat(message.inout(), s);
	strcat(message.inout(), "'");

	print(message);
    }
}

// ----------------------------------------------------------------------
// `quit' command
// ----------------------------------------------------------------------

void
ReceiverBase_impl::cmdQuit()
{
    quit();
}

// ----------------------------------------------------------------------
// `about' command
// ----------------------------------------------------------------------

void
ReceiverBase_impl::cmdAbout()
{
    about();
}

// ----------------------------------------------------------------------
// `help' command
// ----------------------------------------------------------------------

void
ReceiverBase_impl::cmdHelp()
{
    help();
}

// ----------------------------------------------------------------------
// `msg' command
// ----------------------------------------------------------------------

void
ReceiverBase_impl::cmdMsg(const char* nick, const char* msg)
{
    while(true)
    {
	try
	{
	    Broadcaster::ReceiverDescription_var desc;
	    server_ -> getReceiverByNick(nick, desc);
	    
	    CORBA_String_var message1 =
		CORBA_string_alloc(4 + strlen(msg) + 2 + strlen(nick));
	    
	    strcpy(message1.inout(), "-> *");
	    strcat(message1.inout(), nick);
	    strcat(message1.inout(), "* ");
	    strcat(message1.inout(), msg);

	    print(message1);

	    CORBA_String_var message2 =
		CORBA_string_alloc(1 + strlen(msg) + 2 + strlen(nick_));
	    
	    strcpy(message2.inout(), "*");
	    strcat(message2.inout(), nick_);
	    strcat(message2.inout(), "* ");
	    strcat(message2.inout(), msg);

	    try
	    {
		desc -> receiver -> print((const char*)message2);
	    }
	    catch(const CORBA_COMM_FAILURE& ex)
	    {
		OBPrintException(ex);

		print("*** Communication failure with other client");
		print("*** Can't send message");
	    }
	    
	    return;
	}
	catch(const CORBA_COMM_FAILURE& ex)
	{
	    registerAgain(ex);
	}
	catch(const Broadcaster::UnknownNick&)
	{
	    unknownNick(nick);
	    return;
	}
    }
}

// ----------------------------------------------------------------------
// `nick' command
// ----------------------------------------------------------------------

void
ReceiverBase_impl::cmdNick(const char* nick)
{
    while(true)
    {
	try
	{
	    Receiver_var self = _this();
	    server_ -> setNickName(self, nick);
	    nick_ = CORBA_string_dup(nick);
	    return;
	}
	catch(const CORBA_COMM_FAILURE& ex)
	{
	    registerAgain(ex);
	}
	catch(const Broadcaster::NickExists&)
	{
	    nickExists(nick);
	    return;
	}
	catch(const Broadcaster::UnknownReceiver&)
	{
	    print("*** Warning: client is not known to server");
	    print("*** Registering client now");
	    _register();
	}
    }
}

// ----------------------------------------------------------------------
// `who' command
// ----------------------------------------------------------------------

void
ReceiverBase_impl::cmdWho()
{
    while(true)
    {
	try
	{
	    Broadcaster::StringSeq_var names = server_ -> getReceiverNames();

	    for(CORBA_ULong i = 0; i < names -> length(); i++)
		print(names[i]);

	    return;
	}
	catch(const CORBA_COMM_FAILURE& ex)
	{
	    registerAgain(ex);
	}
    }
}

// ----------------------------------------------------------------------
// `whois' command
// ----------------------------------------------------------------------

void
ReceiverBase_impl::cmdWhois(const char* nick)
{
    while(true)
    {
	try
	{
	    Broadcaster::ReceiverDescription_var desc;
	    server_ -> getReceiverByNick(nick, desc);

	    CORBA_String_var message =
		CORBA_string_alloc(4 + strlen(desc -> nick) + 4 +
				   strlen(desc -> id) + 1 +
				   strlen(desc -> host));
	    
	    strcpy(message.inout(), "*** ");
	    strcat(message.inout(), desc -> nick);
	    strcat(message.inout(), " is ");
	    strcat(message.inout(), desc -> id);
	    strcat(message.inout(), "@");
	    strcat(message.inout(), desc -> host);
	    
	    print(message);
	    
	    return;
	}
	catch(const CORBA_COMM_FAILURE& ex)
	{
	    registerAgain(ex);
	}
	catch(const Broadcaster::UnknownNick&)
	{
	    unknownNick(nick);
	    return;
	}
    }
}

// ----------------------------------------------------------------------
// Print `about' and `help' text
// ----------------------------------------------------------------------

void
ReceiverBase_impl::about()
{
    print("****************************************************");
    print("");
    print("ORBacus Chat Demo Application");
    print("");
    print("Copyright (c) 1999");
    print("Object Oriented Concepts, Inc.");
    print("Billerica, MA, USA");
    print("");
    print("All Rights Reserved");
    print("");
    print("****************************************************");
}

void
ReceiverBase_impl::help()
{
    print("****************************************************");
    print("");
    print("Available commands:");
    print("");
    print("/about             Display `about' info");
    print("/help              Show this help text");
    print("/msg <NAME>        Send personal message to <NAME>");
    print("/nick <NAME>       Change nick name to <NAME>");
    print("/quit              Leave the chat");
    print("/who               List chat participants");
    print("/whois <NAME>      Identify <NAME>");
    print("");
    print("****************************************************");
}

// ----------------------------------------------------------------------
// Quit program
// ----------------------------------------------------------------------

void
ReceiverBase_impl::quit()
{
    try
    {
	Receiver_var self = _this();
	server_ -> unregister(self);
    }
    catch(const Broadcaster::UnknownReceiver&)
    {
	print("*** Warning: client was already unregistered");
    }
    catch(...)
    {
    }

    CORBA_BOA_var boa = _boa();
    boa -> deactivate_impl(CORBA_ImplementationDef::_nil());
}

// ----------------------------------------------------------------------
// Print message
// ----------------------------------------------------------------------

void
ReceiverBase_impl::print(const char* text)
{
    cout << text << endl;
}
