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

#include <OB/Basic.h>
#include <OB/Except.h>
#include <OB/Template.h>
#include <OB/Declarations.h>
#include <OB/IOP.h>
#include <OB/Object.h>
#include <OB/Util.h>

#include <ctype.h>

OBMessageViewer* OBMessageViewer::instance_ = 0;
int OBMessageViewer::traceLevel_;

class OBMessageViewerDestroyer
{
public:

    ~OBMessageViewerDestroyer()
    {
	delete OBMessageViewer::instance_;
	OBMessageViewer::instance_ = 0;
    }
};

static OBMessageViewerDestroyer destroyer;

// ----------------------------------------------------------------------
// OBMessageViewer public member implementation
// ----------------------------------------------------------------------

OBMessageViewer*
OBMessageViewer::instance()
{
    if(!instance_)
	instance_ = new OBMessageViewer();

    return instance_;
}

int
OBMessageViewer::getTraceLevel()
{
    return traceLevel_;
}

void
OBMessageViewer::setTraceLevel(int level)
{
    traceLevel_ = level;
}

void
OBMessageViewer::message(const char* msg)
{
    cerr << msg << endl;
}

void
OBMessageViewer::error(const char* msg)
{
    const char* head = "Error: ";
    CORBA_String_var s = CORBA_string_alloc(strlen(head) + strlen(msg));
    strcpy(s.inout(), head);
    strcat(s.inout(), msg);
    message(s);
}

void
OBMessageViewer::warning(const char* msg)
{
    const char* head = "Warning: ";
    CORBA_String_var s = CORBA_string_alloc(strlen(head) + strlen(msg));
    strcpy(s.inout(), head);
    strcat(s.inout(), msg);
    message(s);
}

void
OBMessageViewer::nca(const char* msg)
{
    const char* head = "Non-compliant application error detected:\n";
    CORBA_String_var s = CORBA_string_alloc(strlen(head) + strlen(msg));
    strcpy(s.inout(), head);
    strcat(s.inout(), msg);
    message(s);
}

void
OBMessageViewer::trace(int level, const char* msg)
{
    if(traceLevel_ >= level)
    {
        CORBA_String_var s = CORBA_string_alloc(strlen(msg) + 2);
        strcpy(s.inout(), "[");
        strcat(s.inout(), msg);
        strcat(s.inout(), "]");
        message(s);
    }
}

// ----------------------------------------------------------------------
// Print system exception
// ----------------------------------------------------------------------

void
OBPrintException(const CORBA_SystemException& ex)
{
    CORBA_String_var s = OBExceptionToString(ex);
    OBMessageViewer* viewer = OBMessageViewer::instance();
    viewer -> message(s);
}

// ----------------------------------------------------------------------
// Get stringified system exception
// ----------------------------------------------------------------------

