#include <CORBA.h>
#include <ctype.h>
#if !defined(__sgi) || !defined(__GNUG__)
// conflict for initstate()
#include <math.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream.h>
#include <mico/util.h>
#include <mico/template_impl.h>
#include "subtype.h"


inline int
check (int expr, const char *msg)
{
    if (!expr) {
	cout << msg << ": failed" << endl;
	return 1;
    }
    return 0;
}

const char *
extract (CORBA::Any &a, char *types, int cond = 1, const char *val = "")
{
    for ( ; *types; ++types) switch (*types) {
    case 's': {
	CORBA::Short o;
	if ((a >>= o) != cond || (cond && o != (CORBA::Short)atol (val)))
	    return "Short";
	break;
    }
    case 'S': {
	CORBA::UShort o;
	if ((a >>= o) != cond || (cond && o != (CORBA::UShort)atol (val)))
	    return "UShort";
	break;
    }
    case 'l': {
	CORBA::Long o;
	if ((a >>= o) != cond || (cond && o != (CORBA::Long)atol (val)))
	    return "Long";
	break;
    }
    case 'L': {
	CORBA::ULong o;
	if ((a >>= o) != cond || (cond && o != (CORBA::ULong)atol (val)))
	    return "ULong";
	break;
    }
    case 'x': {
	CORBA::LongLong o;
	if ((a >>= o) != cond || (cond && o != (CORBA::LongLong)atol (val)))
	    return "LongLong";
	break;
    }
    case 'X': {
	CORBA::ULongLong o;
	if ((a >>= o) != cond || (cond && o != (CORBA::ULongLong)atol (val)))
	    return "ULongLong";
	break;
    }
    case 'f': {
	CORBA::Float o;
	CORBA::Float exp = atof (val);
	
	if ((a >>= o) != cond || (cond && o != exp))
	    return "Float";
	break;
    }
    case 'd': {
	CORBA::Double o;
	CORBA::Double exp = atof (val);
	if ((a >>= o) != cond || (cond && o != exp))
	    return "Double";
	break;
    }
    case 'D': {
	CORBA::LongDouble o;
	CORBA::LongDouble exp = atof (val);
	if ((a >>= o) != cond || (cond && o != exp))
	    return "LongDouble";
	break;
    }
    case 'o': {
	CORBA::Octet o;
	if ((a >>= CORBA::Any::to_octet (o)) != cond ||
	    (cond && o != (CORBA::Octet)atol (val)))
	    return "Octet";
	break;
    }
    case 'b': {
	CORBA::Boolean o;
	if ((a >>= CORBA::Any::to_boolean (o)) != cond ||
	    (cond && o != (CORBA::Boolean)atol (val)))
	    return "Boolean";
	break;
    }
    case 'c': {
	CORBA::Char o;
	if ((a >>= CORBA::Any::to_char (o)) != cond ||
	    (cond && o != (CORBA::Char)atol (val)))
	    return "Char";
	break;
    }
    case 'w': {
	CORBA::WChar o;
	if ((a >>= CORBA::Any::to_wchar (o)) != cond ||
	    (cond && o != (CORBA::WChar)atol (val)))
	    return "WChar";
	break;
    }
    case 't': {
	char *o = 0;
	int ret = ((a >>= o) != cond || (cond && strcmp (o, val)));
	if (o)
	    CORBA::string_free (o);
	if (ret)
	    return "String";
	break;
    }
    case 'T': {
	assert (isdigit (types[1]));
	CORBA::ULong len = strtol (types+1, &types, 10);
	--types;
	char *o = 0;
	int ret = ((a >>= CORBA::Any::to_string (o, len)) != cond ||
		   (cond && strcmp (o, val)));
	if (o)
	    CORBA::string_free (o);
	if (ret)
	    return "BoundedString";
	break;
    }
    case 'u': {
	CORBA::WChar *o = 0;
	int ret = ((a >>= o) != cond ||
		   (cond && xwcscmp (o, (CORBA::WChar *)val)));
	if (o)
	    CORBA::wstring_free (o);
	if (ret)
	    return "WString";
	break;
    }
    case 'U': {
	assert (isdigit (types[1]));
	CORBA::ULong len = strtol (types+1, &types, 10);
	--types;
	CORBA::WChar *o = 0;
	int ret = ((a >>= CORBA::Any::to_wstring (o, len)) != cond ||
		   (cond && xwcscmp (o, (CORBA::WChar *)val)));
	if (o)
	    CORBA::wstring_free (o);
	if (ret)
	    return "BoundedWString";
	break;
    }
    default:
	assert (0);
    }
    return 0;
}

