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

#include <OB/Basic.h>
#include <OB/Except.h>
#include <OB/Util.h>

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

#ifdef HAVE_STRSTREAM
#   include <strstream>
#else
#   if defined(HAVE_STRSTREA_H)
#       include <strstrea.h>
#   else
#       include <strstream.h>
#   endif
#endif

//
// The ORBacus version, as string and as integer
//
const char* OBVersion = OB_VERSION;
unsigned long OBIntegerVersion = OB_INTEGER_VERSION;

// ----------------------------------------------------------------------
// Helper function for assert_nca()
// ----------------------------------------------------------------------

void
OBNCA(OBNCAReason reason)
{
    //
    // Update the documentation if error messages are changed here !!!!
    //

    const char* err = 0;

    switch(reason)
    {
    case OBNCANotImpl:
	err =
"Application requested a feature that is not yet implemented";
	break;

    case OBNCAWrongAlloc:
	err =
"Application used wrong memory allocation function";
	break;

    case OBNCADoubleFree:
	err =
"Memory that was already deallocated was deallocated again";
	break;

    case OBNCARefCountNotZero:
	err =
"Object was deleted without an object reference count of zero";
	break;

    case OBNCARefCountZero:
	err =
"Object was already deleted (object reference count was already zero)";
	break;

    case OBNCASeqLenGrMax:
	err =
"Sequence length was greater than maximum sequence length";
	break;

    case OBNCASeqRange:
	err =
"Index for sequence operator[]() or remove() function was out of range";
	break;

    case OBNCANullInit:
	err =
"Null pointer was used to initialize T_var type";
	break;

    case OBNCANullPtr:
	err =
"operator->() was used on null pointer or nil object reference";
	break;

    case OBNCANullDeref:
	err =
"Application tried to dereference a null pointer";
	break;

    case OBNCANullString:
	err =
"Null pointer was passed as string parameter or return value";
	break;

    case OBNCASelfAssign:
	err =
"Self assignment caused a dangling pointer";
	break;

    case OBNCAAnySelfReplace:
	err =
"Replacement of Any content by its own value caused a dangling pointer";
	break;

    case OBNCADiscType:
	err =
"Invalid union discriminator type used";
	break;

    case OBNCADiscMismatch:
	err =
"Union discriminator mismatch";
	break;

    case OBNCAUnionInit:
	err =
"Uninitialized union used";
	break;

    case OBNCADynamicAsStatic:
	err =
"Dynamic implementation object cannot be used as static implementation object";
	break;
    }
    
    assert(err);

    OBMessageViewer* viewer = OBMessageViewer::instance();
    viewer -> nca(err);
}

// ----------------------------------------------------------------------
// Special ORBacus memory allocation that keeps track of the
// number of elements in a buffer
// ----------------------------------------------------------------------

static const size_t more = sizeof(CORBA_ULong) * 2;

#ifndef NDEBUG
static const CORBA_ULong magic     = 0x02468ACE;
static const CORBA_ULong magicFree = 0x0E2C4A68;
#endif

void*
OBAlloc(size_t sz, CORBA_ULong n)
{
#ifndef NDEBUG

    char* p = (char*)malloc(more + n * sz);
    
    CORBA_ULong* np = (CORBA_ULong*)p;
    *np++ = magic;
    *np = n;

#else

    char* p = (char*)malloc(more + n * sz);

    CORBA_ULong* np = (CORBA_ULong*)p;
    *np = n;

#endif

    return p + more;
}

void
OBFree(void* vp)
{
    char* p = (char*)vp;

#ifndef NDEBUG

    CORBA_ULong* magicp = (CORBA_ULong*)(p - more);
    assert_nca(*magicp != magicFree, OBNCADoubleFree);
    assert_nca(*magicp == magic, OBNCAWrongAlloc);
    *magicp = magicFree;

#endif

    free(p - more);
}

CORBA_ULong
OBAllocated(void* vp)
{
    char* p = (char*)vp;

#ifndef NDEBUG

    CORBA_ULong* magicp = (CORBA_ULong*)(p - more);
    assert_nca(*magicp == magic, OBNCAWrongAlloc);

    CORBA_ULong* np = magicp + 1;

#else

    CORBA_ULong* np = (CORBA_ULong*)(p - more);

#endif

    return *np;
}