char*
OBExceptionToString(const CORBA_SystemException& ex)
{
    CORBA_String_var msg = CORBA_string_dup("System exception ");

    if(CORBA_UNKNOWN::_narrow((CORBA_SystemException*)&ex))
	msg += "`UNKNOWN'";
    else if(CORBA_BAD_PARAM::_narrow((CORBA_SystemException*)&ex))
	msg += "`BAD_PARAM'";
    else if(CORBA_NO_MEMORY::_narrow((CORBA_SystemException*)&ex))
	msg += "`NO_MEMORY'";
    else if(CORBA_IMP_LIMIT::_narrow((CORBA_SystemException*)&ex))
	msg += "`IMP_LIMIT'";
    else if(CORBA_COMM_FAILURE::_narrow((CORBA_SystemException*)&ex))
	msg += "`COMM_FAILURE'";
    else if(CORBA_INV_OBJREF::_narrow((CORBA_SystemException*)&ex))
	msg += "`INV_OBJREF'";
    else if(CORBA_NO_PERMISSION::_narrow((CORBA_SystemException*)&ex))
	msg += "`NO_PERMISSION'";
    else if(CORBA_INTERNAL::_narrow((CORBA_SystemException*)&ex))
	msg += "`INTERNAL'";
    else if(CORBA_MARSHAL::_narrow((CORBA_SystemException*)&ex))
	msg += "`MARSHAL'";
    else if(CORBA_INITIALIZE::_narrow((CORBA_SystemException*)&ex))
	msg += "`INITIALIZE'";
    else if(CORBA_NO_IMPLEMENT::_narrow((CORBA_SystemException*)&ex))
	msg += "`NO_IMPLEMENT'";
    else if(CORBA_BAD_TYPECODE::_narrow((CORBA_SystemException*)&ex))
	msg += "`BAD_TYPECODE'";
    else if(CORBA_BAD_OPERATION::_narrow((CORBA_SystemException*)&ex))
	msg += "`BAD_OPERATION'";
    else if(CORBA_NO_RESOURCES::_narrow((CORBA_SystemException*)&ex))
	msg += "`NO_RESOURCES'";
    else if(CORBA_NO_RESPONSE::_narrow((CORBA_SystemException*)&ex))
	msg += "`NO_RESPONSE'";
    else if(CORBA_PERSIST_STORE::_narrow((CORBA_SystemException*)&ex))
	msg += "`PERSIST_STORE'";
    else if(CORBA_BAD_INV_ORDER::_narrow((CORBA_SystemException*)&ex))
	msg += "`BAD_INV_ORDER'";
    else if(CORBA_TRANSIENT::_narrow((CORBA_SystemException*)&ex))
	msg += "`TRANSIENT'";
    else if(CORBA_FREE_MEM::_narrow((CORBA_SystemException*)&ex))
	msg += "`FREE_MEM'";
    else if(CORBA_INV_IDENT::_narrow((CORBA_SystemException*)&ex))
	msg += "`INV_IDENT'";
    else if(CORBA_INV_FLAG::_narrow((CORBA_SystemException*)&ex))
	msg += "`INV_FLAG'";
    else if(CORBA_INTF_REPOS::_narrow((CORBA_SystemException*)&ex))
	msg += "`INTF_REPOS'";
    else if(CORBA_BAD_CONTEXT::_narrow((CORBA_SystemException*)&ex))
	msg += "`BAD_CONTEXT'";
    else if(CORBA_OBJ_ADAPTER::_narrow((CORBA_SystemException*)&ex))
	msg += "`OBJ_ADAPTER'";
    else if(CORBA_DATA_CONVERSION::_narrow((CORBA_SystemException*)&ex))
	msg += "`DATA_CONVERSION'";
    else if(CORBA_OBJECT_NOT_EXIST::_narrow((CORBA_SystemException*)&ex))
	msg += "`OBJECT_NOT_EXIST'";
    else if(CORBA_TRANSACTION_REQUIRED::_narrow((CORBA_SystemException*)&ex))
	msg += "`TRANSACTION_REQUIRED'";
    else if(CORBA_TRANSACTION_ROLLEDBACK::_narrow((CORBA_SystemException*)&ex))
	msg += "`TRANSACTION_ROLLEDBACK'";
    else if(CORBA_INVALID_TRANSACTION::_narrow((CORBA_SystemException*)&ex))
	msg += "`INVALID_TRANSACTION'";
    else if(CORBA_INV_POLICY::_narrow((CORBA_SystemException*)&ex))
	msg += "`INV_POLICY'";
    else
	assert(false);

    const char* reason = ex.reason();
    assert(reason);
    if(strlen(reason) > 0)
    {
	msg += "\nReason: ";
	msg += reason;
    }

    msg += "\nCompleted: ";

    switch(ex.completed())
    {
    case CORBA_COMPLETED_YES:
	msg += "yes";
	break;

    case CORBA_COMPLETED_NO:
	msg += "no";
	break;

    case CORBA_COMPLETED_MAYBE:
	msg += "maybe";
	break;
    }

    msg += "\nMinor code: ";
    msg += ex.minor();

    const CORBA_COMM_FAILURE* commFailure =
	CORBA_COMM_FAILURE::_narrow((CORBA_SystemException*)&ex);

    if(commFailure)
    {
	switch(commFailure -> minor())
	{
	case OBMinorRecv:
	    msg += " (recv() failed)";
	    break;

	case OBMinorRecvZero:
	    msg += " (recv() returned zero)";
	    break;

	case OBMinorSend:
	    msg += " (send() failed)";
	    break;

	case OBMinorSendZero:
	    msg += " (send() returned zero)";
	    break;

	case OBMinorSocket:
	    msg += " (socket() failed)";
	    break;

	case OBMinorSetsockopt:
	    msg += " (setsockopt() failed)";
	    break;

	case OBMinorGetsockopt:
	    msg += " (getsockopt() failed)";
	    break;

	case OBMinorBind:
	    msg += " (bind() failed)";
	    break;

	case OBMinorListen:
	    msg += " (listen() failed)";
	    break;

	case OBMinorConnect:
	    msg += " (connect() failed)";
	    break;

	case OBMinorAccept:
	    msg += " (accept() failed)";
	    break;

	case OBMinorSelect:
	    msg += " (select() failed)";
	    break;

	case OBMinorGethostname:
	    msg += " (gethostname() failed)";
	    break;

	case OBMinorGethostbyname:
	    msg += " (gethostbyname() failed)";
	    break;

	case OBMinorWSAStartup:
	    msg += " (WSAStartup() failed)";
	    break;

	case OBMinorWSACleanup:
	    msg += " (WSACleanup() failed)";
	    break;

	case OBMinorNoGIOP:
	    msg += " (not a GIOP message)";
	    break;

	case OBMinorUnknownMessage:
	    msg += " (unknown GIOP message)";
	    break;

	case OBMinorWrongMessage:
	    msg += " (wrong GIOP message)";
	    break;

	case OBMinorCloseConnection:
	    msg += " (got a `close connection' message)";
	    break;

	case OBMinorMessageError:
	    msg += " (got a `message error' message)";
	    break;
	}
    }

    const CORBA_INTF_REPOS* intfRepos =
	CORBA_INTF_REPOS::_narrow((CORBA_SystemException*)&ex);

    if(intfRepos)
    {
	switch(intfRepos -> minor())
	{
	case OBMinorNoIntfRepos:
	    msg += " (interface repository is not available)";
	    break;

	case OBMinorIdExists:
	    msg += " (repository id already exists)";
	    break;

	case OBMinorNameExists:
            msg += " (name already exists)";
	    break;

	case OBMinorRepositoryDestroy:
	    msg += " (destroy() invoked on Repository object)";
	    break;

	case OBMinorPrimitiveDefDestroy:
	    msg += " (destroy() invoked on PrimitiveDef object)";
	    break;

	case OBMinorAttrExists:
            msg += " (attribute is already defined in a base interface)";
	    break;

	case OBMinorOperExists:
            msg += " (operation is already defined in a base interface)";
	    break;

	case OBMinorLookupAmbiguous:
	    msg += " (search name for lookup() is ambiguous)";
	    break;

	case OBMinorAttrAmbiguous:
	    msg += " (attribute name collisions in base interfaces)";
	    break;

	case OBMinorOperAmbiguous:
	    msg += " (operation name collisions in base interfaces)";
	    break;

	case OBMinorIllegalRecursion:
	    msg += " (illegal recursion)";
	    break;
	}
    }

    const CORBA_BAD_PARAM* badParam =
	CORBA_BAD_PARAM::_narrow((CORBA_SystemException*)&ex);

    if(badParam)
    {
	switch(badParam -> minor())
	{
	case OBMinorBadSchemeName:
	    msg += " (bad scheme name)";
	    break;

	case OBMinorBadAddress:
	    msg += " (bad address)";
	    break;

	case OBMinorBadSchemeSpecificPart:
            msg += " (bad scheme specific part)";
	    break;

	case OBMinorOther:
	    msg += " (other)";
	    break;
	}
    }

    return msg._retn();
}