/****************************** Basic Types *******************************/

int
test_basic ()
{
    int res = 0;

    CORBA::Any a;

    CORBA::Short s = 1234;
    res += check ((a <<= s) &&
		  !extract (a, "slxfdD", 1, "1234") &&
		  !extract (a, "slxfdD", 1, "1234") &&
		  !extract (a, "SLXcwobtu", 0),
		  "Short");

    CORBA::UShort us = 4321;
    res += check ((a <<= us) &&
		  !extract (a, "SlLxXfdD", 1, "4321") &&
		  !extract (a, "SlLxXfdD", 1, "4321") &&
		  !extract (a, "scwobtu", 0),
		  "UShort");

    CORBA::Long l = 12345678;
    res += check ((a <<= l) &&
		  !extract (a, "lxdD", 1, "12345678") &&
		  !extract (a, "lxdD", 1, "12345678") &&
		  !extract (a, "sSLXfcwobtu", 0),
		  "Long");

    CORBA::LongLong ll = 12345678;
    res += check ((a <<= ll) &&
		  !extract (a, "xD", 1, "12345678") &&
		  !extract (a, "xD", 1, "12345678") &&
		  !extract (a, "sSlLXfcwobtu", 0),
		  "LongLong");

    CORBA::ULong ul = 87654321;
    res += check ((a <<= ul) &&
		  !extract (a, "LxXdD", 1, "87654321") &&
		  !extract (a, "LxXdD", 1, "87654321") &&
		  !extract (a, "sSlfcwobtu", 0),
		  "ULong");

    CORBA::ULongLong ull = 87654321;
    res += check ((a <<= ull) &&
		  !extract (a, "XD", 1, "87654321") &&
		  !extract (a, "XD", 1, "87654321") &&
		  !extract (a, "sSlLfdcwobtu", 0),
		  "ULongLong");

    CORBA::Float f = 8.125;
    res += check ((a <<= f) &&
		  !extract (a, "fdD", 1, "8.125") &&
		  !extract (a, "fdD", 1, "8.125") &&
		  !extract (a, "sSlLxXcwobtu", 0),
		  "Float");

    CORBA::Double d = 16.0625;
    res += check ((a <<= d) &&
		  !extract (a, "dD", 1, "16.0625") &&
		  !extract (a, "dD", 1, "16.0625") &&
		  !extract (a, "sSxXlLfcwobtu", 0),
		  "Double");

    CORBA::LongDouble dd = 16.0625;
    res += check ((a <<= dd) &&
		  !extract (a, "D", 1, "16.0625") &&
		  !extract (a, "D", 1, "16.0625") &&
		  !extract (a, "sSxXlLfdcwobtu", 0),
		  "LongDouble");

    CORBA::Char c = 21;
    res += check ((a <<= CORBA::Any::from_char(c)) &&
		  !extract (a, "c", 1, "21") &&
		  !extract (a, "c", 1, "21") &&
		  !extract (a, "sSlLfdobt", 0),
		  "Char");

    CORBA::WChar w = 21;
    res += check ((a <<= CORBA::Any::from_wchar(w)) &&
		  !extract (a, "w", 1, "21") &&
		  !extract (a, "w", 1, "21") &&
		  !extract (a, "sSlLxXfdDocbtu", 0),
		  "WChar");

    CORBA::Octet o = 42;
    res += check ((a <<= CORBA::Any::from_octet(o)) &&
		  !extract (a, "o", 1, "42") &&
		  !extract (a, "o", 1, "42") &&
		  !extract (a, "sSlLfdcbt", 0),
		  "Octet");

    CORBA::Boolean b = 1;
    res += check ((a <<= CORBA::Any::from_boolean(b)) &&
		  !extract (a, "b", 1, "1") &&
		  !extract (a, "b", 1, "1") &&
		  !extract (a, "sSlLfdcot", 0),
		  "Boolean");
    return res;
}

/******************************** Fixed ********************************/