#ifndef NDEBUG

bool
OBAllocCheck(const void* vp)
{
    const char* p = (const char*)vp;
    const CORBA_ULong* magicp = (const CORBA_ULong*)(p - more);
    return *magicp == magic;
}

#endif

// ----------------------------------------------------------------------
// Marshal/unmarshal functions
// ----------------------------------------------------------------------

void
OBMarshal(CORBA_Short val, CORBA_Octet*& oct)
{
    oct += (unsigned long)oct % 2;
    *(CORBA_Short*)oct = val;
    oct += 2;
}

void
OBMarshalMulti(const CORBA_Short* val, CORBA_Octet*& oct, CORBA_ULong n)
{
    if(n)
    {
	oct += (unsigned long)oct % 2;
	memcpy(oct, val, n * 2);
	oct += n * 2;
    }
}

void
OBMarshalCount(CORBA_Short, CORBA_ULong& count)
{
    count += count % 2 + 2;
}

void
OBMarshalCountMulti(const CORBA_Short*, CORBA_ULong& count, CORBA_ULong n)
{
    if(n)
	count += count % 2 + n * 2;
}

void
OBUnmarshal(CORBA_Short& val, const CORBA_Octet*& oct, bool swap)
{
    oct += (unsigned long)oct % 2;
    val = *(const CORBA_Short*)oct;
    oct += 2;

    if(swap)
    {
	CORBA_UShort& v = (CORBA_UShort&)val;
	v = (v << 8) | (v >> 8);
    }
}

void
OBUnmarshalMulti(CORBA_Short* val, const CORBA_Octet*& oct, bool swap,
		 CORBA_ULong n)
{
    if(n)
    {
	oct += (unsigned long)oct % 2;
	memcpy(val, oct, n * 2);
	oct += n * 2;

	if(swap)
	{
	    CORBA_UShort* v = (CORBA_UShort*)val;
	    while(n-- > 0)
	    {
		*v = (*v << 8) | (*v >> 8);
		v++;
	    }
	}
    }
}

void
OBMarshal(CORBA_Long val, CORBA_Octet*& oct)
{
    oct += 3;
    oct -= (unsigned long)oct % 4;
    *(CORBA_Long*)oct = val;
    oct += 4;
}

void
OBMarshalMulti(const CORBA_Long* val, CORBA_Octet*& oct, CORBA_ULong n)
{
    if(n)
    {
	oct += 3;
	oct -= (unsigned long)oct % 4;
	memcpy(oct, val, n * 4);
	oct += n * 4;
    }
}

void
OBMarshalCount(CORBA_Long, CORBA_ULong& count)
{
    count += 3;
    count -= count % 4;
    count += 4;
}

void
OBMarshalCountMulti(const CORBA_Long*, CORBA_ULong& count, CORBA_ULong n)
{
    if(n)
    {
	count += 3;
	count -= count % 4;
	count += n * 4;
    }
}

void
OBUnmarshal(CORBA_Long& val, const CORBA_Octet*& oct, bool swap)
{
    oct += 3;
    oct -= (unsigned long)oct % 4;
    val = *(const CORBA_Long*)oct;
    oct += 4;

    if(swap)
    {
	CORBA_ULong& v = (CORBA_ULong&)val;
	v = ((v << 24) | ((v & 0xff00) << 8)
	     | ((v >> 8) & 0xff00) | (v >> 24));
    }
}

void
OBUnmarshalMulti(CORBA_Long* val, const CORBA_Octet*& oct, bool swap,
		 CORBA_ULong n)
{
    if(n)
    {
	oct += 3;
	oct -= (unsigned long)oct % 4;
	memcpy(val, oct, n * 4);
	oct += n * 4;

	if(swap)
	{
	    CORBA_ULong* v = (CORBA_ULong*)val;
	    while(n-- > 0)
	    {
		*v = ((*v << 24) | ((*v & 0xff00) << 8)
		      | ((*v >> 8) & 0xff00) | (*v >> 24));
		v++;
	    }
	}
    }
}

