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

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

void TestUnconstrained(CosPropertyService_PropertySetDef_ptr);
void TestConstrained(CosPropertyService_PropertySetDef_ptr);
void TestInitial(CosPropertyService_PropertySetDef_ptr);

int
main(int argc, char* argv[], char*[])
{
    int status = 0;
 
    try
    {
        //
        // Create ORB
        //
        CORBA_ORB_var orb = CORBA_ORB_init(argc, argv);
 
	CosPropertyService_PropertySetDefFactory_var factory =
	    new CosPropertyService_PropertySetDefFactory_impl(orb);

	CosPropertyService_PropertySetDef_var set;

        //
        // Run tests with unconstrained PropertySetDef
        //
	cout << "Running tests with unconstrained PropertySet" << endl;
	set = factory -> create_propertysetdef();
	TestUnconstrained(set);

        //
        // Set up PropertySetDef constraints, only tk_string is allowed
        //
	CosPropertyService_PropertyTypes allowedTypes;
	allowedTypes.length(1);
	allowedTypes[0] = CORBA_TypeCode::_duplicate(CORBA__tc_string);

	CORBA_Any any;
	any <<= CORBA_string_dup("property_value");

	CosPropertyService_PropertyDefs allowedProperties;
	allowedProperties.length(3);
	CosPropertyService_PropertyDef p;

	p.property_name = CORBA_string_dup("read_only");
	p.property_value = any;
	p.property_mode = CosPropertyService_read_only;
	allowedProperties[0] = p;

	p.property_name = CORBA_string_dup("fixed_normal");
	p.property_value = any;
	p.property_mode = CosPropertyService_fixed_normal;
	allowedProperties[1] = p;

	p.property_name = CORBA_string_dup("fixed_readonly");
	p.property_value = any;
	p.property_mode = CosPropertyService_fixed_readonly;
	allowedProperties[2] = p;

	cout << "Running tests with constrained PropertySet" << endl;
	set = factory -> create_constrained_propertysetdef(allowedTypes, 
							   allowedProperties);
	TestConstrained(set);

	//
        // Set up initial PropertySetDef
        //
	any <<= CORBA_string_dup("initial_property_value");
	CosPropertyService_PropertyDefs initialProperties;
	p.property_name = CORBA_string_dup("initial_property");
	p.property_value = any;
	p.property_mode = CosPropertyService_normal;
	initialProperties.length(1);
	initialProperties[0] = p;

	cout << "Running tests with initial PropertySet" << endl;
	set = factory -> create_initial_propertysetdef(initialProperties);
	TestInitial(set);
    }
    catch(CORBA_SystemException& ex)
    {
        OBPrintException(ex);
        status = 1;
    }
 
    return status;
}

