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

#include <OB/CORBA.h>
#include <Types.h>
#include <GenUtil.h>
#include <GenCPP.h>

// ----------------------------------------------------------------------
// Generate code for the tie header file
// ----------------------------------------------------------------------

void
IdlCPPGenerator::genCPlusPlusTieH(CORBA_Container_ptr container,
				  IdlPrettyPrint& out)
{
    //
    // Get scope and prefix
    // (prefix needed if alternative module mapping is used)
    //
    CORBA_String_var scope;
    CORBA_String_var pre;
    getScopeAndPrefix(container, scope.out(), pre.out());

    //
    // Get container contents descriptions
    //
    CORBA_Container::DescriptionSeq_var seq =
	container -> describe_contents(CORBA_dk_all, true, -1);

    CORBA_ULong i, j, k;

    for(i = 0 ; i < seq -> length() ; i++)
    {
	//
	// Get container
	//
	CORBA_Container_var cont =
	    CORBA_Container::_narrow(seq[i].contained_object);

	switch(seq[i].kind)
	{
	case CORBA_dk_Module:
	{
	    //
	    // Module
	    //
	    CORBA_ModuleDescription* desc;
	    CORBA_Boolean b = seq[i].value >>= desc;
	    assert(b);

	    // Don't add this here!!!
	    // if(!check(desc -> id))
	    //     continue;

 	    comment(desc -> id, out);

	    out.sep();

	    assert(!CORBA_is_nil(cont));
	    genCPlusPlusTieH(cont, out);

	    break;
	}

	case CORBA_dk_Interface:
	{
	    //
	    // Interface
	    //
	    CORBA_InterfaceDef_var interf =
		CORBA_InterfaceDef::_narrow(seq[i].contained_object);
	    assert(!CORBA_is_nil(interf));
	    CORBA_InterfaceDef::FullInterfaceDescription_var desc =
		interf -> describe_interface();

	    if(!check(desc -> id))
		continue;

	    CORBA_Identifier_var name = fixKwd(desc -> name);

	    comment(desc -> id, out);

	    //
	    // The tie template
	    //
	    out.sep();
	    out << "\ntemplate<class T>"
		<< "\nclass " << pre << name << "_skel_tie : ";

	    out << "virtual public " << pre << name << "_skel";

	    out.start();

	    //
	    // Private members
	    //
	    out << "\nT* ptr_;";
//	    out << "\nCORBA_BOA_ptr boa_;";
	    out << "\nCORBA_Boolean rel_;";
	    out.sep();
	    out << '\n' << pre << name << "_skel_tie(const "
		<< pre << name << "_skel_tie<T>&) { }";
	    out << "\nvoid operator=(const " << pre << name
		<< "_skel_tie<T>&) { }";

	    //
	    // Public members
	    //
	    out.sep();
	    out.direct("\npublic:");

/*
	    out.sep();
	    out << '\n' << pre << name << "_skel_tie(T& t)";
	    out.inc();
	    out << "\n: ptr_(&t), boa_(CORBA_BOA::_nil()), rel_(0)";
	    out.dec();
	    out.start();
	    out.end();

	    out.sep();
	    out << '\n' << pre << name
		<< "_skel_tie(T&t, CORBA_BOA_ptr boa)";
	    out.inc();
	    out << "\n: ptr_(&t), boa_(CORBA_BOA::_duplicate(boa)), rel_(0)";
	    out.dec();
	    out.start();
	    out.end();
*/

	    out.sep();
	    out << '\n' << pre << name << "_skel_tie(T* p, "
		<< "CORBA_Boolean release = CORBA_TRUE)";
	    out.inc();
//	    out << "\n: ptr_(p), boa_(CORBA_BOA::_nil()), rel_(release)";
	    out << "\n: ptr_(p), rel_(release)";
	    out.dec();
	    out.start();
	    out.end();

/*
	    out.sep();
	    out << '\n' << pre << name << "_skel_tie(T* p, "
		<< "CORBA_BOA_ptr boa, CORBA_Boolean release = 1)";
	    out.inc();
	    out << "\n: ptr_(p), boa_(CORBA_BOA::_duplicate(boa)), "
		<< "rel_(release)";
	    out.dec();
	    out.start();
	    out.end();
*/

	    out.sep();
	    out << "\nvirtual\n~" << pre << name << "_skel_tie()";
	    out.start();
	    out << "\nif(rel_)";
	    out.inc();
	    out << "\ndelete ptr_;";
	    out.dec();
// 	    out.sep();
// 	    out << "\nCORBA_release(boa_);";
	    out.end();

	    //
	    // Tie-specific functions
	    //
	    out.sep();
	    out << "\nT*\n_tied_object()";
	    out.start();
	    out << "\nreturn ptr_;";
	    out.end();

	    out.sep();
	    out << "\nvoid\n_tied_object(T& obj)";
	    out.start();
	    out << "\nif(rel_)";
	    out.inc();
	    out << "\ndelete ptr_;";
	    out.dec();
	    out.sep();
	    out << "\nptr_ = &obj;";
	    out << "\nrel_ = CORBA_FALSE;";
	    out.end();

	    out.sep();
	    out << "\nvoid\n_tied_object(T* obj, "
		<< "CORBA_Boolean release = CORBA_TRUE)";
	    out.start();
	    out << "\nif(rel_)";
	    out.inc();
	    out << "\ndelete ptr_;";
	    out.dec();
	    out.sep();
	    out << "\nptr_ = obj;";
	    out << "\nrel_ = release;";
	    out.end();

	    out.sep();
	    out << "\nCORBA_Boolean\n_is_owner()";
	    out.start();
	    out << "\nreturn rel_;";
	    out.end();

	    out.sep();
	    out << "\nvoid\n_is_owner(CORBA_Boolean b)";
	    out.start();
	    out << "\nrel_ = b;";
	    out.end();

	    //
	    // Attributes and operations
	    //
	    CORBA_ContainedSeq_var attributes =
		interf -> contents(CORBA_dk_Attribute, false);

	    CORBA_ContainedSeq_var operations =
		interf -> contents(CORBA_dk_Operation, false);

	    for(j = 0 ; j < attributes -> length() ; j++)
	    {
		CORBA_Contained::Description_var contDesc =
		    attributes[j] -> describe();
		CORBA_AttributeDescription* atDesc;
		CORBA_Boolean b = (contDesc -> value) >>= atDesc;
		assert(b);

		CORBA_Identifier_var atName = fixKwd(atDesc -> name);

		out.sep();

		CORBA_TypeCode_var origType = OBGetOrigType(atDesc -> type);
		CORBA_String_var ret =
		    getTypeString(scope, atDesc -> type, GetTypeReturn);
		out << "\nvirtual " << ret << '\n' << atName << "()";
		out.start();
		out << "\nreturn ptr_ -> " << atName << "();";
		out.end();

		if(atDesc -> mode == CORBA_ATTR_NORMAL)
		{
		    out.sep();

		    CORBA_String_var in = getTypeString(scope, atDesc -> type,
							GetTypeIn);

		    out << "\nvirtual void\n" << atName << '(' << in << " a)";
		    out.start();
		    out << "\nptr_ -> " << atName << "(a);";
		    out.end();
		}
	    }

	    for(j = 0 ; j < operations -> length() ; j++)
	    {
		CORBA_Contained::Description_var contDesc =
		    operations[j] -> describe();
		CORBA_OperationDescription* opDesc;
		CORBA_Boolean b = (contDesc -> value) >>= opDesc;
		assert(b);
		
		CORBA_Identifier_var opName = fixKwd(opDesc -> name);

		out.sep();

		CORBA_String_var ret =
		    getTypeString(scope, opDesc -> result, GetTypeReturn);

		out << "\nvirtual " << ret << '\n' << opName << '(';

		unsigned int indent = out.getIndent();
		out.setIndent(out.getPos());

		for(k = 0 ; k < opDesc -> parameters.length() ; k++)
		{
		    GetType tt;
		    switch(opDesc -> parameters[k].mode)
		    {
		    case CORBA_PARAM_IN:
			tt = GetTypeIn;
			break;

		    case CORBA_PARAM_INOUT:
			tt = GetTypeInOut;
			break;

		    case CORBA_PARAM_OUT:
			tt = GetTypeOut;
			break;
		    }

		    CORBA_String_var argName =
			fixKwd(opDesc -> parameters[k].name);

		    CORBA_String_var s =
			getTypeString(scope, opDesc -> parameters[k].type, tt,
				      false, argName);

		    if(k > 0)
			out << ",\n";

		    out << s;
		}

		if(opDesc -> contexts.length() > 0)
		{
		    if(opDesc -> parameters.length() > 0)
			out << ",\n";
		
		    out << "CORBA_Context_ptr context";
		}

		out << ')';

		out.setIndent(indent);

		out.start();
		out << '\n';
		if(opDesc -> result -> kind() != CORBA_tk_void)
		    out << "return ";
		out << "ptr_ -> " << opName << '(';

		for(k = 0 ; k < opDesc -> parameters.length() ; k++)
		{
		    if(k > 0)
			out << ", ";

		    CORBA_String_var argName =
			fixKwd(opDesc -> parameters[k].name);

		    out << argName;
		}

		if(opDesc -> contexts.length() > 0)
		{
		    if(opDesc -> parameters.length() > 0)
			out << ", ";
		
		    out << "context";
		}

		out << ");";

		out.end();
	    }

	    //
	    // Override ServantBase operations
	    //
/*
	    out.sep();
	    out << "\nPOA_ptr _default_POA()";
	    out.start();
	    out << "\nif(!CORBA_is_nil(_poa))";
	    out.inc();
	    out << "\nreturn _poa;";
	    out.dec();
	    out << "\nelse";
	    out.inc();
	    out << "\n// return root POA";
	    out.dec();
	    out.end();
*/

	    out.end();
	    out << ';';

	    break;
	}

	default:
	    break;
	}
    }
}