void
OBMarshal(CORBA_UShort val, CORBA_Octet*& oct)
{
    oct += (unsigned long)oct % 2;
    *(CORBA_UShort*)oct = val;
    oct += 2;
}

void
OBMarshalMulti(const CORBA_UShort* val, CORBA_Octet*& oct, CORBA_ULong n)
{
    if(n)
    {
	oct += (unsigned long)oct % 2;
	memcpy(oct, val, n * 2);
	oct += n * 2;
    }
}

void
OBMarshalCount(CORBA_UShort, CORBA_ULong& count)
{
    count += count % 2 + 2;
}

void
OBMarshalCountMulti(const CORBA_UShort*, CORBA_ULong& count, CORBA_ULong n)
{
    if(n)
	count += count % 2 + n * 2;
}

void
OBUnmarshal(CORBA_UShort& val, const CORBA_Octet*& oct, bool swap)
{
    oct += (unsigned long)oct % 2;
    val = *(const CORBA_UShort*)oct;
    oct += 2;

    if(swap)
	val = (val << 8) | (val >> 8);
}

void
OBUnmarshalMulti(CORBA_UShort* val, const CORBA_Octet*& oct, bool swap,
		 CORBA_ULong n)
{
    if(n)
    {
	oct += (unsigned long)oct % 2;
	memcpy(val, oct, n * 2);
	oct += n * 2;

	if(swap)
	    while(n-- > 0)
	    {
		*val = (*val << 8) | (*val >> 8);
		val++;
	    }
    }
}

void
OBMarshal(CORBA_ULong val, CORBA_Octet*& oct)
{
    oct += 3;
    oct -= (unsigned long)oct % 4;
    *(CORBA_ULong*)oct = val;
    oct += 4;
}

void
OBMarshalMulti(const CORBA_ULong* val, CORBA_Octet*& oct, CORBA_ULong n)
{
    if(n)
    {
	oct += 3;
	oct -= (unsigned long)oct % 4;
	memcpy(oct, val, n * 4);
	oct += n * 4;
    }
}

void
OBMarshalCount(CORBA_ULong, CORBA_ULong& count)
{
    count += 3;
    count -= count % 4;
    count += 4;
}

void
OBMarshalCountMulti(const CORBA_ULong*, CORBA_ULong& count, CORBA_ULong n)
{
    if(n)
    {
	count += 3;
	count -= count % 4;
	count += n * 4;
    }
}

void
OBUnmarshal(CORBA_ULong& val, const CORBA_Octet*& oct, bool swap)
{
    oct += 3;
    oct -= (unsigned long)oct % 4;
    val = *(const CORBA_ULong*)oct;
    oct += 4;

    if(swap)
	val = ((val << 24) | ((val & 0xff00) << 8)
	       | ((val >> 8) & 0xff00) | (val >> 24));
}

void
OBUnmarshalMulti(CORBA_ULong* val, const CORBA_Octet*& oct, bool swap,
		 CORBA_ULong n)
{
    if(n)
    {
	oct += 3;
	oct -= (unsigned long)oct % 4;
	memcpy(val, oct, n * 4);
	oct += n * 4;

	if(swap)
	    while(n-- > 0)
	    {
		*val = ((*val << 24) | ((*val & 0xff00) << 8)
			| ((*val >> 8) & 0xff00) | (*val >> 24));
		val++;
	    }
    }
}

void
OBMarshal(CORBA_Float val, CORBA_Octet*& oct)
{
    oct += 3;
    oct -= (unsigned long)oct % 4;
    *(CORBA_Float*)oct = val;
    oct += 4;
}

void
OBMarshalMulti(const CORBA_Float* val, CORBA_Octet*& oct, CORBA_ULong n)
{
    if(n)
    {
	oct += 3;
	oct -= (unsigned long)oct % 4;
	memcpy(oct, val, n * 4);
	oct += n * 4;
    }
}

void
OBMarshalCount(CORBA_Float, CORBA_ULong& count)
{
    count += 3;
    count -= count % 4;
    count += 4;
}