CORBA::Boolean
operator<<= (CORBA::Any &a, const Fixed<4,2> &f)
{
    FixedBase::FixedValue_var val = f.to_digits();
    return (a <<= CORBA::Any::from_fixed (val, 4, 2));
}

CORBA::Boolean
operator>>= (const CORBA::Any &a, Fixed<4,2> &f)
{
    FixedBase::FixedValue_var val;
    if (!(a >>= CORBA::Any::to_fixed (val, 4, 2)))
	return FALSE;
    f.from_digits (val);
    return TRUE;
}

int
test_fixed ()
{
    Fixed <4,2> f1, f2 = (CORBA::LongDouble)42.42;
    CORBA::Any a;

    if (!(a <<= f2))
	return 1;
    if (!(a >>= f1))
	return 1;
    return (!(f1 == f2));
}

/******************************** String ********************************/

int
test_string ()
{
    int res = 0;
    CORBA::Any a;

    char *i = "foobarbaz";
    res += check ((a <<= i) &&
		  !extract (a, "t", 1, i) &&
		  !extract (a, "T0", 1, i) &&
		  !extract (a, "sSlLfdcob", 0),
		  "unbounded string 1");

    i = "foobazbar";
    res += check ((a <<= CORBA::Any::from_string(i,0)) &&
		  !extract (a, "T0", 1, i) &&
		  !extract (a, "t", 1, i) &&
		  !extract (a, "sSlLfdcob", 0),
		  "unbounded string 2");

    i = "barfoobaz";
    res += check ((a <<= CORBA::Any::from_string(i,20)) &&
		  !extract (a, "T20", 1, i) &&
		  !extract (a, "T20", 1, i) &&
		  !extract (a, "sSlLfdcobtT10", 0),
		  "bounded string");
    return res;
}

int
test_wstring ()
{
    int res = 0;
    CORBA::Any a;

    CORBA::WChar *i = L"AB";

    res += check ((a <<= i) &&
		  !extract (a, "u", 1, (char *)i) &&
		  !extract (a, "U0", 1, (char *)i) &&
		  !extract (a, "sSlLxXfdDcwobt", 0),
		  "unbounded wstring 1");

    res += check ((a <<= CORBA::Any::from_wstring(i,0)) &&
		  !extract (a, "U0", 1, (char *)i) &&
		  !extract (a, "u", 1, (char *)i) &&
		  !extract (a, "sSlLxXfdDcwobt", 0),
		  "unbounded wstring 2");

    res += check ((a <<= CORBA::Any::from_wstring(i,20)) &&
		  !extract (a, "U20", 1, (char *)i) &&
		  !extract (a, "U20", 1, (char *)i) &&
		  !extract (a, "sSlLxXfdDcowbtuU10", 0),
		  "bounded wstring");
    return res;
}

/******************************* Sequence ********************************/

typedef SequenceTmpl<CORBA::Long> LongSeq;

CORBA::TypeCode_ptr _tc_LongSeq;

#if 0
CORBA::Boolean
operator<<= (CORBA::Any &a, const LongSeq &seq)
{
    a.type (_tc_LongSeq);
    if (!a.seq_put_begin (seq.length()))
        return FALSE;
    for (int i = 0; i < seq.length(); ++i)
        if (!(a <<= seq[i]))
            return FALSE;
    return a.seq_put_end();
}

CORBA::Boolean
operator>>= (CORBA::Any &a, LongSeq &seq)
{
    CORBA::ULong len;
    if (!a.seq_get_begin (len))
        return FALSE;
    seq.length (len);
    for (int i = 0; i < len; ++i)
        if (!(a >>= seq[i]))
            return FALSE;
    return a.seq_get_end();
}
#else
CORBA::Boolean operator<<= (CORBA::Any &a, const LongSeq &);
CORBA::Boolean operator>>= (const CORBA::Any &a, LongSeq &);
#endif

CORBA::Boolean
operator== (const LongSeq &s1, const LongSeq &s2)
{
    CORBA::ULong l = s1.length();
    if (l != s2.length())
        return FALSE;
    for (CORBA::ULong i = 0; i < l; ++i)
        if (s1[i] != s2[i])
            return FALSE;
    return TRUE;
}

