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

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

#include <stdlib.h>
#include <time.h>

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

// ----------------------------------------------------------------------
// Add/remove receiver
// ----------------------------------------------------------------------

void
Broadcaster_impl::add(Receiver_ptr receiver, const char* id,
		      const char* host, const char* nick)
{
    CORBA_ULong i;

    //
    // Check if already registered
    //
    for(i = 0 ; i < receiverDescSeq_.length() ; i++)
        if(receiverDescSeq_[i].receiver -> _is_equivalent(receiver))
	    throw AlreadyRegistered();

    //
    // Check if nick already exists
    //
    for(i = 0 ; i < receiverDescSeq_.length() ; i++)
	if(strcmp(receiverDescSeq_[i].nick, nick) == 0)
	    throw NickExists();

    //
    // Print debug info
    //
    cout << "dumping new receiver object reference:\n";
    OBPrintObjref(cout, receiver);
    time_t t;
    char* d;
    t = time(0);
    d = asctime(localtime(&t));
    assert(d && strlen(d) > 0);
    d[strlen(d) - 1] = '\0';
    cout << d << endl;

    //
    // Add receiver description
    //
    CORBA_ULong len = receiverDescSeq_.length() + 1;
    receiverDescSeq_.length(len);
    receiverDescSeq_[len - 1].receiver = Receiver::_duplicate(receiver);
    receiverDescSeq_[len - 1].id = id;
    receiverDescSeq_[len - 1].host = host;
    receiverDescSeq_[len - 1].nick = nick;
}

void
Broadcaster_impl::remove(Receiver_ptr receiver)
{
    //
    // Search receiver
    //
    for(CORBA_ULong i = 0 ; i < receiverDescSeq_.length() ; i++)
        if(receiverDescSeq_[i].receiver -> _is_equivalent(receiver))
        {
            //
            // Receiver found, remove it
            //
            while(i < receiverDescSeq_.length() - 1)
            {
                receiverDescSeq_[i] = receiverDescSeq_[i + 1];
                i++;
            }
            receiverDescSeq_.length(receiverDescSeq_.length() - 1);
 
	    return;
	}

    //
    // Receiver was not found
    //
    throw UnknownReceiver();
}

// ----------------------------------------------------------------------
// Register/unregister receiver
// ----------------------------------------------------------------------

void
Broadcaster_impl::_register(Receiver_ptr receiver, const char* id,
			    const char* host, const char* nick)
{
    //
    // Do nothing if this is a nil object reference
    //
    if(CORBA_is_nil(receiver))
        return;

    //
    // Add receiver
    //
    add(receiver, id, host, nick);

    //
    // Send `join' message to others
    //
    static const char* msg = ") has joined the chat";

    CORBA_String_var message =
	CORBA_string_alloc(4 + strlen(nick) + 2 + strlen(id) + 1 +
			   strlen(host) + strlen(msg));

    strcpy(message.inout(), "*** ");
    strcat(message.inout(), nick);
    strcat(message.inout(), " (");
    strcat(message.inout(), id);
    strcat(message.inout(), "@");
    strcat(message.inout(), host);
    strcat(message.inout(), msg);

    say(message);
}

void
Broadcaster_impl::registerWithCheck(Receiver_ptr receiver, const char* id,
				    const char* host, const char* nick)
{
    //
    // Do nothing if this is a nil object reference
    //
    if(CORBA_is_nil(receiver))
        return;

    //
    // Try to ping the receiver
    //
    CORBA_Boolean nonExist = CORBA_TRUE;
    try
    {
	nonExist = receiver -> _non_existent();
    }
    catch(CORBA_COMM_FAILURE& ex)
    {
	OBPrintException(ex);
    }

    if(nonExist)
	throw ReceiverUnreachable();

    //
    // Register receiver
    //
    _register(receiver, id, host, nick);
}

void
Broadcaster_impl::unregister(Receiver_ptr receiver)
{
    //
    // Do nothing if this is a nil object reference
    //
    if(CORBA_is_nil(receiver))
        return;
 
    //
    // Remember nick name
    //
    CORBA_String_var nick;
    for(CORBA_ULong i = 0 ; i < receiverDescSeq_.length() ; i++)
        if(receiverDescSeq_[i].receiver -> _is_equivalent(receiver))
	    nick = receiverDescSeq_[i].nick;

    //
    // Remove receiver
    //
    remove(receiver);
 
    //
    // Send `leave' message to other
    //
    if((const char*)nick != 0)
    {
	const char* msg = " has left the chat";
	
	CORBA_String_var message =
	    CORBA_string_alloc(4 + strlen(nick) + strlen(msg));
	
	strcpy(message.inout(), "*** ");
	strcat(message.inout(), nick);
	strcat(message.inout(), msg);
	
	say(message);
    }
}