void
OBMarshalCountMulti(const CORBA_Float*, CORBA_ULong& count, CORBA_ULong n)
{
    if(n)
    {
	count += 3;
	count -= count % 4;
	count += n * 4;
    }
}

void
OBUnmarshal(CORBA_Float& val, const CORBA_Octet*& oct, bool swap)
{
    oct += 3;
    oct -= (unsigned long)oct % 4;
    val = *(const CORBA_Float*)oct;
    oct += 4;

    if(swap)
    {
	CORBA_ULong* v = (CORBA_ULong*)&val;
	*v = ((*v << 24) | ((*v & 0xff00) << 8)
	      | ((*v >> 8) & 0xff00) | (*v >> 24));
    }
}

void
OBUnmarshalMulti(CORBA_Float* val, const CORBA_Octet*& oct, bool swap, 
		 CORBA_ULong n)
{
    if(n)
    {
	oct += 3;
	oct -= (unsigned long)oct % 4;
	memcpy(val, oct, n * 4);
	oct += n * 4;

	if(swap)
	{
	    CORBA_ULong* v = (CORBA_ULong*)val;
	    while(n-- > 0)
	    {
		*v = ((*v << 24) | ((*v & 0xff00) << 8)
		      | ((*v >> 8) & 0xff00) | (*v >> 24));
		v++;
	    }
	}
    }
}

void
OBMarshal(CORBA_Double val, CORBA_Octet*& oct)
{
    oct += 7;
    oct -= (unsigned long)oct % 8;
    *(CORBA_Double*)oct = val;
    oct += 8;
}

void
OBMarshalMulti(const CORBA_Double* val, CORBA_Octet*& oct, CORBA_ULong n)
{
    if(n)
    {
	oct += 7;
	oct -= (unsigned long)oct % 8;
	memcpy(oct, val, n * 8);
	oct += n * 8;
    }
}

void
OBMarshalCount(CORBA_Double, CORBA_ULong& count)
{
    count += 7;
    count -= count % 8;
    count += 8;
}

void
OBMarshalCountMulti(const CORBA_Double*, CORBA_ULong& count, CORBA_ULong n)
{
    if(n)
    {
	count += 7;
	count -= count % 8;
	count += n * 8;
    }
}

void
OBUnmarshal(CORBA_Double& val, const CORBA_Octet*& oct, bool swap)
{
    oct += 7;
    oct -= (unsigned long)oct % 8;
    val = *(const CORBA_Double*)oct;
    oct += 8;

    if(swap)
    {
	CORBA_ULong v0 = ((CORBA_ULong*)&val)[0];
	CORBA_ULong v1 = ((CORBA_ULong*)&val)[1];
	v0 = ((v0 << 24) | ((v0 & 0xff00) << 8)
	      | ((v0 >> 8) & 0xff00) | (v0 >> 24));
	v1 = ((v1 << 24) | ((v1 & 0xff00) << 8)
	      | ((v1 >> 8) & 0xff00) | (v1 >> 24));
	((CORBA_ULong*)&val)[0] = v1;
	((CORBA_ULong*)&val)[1] = v0;
    }
}

void
OBUnmarshalMulti(CORBA_Double* val, const CORBA_Octet*& oct, bool swap,
		 CORBA_ULong n)
{
    if(n)
    {
	oct += 7;
	oct -= (unsigned long)oct % 8;
	memcpy(val, oct, n * 8);
	oct += n * 8;

	if(swap)
	{
	    CORBA_ULong* v = (CORBA_ULong*)val;
	    while(n-- > 0)
	    {
		CORBA_ULong v0 = v[0];
		CORBA_ULong v1 = v[1];
		v0 = ((v0 << 24) | ((v0 & 0xff00) << 8)
		      | ((v0 >> 8) & 0xff00) | (v0 >> 24));
		v1 = ((v1 << 24) | ((v1 & 0xff00) << 8)
		      | ((v1 >> 8) & 0xff00) | (v1 >> 24));
		v[0] = v1;
		v[1] = v0;
		v += 2;
	    }
	}
    }
}

void
OBMarshal(const char* s, CORBA_Octet*& oct)
{
    assert_nca(s, OBNCANullString);
    
    CORBA_ULong len = strlen(s) + 1;
    OBMarshal(len, oct);

    char* p = (char*)oct;
    strcpy(p, s);

    oct += len;
}