int
test_seq ()
{
    _tc_LongSeq = CORBA::TypeCode::create_sequence_tc (
	0, CORBA::_tc_long)->mk_constant();

    CORBA::Any a;
    LongSeq s1, s2, s3;

    s1.length (10);
    for (int i = 0; i < 10; ++i)
        s1[i] = i;

    return check ((a <<= s1) &&
                  (a >>= s2) && s1 == s2 &&
                  (a >>= s3) && s1 == s3,
                  "sequence");
}

/******************************* Array ********************************/

CORBA::TypeCode_ptr _tc_LongArray;
CORBA::TypeCode_ptr _tc_LongArrayArray;

CORBA::Boolean operator<<= (CORBA::Any &a, const CORBA::Long arr[2][3])
{
    a.type (_tc_LongArrayArray);
    if (!a.array_put_begin())
	return FALSE;
    for (int i = 0; i < 2; ++i) {
        if (!a.array_put_begin())
	    return FALSE;
        for (int j = 0; j < 3; ++j) {
            if (!(a <<= arr[i][j]))
		return FALSE;
        }
        if (!a.array_put_end())
	    return FALSE;
    }
    return a.array_put_end();
}

CORBA::Boolean operator>>= (CORBA::Any &a, CORBA::Long arr[2][3])
{
    if (!a.array_get_begin())
	return FALSE;
    for (int i = 0; i < 2; ++i) {
        if (!a.array_get_begin())
	    return FALSE;
        for (int j = 0; j < 3; ++j) {
            if (!(a >>= arr[i][j]))
		return FALSE;
        }
        if (!a.array_get_end())
	    return FALSE;
    }
    return a.array_get_end();
}

int test_array ()
{
    _tc_LongArray = CORBA::TypeCode::create_array_tc (
	3, CORBA::_tc_long)->mk_constant();
    _tc_LongArrayArray = CORBA::TypeCode::create_array_tc (
	2, _tc_LongArray)->mk_constant();

    CORBA::Long arr1[2][3], arr2[2][3];

    for (int i = 0; i < 2; ++i)
        for (int j = 0; j < 3; ++j)
            arr1[i][j] = i*3+j;

    CORBA::Any a1, a2;
    if (!(a1 <<= arr1))
	return 1;
    a2.replace (_tc_LongArrayArray, (void *)a1.value());
    if (!(a2 >>= arr2))
	return 1;

    for (int i = 0; i < 2; ++i)
        for (int j = 0; j < 3; ++j)
            if (!(arr1[i][j] == arr2[i][j]))
		return FALSE;

    string s = _tc_LongArrayArray->stringify();
    CORBA::TypeCode t (s.c_str());
    return 0;
}

/******************************* Struct ********************************/

struct SomeStruct {
    CORBA::Long l;
    LongSeq s;
};

CORBA::TypeCode_ptr _tc_SomeStruct;

struct __mk_somestruct_typecode {
    __mk_somestruct_typecode ()
    {
        CORBA::StructMember sm1;
        sm1.name = (const char *)"l";
        sm1.type = CORBA::_tc_long;
        sm1.type_def = 0;

        CORBA::StructMember sm2;
        sm2.name = (const char *)"s";
        sm2.type = _tc_LongSeq;
        sm2.type_def = 0;
        
        CORBA::StructMemberSeq ss;
        ss.length (2);
        ss[0] = sm1;
        ss[1] = sm2;

        _tc_SomeStruct = CORBA::TypeCode::create_struct_tc ("repoid",
							    "SomeStruct",
							    ss)->mk_constant();
    }
};

CORBA::Boolean
operator<<= (CORBA::Any &a, const SomeStruct &s)
{
    a.type (_tc_SomeStruct);
    return (a.struct_put_begin () &&
            (a <<= s.l) &&
            (a <<= s.s) &&
            a.struct_put_end ());
}

CORBA::Boolean
operator>>= (const CORBA::Any &a, SomeStruct &s)
{
    return (a.struct_get_begin () &&
            (a >>= s.l) &&
            (a >>= s.s) &&
            a.struct_get_end ());
}

CORBA::Boolean
operator== (const SomeStruct &s1, const SomeStruct &s2)
{
    return s1.l == s2.l && s1.s == s2.s;
}