// ----------------------------------------------------------------------
// Print octets for debug reasons
// ----------------------------------------------------------------------

void
OBPrintOctets(ostream& out, const CORBA_Octet* oct, CORBA_ULong count)
{
    static const CORBA_ULong inc = 8;
    
    CORBA_ULong j;

    for(j = 0 ; j < count ; j += inc)
    {
	CORBA_ULong k;

	for(k = j ; k - j < inc ; k++)
	{
	    if(k < count)
	    {
		out.width(3);
		out << (int)oct[k] << ' ';
	    }
	    else
		out << "    ";
	}

	out << '"';
	
	for(k = j ; k < count && k - j < inc ; k++)
	{
	    if(isprint((char)oct[k]))
		out << (char)oct[k];
	    else
		out << '.';
	}
	
	out << '"' << endl;
    }
}

// ----------------------------------------------------------------------
// Print object reference
// ----------------------------------------------------------------------

void
OBPrintObjref(ostream& out, CORBA_Object_ptr p)
{
    const IOP_IOR& ior = p -> _OB_ior();
    OBPrintObjref(out, ior);
}

void
OBPrintObjref(ostream& out, const char* s)
{
    assert_nca(s, OBNCANullString);
    
    if(strncmp(s, "IOR:", 4) != 0)
	throw CORBA_INV_OBJREF();
    
    s += 4;
    
    CORBA_Octet* const buf = OBAsciiToOctets(s);
    const CORBA_Octet* oct = buf;
    
    CORBA_Boolean endian;
    OBUnmarshal(endian, oct, false);
    
    IOP_IOR ior;
    OBUnmarshal(ior, oct, endian != OBEndian);
    
    delete [] buf;

    out << "byteorder: " << (endian ? "little" : "big") << " endian\n";

    OBPrintObjref(out, ior);
}

void
OBPrintObjref(ostream& out, const IOP_IOR& ior)
{
    out << "type_id: " << ior.type_id << '\n';

    CORBA_ULong	OBIIOPPrintProfileBody(ostream&, const IOP_IOR&);
    CORBA_ULong n = OBIIOPPrintProfileBody(out, ior);
    if(n == 0)
	out << "(no IIOP tag found)" << '\n';

    out << flush;
}