void
OBMarshalCount(const char* s, CORBA_ULong& count)
{
    assert_nca(s, OBNCANullString);

    CORBA_ULong len = strlen(s) + 1;
    OBMarshalCount(len, count);
    count += len;
}

void
OBUnmarshal(char*& s, const CORBA_Octet*& oct, bool swap)
{
    CORBA_ULong len;
    OBUnmarshal(len, oct, swap);

    if(len == 0)
 	throw CORBA_MARSHAL();

    CORBA_string_free(s);
    s = CORBA_string_alloc(len - 1);
    
    const char* p = (const char*)oct;
    strcpy(s, p);

    if(strlen(s) + 1 != len)
 	throw CORBA_MARSHAL();
    
    oct += len;
}

// ----------------------------------------------------------------------
// Convert between octet sequence and ascii string
// ----------------------------------------------------------------------

char*
OBOctetsToAscii(const CORBA_Octet* oct, CORBA_ULong len)
{
    char* str = CORBA_string_alloc(len * 2);
    char s[3];
    
    for(CORBA_ULong i = 0 ; i < len ; i++, oct++)
    {
	sprintf(s, "%02x", *oct);
	strcpy(&str[i * 2], s);
    }

    return str;
}

CORBA_Octet*
OBAsciiToOctets(const char* str)
{
    CORBA_ULong len = strlen(str) / 2;
    CORBA_Octet* oct = new CORBA_Octet[len];

    int val;
    char hex[3];
    hex[2] = '\0';

    const char* s = str;
    for(CORBA_ULong i = 0 ; i < len ; i++)
    {
	hex[0] = *s++;
	hex[1] = *s++;
	sscanf(hex, "%x", &val);
	oct[i] = val;
    }

    return oct;
}

// ----------------------------------------------------------------------
// String memory management
// ----------------------------------------------------------------------

char*
CORBA_string_alloc(CORBA_ULong len)
{
    return (char*)OBAlloc(sizeof(char), len + 1);
}

void
CORBA_string_free(char* p)
{
    if(p)
	OBFree(p);
}

CORBA_ULong
CORBA_string_allocated(char* p)
{
    if(p)
	return OBAllocated(p) - 1;
    else
	return 0;
}

#ifndef NDEBUG

CORBA_Boolean
CORBA_string_check(const char* p)
{
    if(p)
	return OBAllocCheck(p);
    else
	return true;
}

#endif

char*
CORBA_string_dup(const char* p)
{
    if(p)
    {
	char* ret = CORBA_string_alloc(strlen(p));
	strcpy(ret, p);
	return ret;
    }
    else
	return 0;
}

// ----------------------------------------------------------------------
// String_var type
// ----------------------------------------------------------------------

CORBA_String_var::CORBA_String_var(char* p)
    : ptr_(p)
{
    assert_nca(p, OBNCANullInit);
    assert_nca(CORBA_string_check(p), OBNCAWrongAlloc);
}

CORBA_String_var::CORBA_String_var(const CORBA_String_var& r)
    : ptr_(CORBA_string_dup(r.ptr_))
{
}

CORBA_String_var::CORBA_String_var(const OBStrForSeq& r)
    : ptr_(CORBA_string_dup(r))
{
}

CORBA_String_var&
CORBA_String_var::operator=(char* p)
{
    assert_nca(CORBA_string_check(p), OBNCAWrongAlloc);
    assert_nca(ptr_ == 0 || p != ptr_, OBNCASelfAssign);
    CORBA_string_free(ptr_);
    ptr_ = p;

    return *this;
}

CORBA_String_var&
CORBA_String_var::operator=(const char* p)
{
    if(p != ptr_)
    {
	CORBA_string_free(ptr_);
	ptr_ = CORBA_string_dup(p);
    }

    return *this;
}

CORBA_String_var&
CORBA_String_var::operator=(const CORBA_String_var& r)
{
    if(r.ptr_ != ptr_)
    {
	CORBA_string_free(ptr_);
	ptr_ = CORBA_string_dup(r.ptr_);
    }

    return *this;
}