int
test_struct ()
{
    __mk_somestruct_typecode _mk_somestruct_typecode;
    (void)_mk_somestruct_typecode;

    CORBA::Any a;
    SomeStruct s1, s2, s3, s4;

    s1.l = 1234;
    s1.s.length (10);
    for (int i = 0; i < 10; ++i)
        s1.s[i] = i;

    if (check ((a <<= s1) &&
               (a >>= s2) && s1 == s2 &&
               (a >>= s3) && s1 == s3,
               "struct 1"))
        return 1;

    CORBA::Any a2 (a.type(), (void *)a.value());
    int res = check (a == a2 && (a2 >>= s4) && s1 == s4, "struct 2");

    CORBA::Any a3, a4;
    res += check ((a3 <<= a) && (a3 >>= a4) && (a == a4), "struct 3");
    return res;
}

/********************************* Union *********************************/

/*
 * #define EXPLICIT
 * 
 * union SomeUnion switch (long) {
 * case 1:  long l;
 * case 2:  short s;
 * default: boolean b;
 * };
 *
 * #undef EXPLICIT
 * 
 * union SomeUnion switch (long) {
 * case 1:  long l;
 * case 2:  short s;
 * };
 */

#define EXPLICIT

class SomeUnion {
    CORBA::Long _l;
    CORBA::Short _s;
#ifdef EXPLICIT
    CORBA::Boolean _b;
#endif
    CORBA::Long _disc;
public:
    void _d (CORBA::Long d)
    {
        _disc = d;
    }
    CORBA::Long _d () const
    {
        return _disc;
    }

#ifndef EXPLICIT
    void _default ()
    {
	_disc = 3;
    }
#endif

    void l (CORBA::Long l)
    {
        _d (1);
        _l = l;
    }
    CORBA::Long l () const
    {
        assert (_d() == 1);
        return _l;
    }

    void s (CORBA::Long s)
    {
        _d (2);
        _s = s;
    }
    CORBA::Short s () const
    {
        assert (_d() == 2);
        return _s;
    }

#ifdef EXPLICIT
    void b (CORBA::Boolean b)
    {
        _d (3);
        _b = b;
    }
    CORBA::Boolean b () const
    {
	assert (_d() > 2);
        return _b;
    }
#endif
};

CORBA::TypeCode_ptr _tc_SomeUnion;

struct __mk_someunion_typecode {
    __mk_someunion_typecode ()
    {
        CORBA::UnionMember um1;
        um1.name = (const char *)"l";
        um1.type = CORBA::_tc_long;
        um1.label <<= (CORBA::Long)1;
        um1.type_def = 0;

        CORBA::UnionMember um2;
        um2.name = (const char *)"s";
        um2.type = CORBA::_tc_short;
        um2.label <<= (CORBA::Long)2;
        um2.type_def = 0;

#ifdef EXPLICIT
        CORBA::UnionMember um3;
        um3.name = (const char *)"b";
        um3.type = CORBA::_tc_boolean;
        um3.label <<= CORBA::Any::from_octet (0);
        um3.type_def = 0;
#endif

        CORBA::UnionMemberSeq us;
        us.length (2);
        us[0] = um1;
        us[1] = um2;
#ifdef EXPLICIT
	us.length (3);
        us[2] = um3;
#endif

        _tc_SomeUnion
            = CORBA::TypeCode::create_union_tc ("repoid", "SomeUnion",
						CORBA::_tc_long,
						us)->mk_constant();
    }
};

CORBA::Boolean
operator<<= (CORBA::Any &a, const SomeUnion &u)
{
    a.type (_tc_SomeUnion);
    if (!a.union_put_begin())
	return FALSE;
    if (!(a <<= u._d()))
	return FALSE;
    switch (u._d()) {
    case 1:
        /*
         * the argument to union_*_selection() is the index of the
         * corresponding entry in the TypeCode. do not call for
	 * implicit default case.
         */
        if (!a.union_put_selection (0))
	    return FALSE;
        if (!(a <<= u.l()))
	    return FALSE;
        break;

    case 2:
        if (!a.union_put_selection (1))
	    return FALSE;
        if (!(a <<= u.s()))
	    return FALSE;
        break;
#ifdef EXPLICIT
    default:
	// explicit default
        if (!a.union_put_selection (2))
	    return FALSE;
        if (!(a <<= CORBA::Any::from_boolean (u.b())))
	    return FALSE;
        break;
#else
    default:
	// implicit default
	break;
#endif
    }
    return a.union_put_end();
}

