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

#include <assert.h>
#include <stdlib.h>
#include <string.h>

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

#include <OB/CosNaming.h>

#include "TestObject_impl.h"
#include "TestMacro.h"


//
// Test CosNaming::NamingContext::rebind_context()
//
void
TestRebindContext(CORBA_ORB_ptr orb, CosNaming_NamingContext_ptr root)
{
    TestObject_var test = new TestObject_impl;
    CosNaming_Name name1, name2;
    CosNaming_NamingContext_var context;

    SUCCEED(context = root -> new_context());

    //
    // Test exceptions
    //

    try
    {
        //
        // Try to rebind a context when an intermediate context
        // doesn't exist
        //
        name1.length(2);
        name1[0].id = CORBA_string_dup("bad");
        name1[0].kind = CORBA_string_dup("node");
        name1[1].id = CORBA_string_dup("my");
        name1[1].kind = CORBA_string_dup("node");
        root -> rebind_context(name1, context);
        assert("rebind_context should not have succeeded" == 0);
    }
    catch(const CosNaming_NamingContext::NotFound& ex)
    {
        assert(ex.why == CosNaming_NamingContext::missing_node);
    }

    name1.length(1);
    name1[0].id = CORBA_string_dup("object");
    name1[0].kind = CORBA_string_dup("");
    SUCCEED(root -> bind(name1, test));

    try
    {
        //
        // Try to rebind a context when an object is in the path
        //
        name2.length(2);
        name2[0].id = CORBA_string_dup("object");
        name2[0].kind = CORBA_string_dup("");
        name2[1].id = CORBA_string_dup("my");
        name2[1].kind = CORBA_string_dup("node");
        root -> rebind_context(name2, context);
        assert("rebind_context should not have succeeded" == 0);
    }
    catch(const CosNaming_NamingContext::NotFound& ex)
    {
        assert(ex.why == CosNaming_NamingContext::not_context);
    }

/* XXX Does the spec allow an object binding to be rebound to a context?
   XXX If it doesn't say, should we allow it?
    try
    {
        //
        // Try to rebind a context using the name of an existing object
        //
        root -> rebind_context(name1, context);
        assert("rebind_context should not have succeeded" == 0);
    }
    catch(const CosNaming_NamingContext::NotFound& ex)
    {
        assert(ex.why == CosNaming_NamingContext::not_context);
    }
*/

    SUCCEED(root -> unbind(name1));

    CosNaming_NamingContext_var ctx;
    SUCCEED(ctx = root -> new_context());

    //
    // Test rebind
    //
    name1.length(1);
    name1[0].id = CORBA_string_dup("context");
    name1[0].kind = CORBA_string_dup("");
    SUCCEED(root -> bind_context(name1, context));
    SUCCEED(root -> rebind_context(name1, ctx));
    SUCCEED(root -> unbind(name1));
    SUCCEED(root -> rebind_context(name1, ctx));
    SUCCEED(root -> unbind(name1));

    try
    {
        //
        // Try to rebind a context with an invalid name
        //
        name1.length(0);
        root -> rebind_context(name1, context);
        assert("rebind_context should not have succeeded" == 0);
    }
    catch(const CosNaming_NamingContext::InvalidName&)
    {
        // expected
    }

    try
    {
        //
        // Try to rebind a context with an invalid name
        //
        name1.length(1);
        name1[0].id = CORBA_string_dup("");
        name1[0].kind = CORBA_string_dup("");
        root -> rebind_context(name1, context);
        assert("rebind_context should not have succeeded" == 0);
    }
    catch(const CosNaming_NamingContext::InvalidName&)
    {
        // expected
    }

    //
    // Bind an intermediate context for use in more tests
    //
    name1.length(1);
    name1[0].id = CORBA_string_dup("ctx");
    name1[0].kind = CORBA_string_dup("");
    SUCCEED(root -> bind_context(name1, context));

    try
    {
        //
        // Try to rebind a context when an intermediate context
        // doesn't exist
        //
        name1.length(3);
        name1[0].id = CORBA_string_dup("ctx");
        name1[0].kind = CORBA_string_dup("");
        name1[1].id = CORBA_string_dup("bad");
        name1[1].kind = CORBA_string_dup("node");
        name1[2].id = CORBA_string_dup("my");
        name1[2].kind = CORBA_string_dup("node");
        root -> rebind_context(name1, ctx);
        assert("rebind_context should not have succeeded" == 0);
    }
    catch(const CosNaming_NamingContext::NotFound& ex)
    {
        assert(ex.why == CosNaming_NamingContext::missing_node);
    }

    name1.length(1);
    name1[0].id = CORBA_string_dup("object");
    name1[0].kind = CORBA_string_dup("");
    SUCCEED(context -> bind(name1, test));

    try
    {
        //
        // Try to rebind a context when an object is in the path
        //
        name2.length(3);
        name2[0].id = CORBA_string_dup("ctx");
        name2[0].kind = CORBA_string_dup("");
        name2[1].id = CORBA_string_dup("object");
        name2[1].kind = CORBA_string_dup("");
        name2[2].id = CORBA_string_dup("my");
        name2[2].kind = CORBA_string_dup("node");
        root -> rebind_context(name2, ctx);
        assert("rebind_context should not have succeeded" == 0);
    }
    catch(const CosNaming_NamingContext::NotFound& ex)
    {
        assert(ex.why == CosNaming_NamingContext::not_context);
    }

    SUCCEED(context -> unbind(name1));

    //
    // Clean up context
    //
    name1.length(1);
    name1[0].id = CORBA_string_dup("ctx");
    name1[0].kind = CORBA_string_dup("");
    SUCCEED(root -> unbind(name1));


    //
    // Test name equality to make sure kind is included in comparisons
    //
    CORBA_Object_var o;
    name1.length(1);
    name1[0].id = CORBA_string_dup("context");
    name1[0].kind = CORBA_string_dup("kind1");
    name2.length(1);
    name2[0].id = CORBA_string_dup("context");
    name2[0].kind = CORBA_string_dup("kind2");
    SUCCEED(root -> bind_context(name1, context));
    SUCCEED(o = root -> resolve(name1));
    assert(! CORBA_is_nil(o));
    try
    {
	o = root -> resolve(name2);
	assert("resolve should not have succeeded" == 0);
    }
    catch(const CosNaming_NamingContext::NotFound&)
    {
	// Expected
    }

    SUCCEED(root -> unbind(name1));


    SUCCEED(ctx -> destroy());

    SUCCEED(context -> destroy());
}