//
// Tests for unconstrained PropertySetDef
//
void TestUnconstrained(CosPropertyService_PropertySetDef_ptr set)
{
    CORBA_ULong i, j;

    {
	cout << "  Testing define_property()... ";
	cout.flush();

	for(i = 0 ; i < 10 ; i++)
	{
	    CORBA_Any any;
	    any <<= i;

	    try
	    {
		CORBA_String_var s = CORBA_string_dup("property_");
		s += i;
		set -> define_property(s, any);
	    }
	    catch(const CosPropertyService_ReadOnlyProperty&)
	    {
		assert(false);
	    }
	    catch(const CosPropertyService_ConflictingProperty&)
	    {
		assert(false);
	    }
	    catch(const CosPropertyService_UnsupportedProperty&)
	    {
		assert(false);
	    }
	    catch(const CosPropertyService_UnsupportedTypeCode&)
	    {
		assert(false);
	    }
	    catch(const CosPropertyService_InvalidPropertyName&)
	    {
		assert(false);
	    }
	}

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing define_properties()... ";
	cout.flush();

	CosPropertyService_Properties nproperties;

	for(i = 10 ; i < 20 ; i++)
	{
	    CORBA_Any any;
	    any <<= i;
	    CORBA_String_var s = CORBA_string_dup("property_");
	    s += i;
	    CosPropertyService_Property property;
	    property.property_name = s;
	    property.property_value = any;
	    nproperties.append(property);
	}

	try
	{
	    set -> define_properties(nproperties);
	}
	catch(const CosPropertyService_MultipleExceptions&)
	{
	    assert(false);
	}

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing get_all_property_names() without iterator... ";
	cout.flush();

	CosPropertyService_PropertyNames_var names;
	CosPropertyService_PropertyNamesIterator_var iterator;
	set -> get_all_property_names(set -> get_number_of_properties(),
				      names.out(), iterator.out());

	assert(names -> length() == set -> get_number_of_properties());

	CosPropertyService_PropertyNames v;
	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    CORBA_String_var s = CORBA_string_dup("property_");
	    s += i;
	    v.append(s);
	}

	//
	// Check if each property name exists only once
	//
	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    for(j = 0 ; j < v.length() ; j++)
	    {
		if(!strcmp(names[i], v[j]))
		    break;

		assert(false);
	    }

	    v.remove(j);
	}

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing get_all_property_names() with iterator... ";
	cout.flush();

	CosPropertyService_PropertyNames_var names;
	CosPropertyService_PropertyNamesIterator_var iterator;

	//
	// Get one name without iterator
	//
	set -> get_all_property_names(1, names.out(), iterator.out());
	assert(names -> length() == 1);

	names -> length(set -> get_number_of_properties());

	//
	// Get remaining names with iterator
	//
	CosPropertyService_PropertyName_var n;
	i = 1;
	while(iterator -> next_one(n.out()))
	    names[i++] = n;

	CosPropertyService_PropertyNames v;
	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    CORBA_String_var s = CORBA_string_dup("property_");
	    s += i;
	    v.append(s);
	}

	//
	// Check if each property name exists only once
	//
	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    for(j = 0 ; j < v.length() ; j++)
	    {
		if(!strcmp(names[i], v[j]))
		    break;

		assert(false);
	    }

	    v.remove(j);
	}

	iterator -> destroy();

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing get_property_value()... ";
	cout.flush();

	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    CORBA_Any_var any;

	    try
	    {
		CORBA_String_var s = CORBA_string_dup("property_");
		s += i;
		any = set -> get_property_value(s);
	    }
	    catch(const CosPropertyService_PropertyNotFound&)
	    {
		assert(false);
	    }

	    CORBA_ULong value;
	    any >>= value;
	    assert(value == i);
	}

	cout << "OK!" << endl;
	}

    {
	cout << "  Testing get_properties()... ";
	cout.flush();

	CosPropertyService_PropertyNames names;
	names.length(set -> get_number_of_properties());
	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    CORBA_String_var s = CORBA_string_dup("property_");
	    s += i;
	    names[i] = s;
	}

	CosPropertyService_Properties_var nproperties;
	set -> get_properties(names, nproperties.out());

	CosPropertyService_PropertyNames vs;
	CORBA_ULong vi[20];
	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    CORBA_String_var s = CORBA_string_dup("property_");
	    s += i;
	    vs.append(s);
	    vi[i] = i;
	}

	//
	// Check if each property name exists only once
	//
	CORBA_ULong match = 0;
	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    for(j = 0 ; j < set -> get_number_of_properties() ; j++)
	    {
		CORBA_String_var s = nproperties[i].property_name;
		if(strcmp(s, vs[j]))
		    continue;

		CORBA_Any any = nproperties[i].property_value;
		CORBA_ULong value;
		any >>= value;
		if(value != vi[j])
		    continue;

		//
		// Mark current element as invalid, it has passed the test
		//
		vs[j] = CORBA_string_dup("");
		vi[j] = 9999;

		match++;
	    }
	}

	assert(match == set -> get_number_of_properties());

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing get_all_properties() without iterator... ";
	cout.flush();

	CosPropertyService_Properties_var properties;
	CosPropertyService_PropertiesIterator_var iterator;
	set -> get_all_properties(set -> get_number_of_properties(),
				  properties.out(), iterator.out());

	assert(properties -> length() == set -> get_number_of_properties());

	CosPropertyService_PropertyNames vs;
	CORBA_ULong vi[20];
	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    CORBA_String_var s = CORBA_string_dup("property_");
	    s += i;
	    vs.append(s);
	    vi[i] = i;
	}

	//
	// Check if each property exists only once
	//
	CORBA_ULong match = 0;
	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    for(j = 0 ; j < set -> get_number_of_properties() ; j++)
	    {
		CORBA_String_var s = properties[i].property_name;
		if(strcmp(s, vs[j]))
		    continue;

		CORBA_Any any = properties[i].property_value;
		CORBA_ULong value;
		any >>= value;
		if(value != vi[j])
		    continue;

		//
		// Mark current element as invalid, it has passed the test
		//
		vs[j] = CORBA_string_dup("");
		vi[j] = 9999;

		match++;
	    }
	}

	assert(match == set -> get_number_of_properties());

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing get_all_properties() with iterator... ";
	cout.flush();

	CosPropertyService_Properties_var properties;
	CosPropertyService_PropertiesIterator_var iterator;

	//
	// Get one property without iterator
	//
	set -> get_all_properties(1, properties.out(), iterator.out());
	assert(properties -> length() == 1);

	properties -> length(set -> get_number_of_properties());

	//
	// Get remaining properties with iterator
	//
	CosPropertyService_Property_var n;
	i = 1;
	while(iterator -> next_one(n.out()))
	    properties[i++] = n;

	CosPropertyService_PropertyNames vs;
	CORBA_ULong vi[20];
	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    CORBA_String_var s = CORBA_string_dup("property_");
	    s += i;
	    vs.append(s);
	    vi[i] = i;
	}

	//
	// Check if each property exists only once
	//
	CORBA_ULong match = 0;
	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    for(j = 0 ; j < set -> get_number_of_properties() ; j++)
	    {
		CORBA_String_var s = properties[i].property_name;
		if(strcmp(s, vs[j]))
		    continue;

		CORBA_Any any = properties[i].property_value;
		CORBA_ULong value;
		any >>= value;
		if(value != vi[j])
		    continue;

		//
		// Mark current element as invalid, it has passed the test
		//
		vs[j] = CORBA_string_dup("");
		vi[j] = 9999;

		match++;
	    }
	}

	assert(match == set -> get_number_of_properties());

	iterator -> destroy();

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing is_property_defined()... ";
	cout.flush();

	for(i = 0 ; i < set -> get_number_of_properties() ; i++)
	{
	    CORBA_String_var name = CORBA_string_dup("property_");
	    name += i;

	    try
	    {
		assert(set -> is_property_defined(name));
	    }
	    catch(const CosPropertyService_InvalidPropertyName&)
	    {
		assert(false);
	    }
	}

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing delete_property()... ";
	cout.flush();

	//
	// Delete first 5 properties, 15 remain
	//
	for(i = 0 ; i < 5 ; i++)
	{
	    CORBA_String_var name = CORBA_string_dup("property_");
	    name += i;

	    try
	    {
		set -> delete_property(name);
	    }
	    catch(const CosPropertyService_InvalidPropertyName&)
	    {
		assert(false);
	    }
	    catch(const CosPropertyService_PropertyNotFound&)
	    {
		assert(false);
	    }
	    catch(const CosPropertyService_FixedProperty&)
	    {
		assert(false);
	    }
	}

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing delete_properties()... ";
	cout.flush();

	//
	// Delete next 10 properties, 5 remain
	//
	CosPropertyService_PropertyNames names;
	for(i = 5 ; i < 15 ; i++)
	{
	    CORBA_String_var s = CORBA_string_dup("property_");
	    s += i;

	    names.append(s);
	}

	try
	{
	    set -> delete_properties(names);
	}
	catch(const CosPropertyService_MultipleExceptions&)
	{
	    assert(false);
	}

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing delete_all_properties()... ";
	cout.flush();

	//
	// 10 properties remain and are deleted now
	//
	assert(set -> delete_all_properties());

	assert(!set -> get_number_of_properties());

	cout << "OK!" << endl;
    }
}