CORBA::Boolean
operator>>= (const CORBA::Any &a, SomeUnion &u)
{
    if (!a.union_get_begin())
	return FALSE;
    CORBA::Long d;
    if (!(a >>= d))
	return FALSE;
    switch (d) {
    case 1: {
        if (!a.union_get_selection (0))
	    return FALSE;
        CORBA::Long l;
        if (!(a >>= l))
	    return FALSE;
        u.l (l);
        u._d (1); // in case multiple labels for one value
        break;
    }
    case 2: {
        if (!a.union_get_selection (1))
	    return FALSE;
        CORBA::Short s;
        if (!(a >>= s))
	    return FALSE;
        u.s (s);
        u._d (2); // in case multiple labels for one value
        break;
    }
#ifdef EXPLICIT
    default: {
	// explicit default
        if (!(a.union_get_selection (2)))
	    return FALSE;
        CORBA::Boolean b;
        if (!(a >>= CORBA::Any::to_boolean (b)))
	    return FALSE;
        u.b (b);
        u._d (d);
        break;
    }
#else
    default:
	// implicit default
	u._default ();
	u._d (d);
	break;
#endif
    }
    return a.union_get_end();
}

int
test_union ()
{
    __mk_someunion_typecode _mk_someunion_typecode;
    (void)_mk_someunion_typecode;
     
    CORBA::Any a1, a2;
    SomeUnion u1, u2;

    u1.l (10);
    if (!(a1 <<= u1))
	return 1;
    a2.replace (_tc_SomeUnion, (void *)a1.value());
    if (!(a2 >>= u2))
	return 1;

    if (!(u1._d() == u2._d()))
	return 1;
    if (!(u1.l() == u2.l()))
	return 1;

    
    u1.s (5);
    if (!(a1 <<= u1))
	return 1;
    a2.replace (_tc_SomeUnion, (void *)a1.value());
    if (!(a2 >>= u2))
	return 1;

    if (!(u1._d() == u2._d()))
	return 1;
    if (!(u1.s() == u2.s()))
	return 1;


#ifdef EXPLICIT
    u1.b (TRUE);
    if (!(a1 <<= u1))
	return 1;
    a2.replace (_tc_SomeUnion, (void *)a1.value());
    if (!(a2 >>= u2))
	return 1;

    if (!(u1._d() == u2._d()))
	return 1;
    if (!(u1.b() == u2.b()))
	return 1;
#else
    // implicit default
    u1._default();
    if (!(a1 <<= u1))
	return 1;
    a2.replace (_tc_SomeUnion, (void *)a1.value());
    if (!(a2 >>= u2))
	return 1;

    if (!(u1._d() == u2._d()))
	return 1;
#endif
    
    string s = _tc_SomeUnion->stringify();
    CORBA::TypeCode t (s.c_str());

    return 0;
}

/*************************** RecursiveSequence ***************************/

/*
 * struct SomeRecStruct {
 *     long l;
 *     sequence<SomeRecStruct> childs;
 * };
 */

struct SomeRecStruct {
    CORBA::Long l;
    SequenceIndTmpl<SomeRecStruct, SomeRecStruct, 1> childs;

    SomeRecStruct ()
    {
    }

    SomeRecStruct (const SomeRecStruct &s)
      : l (s.l), childs (s.childs)
    {
    }

    ~SomeRecStruct ()
    {
    }

    SomeRecStruct &operator= (const SomeRecStruct &s)
    {
      l = s.l;
      childs = s.childs;
      return *this;
    }
};

typedef SequenceIndTmpl<SomeRecStruct, SomeRecStruct, 1> SomeRecStructSeq;


CORBA::TypeCode_ptr _tc_SomeRecStruct;