// ----------------------------------------------------------------------
// Set nickname
// ----------------------------------------------------------------------

void
Broadcaster_impl::setNickName(Receiver_ptr receiver, const char* nick)
{
    CORBA_ULong i;

    //
    // Do nothing if this is a nil object reference
    //
    if(CORBA_is_nil(receiver))
        return;

    //
    // Check if nick already exists
    //
    for(i = 0 ; i < receiverDescSeq_.length() ; i++)
	if(strcmp(receiverDescSeq_[i].nick, nick) == 0)
	    throw NickExists();

    //
    // Search receiver
    //
    for(i = 0 ; i < receiverDescSeq_.length() ; i++)
        if(receiverDescSeq_[i].receiver -> _is_equivalent(receiver))
        {
            //
            // Receiver found, set nick name
            //
	    CORBA_String_var oldNick = receiverDescSeq_[i].nick;
	    receiverDescSeq_[i].nick = nick;

	    //
	    // Send `is known as' message to all
	    //
	    const char* msg = " is now known as ";
	    
	    CORBA_String_var message =
		CORBA_string_alloc(4 + strlen(oldNick) + strlen(nick) +
				   strlen(msg));
	    
	    strcpy(message.inout(), "*** ");
	    strcat(message.inout(), oldNick);
	    strcat(message.inout(), msg);
	    strcat(message.inout(), nick);
	    
	    say(message);

	    return;
	}

    //
    // Receiver was not found
    //
    throw UnknownReceiver();
}

// ----------------------------------------------------------------------
// Get receiver by nickname
// ----------------------------------------------------------------------

void
Broadcaster_impl::getReceiverByNick(const char* nick,
				    ReceiverDescription*& desc)
{
    //
    // Search for nick name
    //
    for(CORBA_ULong i = 0 ; i < receiverDescSeq_.length() ; i++)
	if(!strcmp(receiverDescSeq_[i].nick, nick))
	{
	    desc = new ReceiverDescription(receiverDescSeq_[i]);
	    return;
	}

    //
    // Nick name was not found
    //
    throw UnknownNick();
}

// ----------------------------------------------------------------------
// Get all receiver names
// ----------------------------------------------------------------------

Broadcaster_impl::StringSeq*
Broadcaster_impl::getReceiverNames()
{
    StringSeq* names = new StringSeq;
    names -> length(receiverDescSeq_.length());

    for(CORBA_ULong i = 0; i < receiverDescSeq_.length(); i++)
    {
	char* s = CORBA_string_alloc(strlen(receiverDescSeq_[i].nick) + 2 +
				     strlen(receiverDescSeq_[i].id) + 1 +
				     strlen(receiverDescSeq_[i].host) + 1);
	
	strcpy(s, receiverDescSeq_[i].nick);
	strcat(s, " (");
	strcat(s, receiverDescSeq_[i].id);
	strcat(s, "@");
	strcat(s, receiverDescSeq_[i].host);
	strcat(s, ")");

	(*names)[i] = s;
    }

    return names;
}

// ----------------------------------------------------------------------
// Say something
// ----------------------------------------------------------------------

void
Broadcaster_impl::say(const char* text)
{
    CORBA_ULong i;

    //
    // Remember `bad' object references in a `bad sequence'
    //
    ReceiverDescSeq badSeq;
    for(i = 0 ; i < receiverDescSeq_.length() ; i++)
    {
        try
        {
            receiverDescSeq_[i].receiver -> print(text);
        }
        catch(CORBA_COMM_FAILURE& ex)
        {
            OBPrintException(ex);

	    CORBA_ULong len = badSeq.length() + 1;
	    badSeq.length(len);
	    badSeq[len - 1] = receiverDescSeq_[i];
        }
    }
 
    //
    // Unregister `bad' object references
    //
    for(i = 0 ; i < badSeq.length() ; i++)
	remove(badSeq[i].receiver);

    //
    // Send `vanish' messages
    //
    for(i = 0 ; i < badSeq.length() ; i++)
    {
	const char* msg = " vanished";
	
	CORBA_String_var message =
	    CORBA_string_alloc(4 + strlen(badSeq[i].nick) + strlen(msg));
	
	strcpy(message.inout(), "*** ");
	strcat(message.inout(), badSeq[i].nick);
	strcat(message.inout(), msg);
	
	say(message);
    }

    //
    // Print text as log message, too
    //
    cout << text << endl;
}