//
// Tests for constrained PropertySetDef
//
void TestConstrained(CosPropertyService_PropertySetDef_ptr set)
{
    CORBA_Any any;
    bool except;

    {
	cout << "  Testing get_allowed_property_types()... ";
	cout.flush();

	CosPropertyService_PropertyTypes_var types;
	set -> get_allowed_property_types(types.out());

	assert(types -> length() == 1);

	assert(types[0] -> equal(CORBA__tc_string));

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing get_allowed_properties()... ";
	cout.flush();

	CosPropertyService_PropertyDefs_var properties;
	set -> get_allowed_properties(properties);

	assert(properties -> length() == 3);

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing define_property_with_mode()... ";
	cout.flush();

	//
	// Try to add allowed properties
	//
	any <<= CORBA_string_dup("property_value");
	try
	{
	    set -> define_property_with_mode("fixed_normal", any,
					     CosPropertyService_fixed_normal);
	    set -> define_property_with_mode("read_only", any,
					     CosPropertyService_read_only);
	    set -> define_property_with_mode(
		"fixed_readonly", any, CosPropertyService_fixed_readonly);
	}
	catch(const CosPropertyService_ReadOnlyProperty&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_ConflictingProperty&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_UnsupportedProperty&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_UnsupportedTypeCode&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_InvalidPropertyName&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_UnsupportedMode&)
	{
	    assert(false);
	}

	//
	// Try to change readonly property
	//
	any <<= CORBA_string_dup("read_only");
	except = false;
	try
	{
	    set -> define_property("read_only", any);
	}
	catch(const CosPropertyService_ReadOnlyProperty&)
	{
	    // This exception must be thrown
	    except = true;
	}
	catch(const CosPropertyService_ConflictingProperty&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_UnsupportedProperty&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_UnsupportedTypeCode&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_InvalidPropertyName&)
	{
	    assert(false);
	}

	assert(except);

	//
	// Try to add illegal TypeCode
	//
	any <<= (CORBA_ULong)1;
	except = false;
	try
	{
	    set -> define_property("fixed_normal", any);
	}
	catch(const CosPropertyService_ReadOnlyProperty&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_ConflictingProperty&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_UnsupportedProperty&)
	{
	    // This exception must be thrown
	    except = true;
	}
	catch(const CosPropertyService_UnsupportedTypeCode&)
	{
	    // This exception must be thrown
	    except = true;
	}
	catch(const CosPropertyService_InvalidPropertyName&)
	{
	    assert(false);
	}
      
	assert(except);

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing get_property_mode()... ";
	cout.flush();

	CosPropertyService_PropertyModeType fixed_normal;
	CosPropertyService_PropertyModeType read_only;
	CosPropertyService_PropertyModeType fixed_readonly;
	try
	{
	    fixed_normal = set -> get_property_mode("fixed_normal");
	    read_only = set -> get_property_mode("read_only");
	    fixed_readonly = set -> get_property_mode("fixed_readonly");
	}
	catch(const CosPropertyService_InvalidPropertyName&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_PropertyNotFound&)
	{
	    assert(false);
	}

	assert(fixed_normal == CosPropertyService_fixed_normal ||
               read_only == CosPropertyService_read_only ||
               fixed_readonly == CosPropertyService_fixed_readonly);

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing delete_property()... ";

	except = false;
	//
	// Try to delete fixed_normal property;
	//
	try
	{
	    set -> delete_property("fixed_normal");
	}
	catch(const CosPropertyService_FixedProperty&)
	{
	    // This exception must be thrown
	    except = true;
	}
	catch(const CosPropertyService_InvalidPropertyName&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_PropertyNotFound&)
	{
	    assert(false);
	}

	assert(except);

	cout << "OK!" << endl;
    }
}

//
// Tests for initial PropertySetDef
//
void TestInitial(CosPropertyService_PropertySetDef_ptr set)
{
    {
	cout << "  Testing get_number_of_properties()... ";
	cout.flush();

	assert(set -> get_number_of_properties() == 1);

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing is_property_defined()... ";
	cout.flush();

	try
	{
	    assert(set -> is_property_defined("initial_property"));
	}
	catch(const CosPropertyService_InvalidPropertyName&)
	{
	    assert(false);
	}

	try
	{
	    assert(!set -> is_property_defined("illegal_property"));
	}
	catch(const CosPropertyService_InvalidPropertyName&)
	{
	    assert(false);
	}

	cout << "OK!" << endl;
    }

    {
	cout << "  Testing get_property_value()... ";
	cout.flush();

	CORBA_Any_var any;

	try
	{
	    any = set -> get_property_value("initial_property");
	}
	catch(const CosPropertyService_InvalidPropertyName&)
	{
	    assert(false);
	}
	catch(const CosPropertyService_PropertyNotFound&)
	{
	    assert(false);
	    }

	char* value;
	any >>= value;
	assert(!strcmp(value, "initial_property_value"));

	cout << "OK!" << endl;
    }
}