struct __mk_somerecstruct_typecode {
    __mk_somerecstruct_typecode ()
    {
        CORBA::StructMember sm1;
        sm1.name = (const char *)"l";
        sm1.type = CORBA::_tc_long;
        sm1.type_def = 0;

        CORBA::StructMember sm2;
        sm2.name = (const char *)"childs";
	/*
	 * first argument is the sequence bound.
	 * second arg is the recursion depth: a value of 0 would result
	 * in a sequence of itself, 1 results in a sequence of its
	 * enclosing type and so on.
	 */
        sm2.type = CORBA::TypeCode::create_recursive_sequence_tc (0, 1);
        sm2.type_def = 0;
        
        CORBA::StructMemberSeq ss;
        ss.length (2);
        ss[0] = sm1;
        ss[1] = sm2;

        _tc_SomeRecStruct
	    = CORBA::TypeCode::create_struct_tc ("repoid",
						 "SomeRecStruct",
						 ss)->mk_constant();
    }
} __mk_somerecstruct_tc;

CORBA::Boolean operator<<= (CORBA::Any &a, const SomeRecStruct &s);
CORBA::Boolean operator>>= (const CORBA::Any &a, SomeRecStruct &s);

CORBA::Boolean
operator<<= (CORBA::Any &a, const SomeRecStructSeq &s)
{
    static CORBA::TypeCode_ptr _tc =
	CORBA::TypeCode::create_sequence_tc (0,
					     _tc_SomeRecStruct)->mk_constant();

    a.type (_tc);
    if (!a.seq_put_begin (s.length()))
	return FALSE;
    for (CORBA::ULong i = 0; i < s.length(); ++i) {
	if (!(a <<= s[i]))
	    return FALSE;
    }
    return a.seq_put_end ();
}

CORBA::Boolean
operator>>= (const CORBA::Any &a, SomeRecStructSeq &s)
{
    CORBA::ULong len;
    if (!a.seq_get_begin (len))
	return FALSE;
    s.length (len);
    for (CORBA::ULong i = 0; i < len; ++i) {
        if (!(a >>= s[i]))
	    return FALSE;
    }
    return a.seq_get_end();
}

CORBA::Boolean
operator<<= (CORBA::Any &a, const SomeRecStruct &s)
{
    a.type (_tc_SomeRecStruct);
    return (a.struct_put_begin () &&
	    (a <<= s.l) &&
	    (a <<= s.childs) &&
	    a.struct_put_end ());
}

CORBA::Boolean
operator>>= (const CORBA::Any &a, SomeRecStruct &s)
{
    return (a.struct_get_begin () &&
	    (a >>= s.l) &&
	    (a >>= s.childs) &&
	    a.struct_get_end ());
}

CORBA::Boolean
operator== (const SomeRecStruct &s1, const SomeRecStruct &s2)
{
    if (s1.l != s2.l || s1.childs.length() != s2.childs.length())
        return FALSE;
    for (CORBA::ULong i = 0; i < s1.childs.length(); ++i) {
        if (!(s1.childs[i] == s2.childs[i]))
            return FALSE;
    }
    return TRUE;
}

int
test_recursive ()
{
    __mk_somerecstruct_typecode _mk_somerecstruct_typecode;
    (void)_mk_somerecstruct_typecode;

    CORBA::Any a;
    CORBA::TypeCode_var tc;

    if (!(a <<= _tc_SomeRecStruct))
	return 1;
    if (!(a >>= tc))
	return 1;
    if (!(tc->equal (_tc_SomeRecStruct)))
	return 1;

    SomeRecStruct s1, s11, s12, s13, s111, s112;
    SomeRecStruct s2;

    s1.l = 1;
    s11.l = 11;
    s12.l = 12;
    s13.l = 13;
    s111.l = 111;
    s112.l = 112;

    s1.childs.length (3);
    s1.childs[0] = s11;
    s1.childs[1] = s12;
    s1.childs[2] = s13;

    s11.childs.length (2);
    s11.childs[0] = s111;
    s11.childs[1] = s112;

    if (!(a <<= s1))
	return 1;
    if (!(a >>= s2))
	return 1;

    if (!(s1 == s2))
	return 1;

    return 0;
}

/*************************** Subtyping ***********************************/