CORBA_String_var&
CORBA_String_var::operator=(const OBStrForSeq& r)
{
    if(r.in() != ptr_)
    {
	CORBA_string_free(ptr_);
	ptr_ = CORBA_string_dup(r);
    }

    return *this;
}

char*&
CORBA_String_var::out()
{
    CORBA_string_free(ptr_);
    ptr_ = 0;
    return ptr_;
}

char*
CORBA_String_var::_retn()
{
    char* ret = ptr_;
    ptr_ = 0;
    return ret;
}

CORBA_String_var&
CORBA_String_var::operator+=(const char* p)
{
    if(p)
    {
	if(ptr_)
	{
	    CORBA_ULong max = CORBA_string_allocated(ptr_);
	    CORBA_ULong newLen = strlen(ptr_) + strlen(p);

	    if(newLen > max)
	    {
		max = newLen > max * 2 ? newLen : max * 2;
		char* oldPtr = ptr_;
		ptr_ = CORBA_string_alloc(max);
		strcpy(ptr_, oldPtr);
		CORBA_string_free(oldPtr);
	    }

	    strcat(ptr_, p);
	}
	else
	    ptr_ = CORBA_string_dup(p);
    }

    return *this;
}

CORBA_String_var&
CORBA_String_var::operator+=(char c)
{
    char s[2];
    s[0] = c;
    s[1] = '\0';
    return operator+=(s);
}

CORBA_String_var&
CORBA_String_var::operator+=(unsigned char c)
{
    return operator+=((char)c);
}

CORBA_String_var&
CORBA_String_var::operator+=(long n)
{
    char str[1024];
    ostrstream s(str, 1024);
    s << n << '\0';
    assert(s.good());
    return operator+=(str);
}

CORBA_String_var&
CORBA_String_var::operator+=(unsigned long n)
{
    char str[1024];
    ostrstream s(str, 1024);
    s << n << '\0';
    assert(s.good());
    return operator+=(str);
}

// ----------------------------------------------------------------------
// String type for string sequences
// ----------------------------------------------------------------------

OBStrForSeq&
OBStrForSeq::operator=(char* p)
{
    assert_nca(CORBA_string_check(p), OBNCAWrongAlloc);
    assert_nca(p != *ptr_, OBNCASelfAssign);

    if(rel_)
	CORBA_string_free(*ptr_);

    *ptr_ = p;

    return *this;
}

OBStrForSeq&
OBStrForSeq::operator=(const char* p)
{
    if(p != *ptr_)
    {
	if(rel_)
	    CORBA_string_free(*ptr_);

	*ptr_ = CORBA_string_dup(p);
    }

    return *this;
}

OBStrForSeq&
OBStrForSeq::operator=(const CORBA_String_var& r)
{
    if(r.in() != *ptr_)
    {
	if(rel_)
	    CORBA_string_free(*ptr_);

	*ptr_ = CORBA_string_dup(r);
    }

    return *this;
}

OBStrForSeq&
OBStrForSeq::operator=(const OBStrForSeq& r)
{
    if(*(r.ptr_) != *ptr_)
    {
	if(rel_)
	    CORBA_string_free(*ptr_);

	*ptr_ = CORBA_string_dup(*(r.ptr_));
    }

    return *this;
}

char*&
OBStrForSeq::out()
{
    if(rel_)
	CORBA_string_free(*ptr_);
    *ptr_ = 0;
    return *ptr_;
}

char*
OBStrForSeq::_retn()
{
    char* ret = *ptr_;
    *ptr_ = 0;
    return ret;
}

// ----------------------------------------------------------------------
// CORBA_String_var and OBStrForSeq output on output stream
// ----------------------------------------------------------------------

ostream&
operator<<(ostream& out, const CORBA_String_var& s)
{
    return out << s.in();
}

ostream&
operator<<(ostream& out, const OBStrForSeq& s)
{
    return out << s.in();
}

istream&
operator>>(istream& in, CORBA_String_var& s)
{
    return in >> s.inout();
}

istream&
operator>>(istream& in, OBStrForSeq& s)
{
    return in >> s.inout();
}