int
test_subtyping()
{
  CORBA::Any a;
  T1 t1;
  T2 t2;
  
  t1.x = 1;
  t1.z.y = 2;
  t1.z.next.length( 1 );
  t1.z.next[ 0 ].x = 3;
  t1.z.next[ 0 ].z.y = 4;

  if (!( a <<= t1 ))
      return 1;
  if (!( a >>= t2 ))
      return 1;

  if (!( t2.x == 1 ))
      return 1;
  if (!( t2.z.y == 2 ))
      return 1;
  if (!( t2.z.next[ 0 ].x == 3 ))
      return 1;
  if (!( t2.z.next[ 0 ].z.y == 4 ))
      return 1;
  if (!( t2.z.next[ 0 ].z.next.length() == 0 ))
      return 1;

  return 0;
}


/******************************* TypeCode ********************************/

int
test_typecode ()
{
    CORBA::Any a;

    CORBA::TypeCode_ptr i = _tc_SomeStruct;
    CORBA::TypeCode_ptr o = CORBA::_tc_null;

    int ret = check ((a <<= i) && (a >>= o) && i->equal (o),
                     "typecode 1");

    string strtc = i->stringify();
    CORBA::TypeCode tmp (strtc);
    check (i->equal (&tmp), "typecode 2");
    CORBA::release (i);
    CORBA::release (o);
    return ret;
}

/********************************* Enum **********************************/

enum SomeEnum {
    Red, Green, Blue
};

CORBA::TypeCode_ptr _tc_SomeEnum;

struct __mk_someenum_typecode {
    __mk_someenum_typecode ()
    {
        CORBA::EnumMemberSeq es;
        es.length (3);
        es[0] = (const char *)"Red";
        es[1] = (const char *)"Green";
        es[2] = (const char *)"Blue";

        _tc_SomeEnum =
            CORBA::TypeCode::create_enum_tc ("repoid",
					     "SomeEnum", es)->mk_constant();
    }
};

CORBA::Boolean
operator<<= (CORBA::Any &a, const SomeEnum &en)
{
    a.type (_tc_SomeEnum);
    return (a.enum_put ((CORBA::ULong)en));
}

CORBA::Boolean
operator>>= (const CORBA::Any &a, SomeEnum &en)
{
    CORBA::ULong ul;
    if (!a.enum_get (ul))
        return FALSE;
    en = (SomeEnum)ul;
    return TRUE;
}

int
test_enum()
{
    __mk_someenum_typecode _mk_someenum_typecode;
    (void)_mk_someenum_typecode;

    CORBA::Any a;
    SomeEnum e1, e2, e3;

    e1 = Green;
    return (check ((a <<= e1) &&
                   (a >>= e2) && e1 == e2 &&
                   (a >>= e3) && e1 == e3,
                   "enum"));
}

/******************************* Object **********************************/

CORBA::TypeCode_ptr _tc_SomeInterface;


class SomeInterface;
typedef SomeInterface *SomeInterface_ptr;

class SomeInterface : public CORBA::Object {
public:
    static SomeInterface_ptr narrow (CORBA::Object_ptr obj)
    {
        SomeInterface_ptr o = new SomeInterface;
        o->CORBA::Object::operator= (*obj);
        return o;
    }

    void foo () {}
};

CORBA::Boolean
operator<<= (CORBA::Any &a, SomeInterface_ptr obj)
{
    return (a <<= CORBA::Any::from_object (obj, "SomeInterface"));
}

CORBA::Boolean
operator>>= (const CORBA::Any &a, SomeInterface_ptr &obj)
{
    CORBA::Object_ptr o;
    if (!(a >>= CORBA::Any::to_object (o)))
        return FALSE;
    // XXX catch error if narrow fails ...
    obj = SomeInterface::narrow (o);
    CORBA::release (o);
    return TRUE;
}

int
test_interface ()
{
    _tc_SomeInterface = CORBA::TypeCode::create_interface_tc (
	"SomeInterface", "repo-id")->mk_constant();

    // XXX have no object to do test with ...
    return 0;
}

/*************************************************************************/

int
main (int argc, char *argv[])
{
    CORBA::ORB_var orb = CORBA::ORB_init (argc, argv, "mico-local-orb");

    int res = 0;
    res += test_basic ();
    res += test_string ();
    res += test_wstring ();
    res += test_fixed ();
    res += test_seq ();
    res += test_array ();
    res += test_struct ();
    res += test_union ();
    res += test_recursive ();
    res += test_subtyping();
    res += test_typecode ();
    res += test_enum ();
    res += test_interface ();
    
    if (res > 0)
	cout << res << " errors" << endl;
    return !!res;
}
