// **********************************************************************
//
// 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 skeleton source file, general part
// ----------------------------------------------------------------------

void
IdlCPPGenerator::genCPlusPlusSkelC(CORBA_Container_ptr container,
				   IdlPrettyPrint& out)
{
    //
    // Get container contents descriptions
    //
    CORBA_Container::DescriptionSeq_var seq =
	container -> describe_contents(CORBA_dk_all, true, -1);
    
    CORBA_ULong i, j;

    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;

	    CORBA_Identifier_var name = fixKwd(desc -> name);

 	    comment(desc -> id, out);

	    out.sep();

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

	    break;
	}

	case CORBA_dk_Interface:
        {
	    assert(!CORBA_is_nil(cont));
	    genCPlusPlusSkelCProtected(cont, out);

            //
            // Interface
            //
	    CORBA_InterfaceDescription* desc;
	    CORBA_Boolean b = seq[i].value >>= desc;
	    assert(b);

            if(!check(desc -> id))
                continue;
            
            CORBA_Identifier_var name = fixKwd(desc -> name);
            CORBA_ScopedName_var absolute = getAbsolute(desc -> id);
            
            comment(desc -> id, out);
            
            //
            // _OB_dispatch()
            //
            unsigned int indent = out.getIndent();
            out.sep();
            out << "\nOBDispatchStatus\n" << absolute
                << "_skel::_OB_dispatch(";
            out.setIndent(out.getPos());
            out << "const char* _ob_op,";
            out << "\nOBBuffer& _ob_buf,";
            out << "\nbool _ob_sw,";
            out << "\nCORBA_ULong _ob_offIn,";
            out << "\nCORBA_ULong _ob_offOut)";
            out.setIndent(indent);
            out.start();

            CORBA_InterfaceDef_var interf =
		CORBA_InterfaceDef::_narrow(seq[i].contained_object);
            assert(!CORBA_is_nil(interf));

            //
            // Method name table for operations
            //
            CORBA_ContainedSeq_var operations =
		interf -> contents(CORBA_dk_Operation, false);
	    CORBA_ContainedSeq_var attributes =
		interf -> contents(CORBA_dk_Attribute, false);
            IdlStringSeq_var names = IdlGetMethodNames(attributes, operations);
            
            if(names -> length() > 0)
            {
                //
                // Emit the operation table
                //
                out << "\nstatic const char* _ob_names[] =";
                out.start();
                for(j = 0; j < names -> length(); j++)
                {
                    if(j != 0)
                        out << ",";
                    out << "\n\"" << names[j] << "\"";
                }
                out.end();
		out << ';';
                out << "\nstatic const CORBA_ULong _ob_numNames = "
                    << names -> length() << ";";

                //
                // Emit the giant switch statement
                //
                out.sep();
                out << "\nswitch(_OB_findOperation(_ob_op, _ob_names, "
                    << "_ob_numNames))";
                out.start();
		out.dec();
                
                for(j = 0 ; j < names -> length(); j++)
                {
		    if(j > 0)
			out.sep();
                    out << "\ncase " << j << ": // " << names[j];
		    out.inc();
                    int len = strlen(names[j]);
		    out << "\nreturn _OB";
                    if(!(len > 5 && (strncmp(names[j], "_get_", 5) == 0 ||
				     strncmp(names[j], "_set_", 5) == 0)))
			out << "_op_";
		    out << names[j]
			<< "(_ob_buf, _ob_sw, _ob_offIn, _ob_offOut);";
		    out.dec();
                }

		out.inc();
                out.end();
	    }
            
            if(names -> length() > 0)
		out.sep();

	    out << "\nreturn CORBA_Object_skel::_OB_dispatch(_ob_op, "
		<< "_ob_buf, _ob_sw, _ob_offIn, _ob_offOut);";
            
            out.end();
            
            break;
        }
        
        default:
            break;
        }
    }
}

// ----------------------------------------------------------------------
// Generate code for the skeleton source file, protected part
// ----------------------------------------------------------------------

void
IdlCPPGenerator::genCPlusPlusSkelCProtected(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 the skeleton scope
    //
    const char* skel = "_skel";
    CORBA_String_var skscope =
	CORBA_string_alloc(strlen(scope) + strlen(skel));
    strncpy(skscope.inout(), scope, strlen(scope) - 2);
    skscope[strlen(scope) - 2] = '\0';
    strcat(skscope.inout(), skel);
    strcat(skscope.inout(), "::");

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

    CORBA_ULong i, j;

    for(i = 0 ; i < seq -> length() ; i++)
    {
	switch(seq[i].kind)
	{
	case CORBA_dk_Attribute:
	{
	    //
	    // Attribute
	    //
	    CORBA_AttributeDescription* desc;
	    CORBA_Boolean b = seq[i].value >>= desc;
	    assert(b);

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

	    CORBA_Identifier_var name = fixKwd(desc -> name);

	    comment(desc -> id, out);

	    //
	    // Don't use name here instead of desc -> name. We need
	    // the name "unfixed".
	    //
	    out.sep();
            unsigned int indent = out.getIndent();
	    out << "\nOBDispatchStatus\n" << skscope << "_OB_get_"
		<< desc -> name << '(';
            out.setIndent(out.getPos());
            out << "OBBuffer& _ob_buf,";
            out << "\nbool _ob_sw,";
            out << "\nCORBA_ULong _ob_offIn,";
            out << "\nCORBA_ULong _ob_offOut)";
            out.setIndent(indent);
            out.start();
	    CORBA_TypeCode_var origType = OBGetOrigType(desc -> type);
	    CORBA_String_var s;
	    bool useVar = IdlIsVariable(origType) ||
		origType -> kind() == CORBA_tk_array;
	    if(useVar)
		s = getTypeString(scope, desc -> type, GetTypeVar);
	    else
		s = getTypeString(scope, desc -> type, GetTypeNormal);
	    out << '\n' << s << " _ob_r = " << name << "();";
	    out.sep();
	    out << "\nCORBA_ULong _ob_cnt = _ob_offOut;";
	    if(useVar)
		writeMarshalCountCode(desc -> type, false, "_ob_r.in()",
				      "_ob_cnt", out);
	    else
		writeMarshalCountCode(desc -> type, false, "_ob_r", "_ob_cnt",
				      out);
	    out.sep();
	    out << "\n_ob_buf.alloc(_ob_cnt);";
	    out << "\nCORBA_Octet* _ob_o = _ob_buf.data + _ob_offOut;";
	    if(useVar)
		writeMarshalCode(desc -> type, false, "_ob_r.in()", "_ob_o",
				 out);
	    else
		writeMarshalCode(desc -> type, false, "_ob_r", "_ob_o", out);
	    out.sep();
	    out << "\nreturn OBDispatchStatusOK;";
	    out.end();

	    if(desc -> mode == CORBA_ATTR_NORMAL)
	    {
		//
		// Don't use name here instead of desc -> name. We need
		// the name "unfixed".
		//
		out.sep();
		unsigned int indent = out.getIndent();
		out << "\nOBDispatchStatus\n" << skscope << "_OB_set_"
		    << desc -> name << '(';
		out.setIndent(out.getPos());
		out << "OBBuffer& _ob_buf,";
		out << "\nbool _ob_sw,";
		out << "\nCORBA_ULong _ob_offIn,";
		out << "\nCORBA_ULong _ob_offOut)";
		out.setIndent(indent);
		out.start();
		out << "\nconst CORBA_Octet* _ob_co = "
		    << "_ob_buf.data + _ob_offIn;";
		CORBA_String_var s = getTypeString(scope, desc -> type,
						   GetTypeStruct);
		out << '\n' << s << " _ob_a;";
		writeUnmarshalCode(desc -> type, true, "_ob_a", "_ob_co",
				   "_ob_sw", out);
		out.sep();
		if(origType -> kind() == CORBA_tk_array)
		{
		    out.direct("\n#ifdef HAVE_VCPLUSPLUS_BUGS");
		    CORBA_String_var conv =
			getTypeString(scope, desc -> type, GetTypeSlice);
		    out << '\n' << name << "((const " << conv << "*)_ob_a);";
		    out.direct("\n#else");
		}
		if(origType -> kind() == CORBA_tk_string ||
		   origType -> kind() == CORBA_tk_objref ||
		   origType -> kind() == CORBA_tk_TypeCode ||
		   origType -> kind() == CORBA_tk_Principal)
		{
		    out << '\n' << name << "(_ob_a.in());";
		}
		else
		{
		    out << '\n' << name << "(_ob_a);";
		}
		if(origType -> kind() == CORBA_tk_array)
		{
		    out.direct("\n#endif");
		    // HAVE_VCPLUSPLUS_BUGS
		}
		out.sep();
		out << "\n_ob_buf.alloc(_ob_offOut);";
		out.sep();
		out << "\nreturn OBDispatchStatusOK;";
		out.end();
	    }
            
            break;
        }
        
	case CORBA_dk_Operation:
	{
	    //
	    // Operation
	    //
	    CORBA_OperationDescription* desc;
	    CORBA_Boolean b = seq[i].value >>= desc;
	    assert(b);

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

	    CORBA_Identifier_var name = fixKwd(desc -> name);

	    comment(desc -> id, out);

	    //
	    // Don't use name here instead of desc -> name. We need
	    // the name "unfixed".
	    //
	    out.sep();
            unsigned int indent = out.getIndent();
	    out << "\nOBDispatchStatus\n" << skscope << "_OB_op_"
		<< desc -> name << '(';
            out.setIndent(out.getPos());
            out << "OBBuffer& _ob_buf,";
            out << "\nbool _ob_sw,";
            out << "\nCORBA_ULong _ob_offIn,";
            out << "\nCORBA_ULong _ob_offOut)";
            out.setIndent(indent);
            out.start();
	    
	    bool needUnmarshal = false;
	    for(j = 0 ; j < desc -> parameters.length() ; j++)
	    {
		if(desc -> parameters[j].mode != CORBA_PARAM_OUT)
		{
		    needUnmarshal = true;
		    break;
		}
	    }
	    if(desc -> contexts.length() > 0)
	    {
		needUnmarshal = true;
	    }
	    
	    if(needUnmarshal)
	    {
		out << "\nconst CORBA_Octet* _ob_co = "
		    << "_ob_buf.data + _ob_offIn;";
	    }
	    
	    for(j = 0 ; j < desc -> parameters.length() ; j++)
	    {
		CORBA_TypeCode_ptr parType = desc -> parameters[j].type;
		
		if(desc -> parameters[j].mode != CORBA_PARAM_OUT)
		{
		    CORBA_String_var s =
			getTypeString(scope, parType, GetTypeStruct);
		    out << '\n' << s << " _ob_a" << j << ';';
		    CORBA_String_var arg = CORBA_string_dup("_ob_a");
		    arg += j;
		    writeUnmarshalCode(parType, true, arg, "_ob_co", "_ob_sw",
				       out);
		}
		else
		{
		    CORBA_String_var s;
		    if(IdlIsVariable(parType))
			s = getTypeString(scope, parType, GetTypeVar);
		    else
			s = getTypeString(scope, parType, GetTypeNormal);
		    out << '\n' << s << " _ob_a" << j << ';';
		}
	    }
	    if(desc -> contexts.length() > 0)
	    {
		out << "\nOBStrSeq* _ob_ctxSeq = new OBStrSeq;";
		out << "\nOBUnmarshal(*_ob_ctxSeq, _ob_co, ";
		out << "_ob_sw);";
		out << "\nCORBA_Context_var _ob_ctx = "
		    << "CORBA_Context::_OB_create(_ob_ctxSeq);";
	    }
	    
	    if(desc -> parameters.length() > 0 ||
	       desc -> contexts.length() > 0)
		out.sep();
	    
	    if(desc -> exceptions.length() > 0)
	    {
		out << "\ntry";
		out.start();
	    }
	    
	    CORBA_TypeCode_var origRet = OBGetOrigType(desc -> result);
	    
	    int extra = 0;
	    for(j = 0 ; j < desc -> parameters.length() ; j++)
	    {
		if(desc -> parameters[j].mode == CORBA_PARAM_IN)
		{
		    CORBA_TypeCode_var origPar = desc -> parameters[j].type;
		    origPar = OBGetOrigType(origPar);
		    if(origPar -> kind() == CORBA_tk_array)
			extra = 2;
		}
	    }
	    
	    do
	    {
		if(extra == 2)
		    out.direct("\n#ifdef HAVE_VCPLUSPLUS_BUGS");
		if(origRet -> kind() != CORBA_tk_void)
		{
		    CORBA_String_var s;
		    if(origRet -> kind() == CORBA_tk_array ||
		       IdlIsVariable(origRet))
			s = getTypeString(scope, desc -> result, GetTypeVar);
		    else
			s = getTypeString(scope, desc -> result,
					  GetTypeNormal);
		    out << '\n' << s << " _ob_r = ";
		}
		else
		    out << '\n';
		out << name << '(';
		for(j = 0 ; j < desc -> parameters.length() ; j++)
		{
		    if(j != 0)
			out << ", ";
		    
		    CORBA_TypeCode_var origParType =
			desc -> parameters[j].type;
		    origParType = OBGetOrigType(origParType);
		    
		    if(extra == 2 &&
		       desc -> parameters[j].mode == CORBA_PARAM_IN)
		    {
			if(origParType -> kind() == CORBA_tk_array)
			{
			    CORBA_String_var conv =
				getTypeString(scope,
					      desc -> parameters[j].type,
					      GetTypeSlice);
			    out << "(const " << conv << "*)";
			}
		    }
		    
		    switch(desc -> parameters[j].mode)
		    {
		    case CORBA_PARAM_IN:
			if(origParType -> kind() == CORBA_tk_string ||
			   origParType -> kind() == CORBA_tk_objref ||
			   origParType -> kind() == CORBA_tk_TypeCode||
			   origParType -> kind() == CORBA_tk_Principal)
			{
			    out << "_ob_a" << j << ".in()";
			}
			else
			{
			    out << "_ob_a" << j;
			}
			break;
			
		    case CORBA_PARAM_INOUT:
			if(origParType -> kind() == CORBA_tk_string ||
			   origParType -> kind() == CORBA_tk_objref ||
			   origParType -> kind() == CORBA_tk_TypeCode ||
			   origParType -> kind() == CORBA_tk_Principal)
			{
			    out << "_ob_a" << j << ".inout()";
			}
			else
			{
			    out << "_ob_a" << j;
			}
			break;
			
		    case CORBA_PARAM_OUT:
			if(IdlIsVariable(origParType))
			    out << "_ob_a" << j << ".out()";
			else
			    out << "_ob_a" << j;
			break;
		    }
		}
		if(desc -> contexts.length() > 0)
		{
		    if(desc -> parameters.length() > 0)
			out << ", ";
		    
		    out << "_ob_ctx.in()";
		}
		out << ");";
		if(extra == 2)
		    out.direct("\n#else");
		extra--;
	    }
	    while(extra > 0);
	    
	    if(extra == 0)
	    {
		out.direct("\n#endif");
		// HAVE_VCPLUSPLUS_BUGS
	    }
                        
	    out.sep();
	    
	    out << "\nCORBA_ULong _ob_cnt = _ob_offOut;";
	    
	    bool needMarshal = false;
	    if(origRet -> kind() != CORBA_tk_void)
	    {
		bool useVar = IdlIsVariable(origRet) ||
		    origRet -> kind() == CORBA_tk_array;
		if(useVar)
		    writeMarshalCountCode(origRet, false,
					  "_ob_r.in()", "_ob_cnt", out);
		else
		    writeMarshalCountCode(origRet, false, "_ob_r", "_ob_cnt",
					  out);
		needMarshal = true;
	    }
	    
	    for(j = 0 ; j < desc -> parameters.length() ; j++)
		if(desc -> parameters[j].mode != CORBA_PARAM_IN)
		{
		    CORBA_TypeCode_ptr parType = desc -> parameters[j].type;
		    CORBA_String_var arg = CORBA_string_dup("_ob_a");
		    arg += j;
		    
		    if(desc -> parameters[j].mode != CORBA_PARAM_OUT)
		    {
			writeMarshalCountCode(parType, true, arg, "_ob_cnt",
					      out);
		    }
		    else
		    {
			if(IdlIsVariable(parType))
			    arg += ".in()";
			writeMarshalCountCode(parType, false, arg, "_ob_cnt",
					      out);
		    }
		    
		    needMarshal = true;
		}
	    
	    out.sep();
	    out << "\n_ob_buf.alloc(_ob_cnt);";
	    
	    if(needMarshal)
	    {
		out << "\nCORBA_Octet* _ob_o = _ob_buf.data + _ob_offOut;";
		
		if(origRet -> kind() != CORBA_tk_void)
		{
		    bool useVar = IdlIsVariable(origRet) ||
			origRet -> kind() == CORBA_tk_array;
		    if(useVar)
			writeMarshalCode(origRet, false, "_ob_r.in()", "_ob_o",
					 out);
		    else
			writeMarshalCode(origRet, false, "_ob_r", "_ob_o",
					 out);
		}
		
		for(j = 0 ; j < desc -> parameters.length() ; j++)
		    if(desc -> parameters[j].mode != CORBA_PARAM_IN)
		    {
			CORBA_TypeCode_ptr parType =
			    desc -> parameters[j].type;
			CORBA_String_var arg = CORBA_string_dup("_ob_a");
			arg += j;
			
			if(desc -> parameters[j].mode != CORBA_PARAM_OUT)
			{
			    writeMarshalCode(parType, true, arg, "_ob_o", out);
			}
			else
			{
			    if(IdlIsVariable(parType))
				arg += ".in()";
			    writeMarshalCode(parType, false, arg, "_ob_o",
					     out);
			}
		    }
	    }
	    
	    out.sep();
	    out << "\nreturn OBDispatchStatusOK;";
	    
	    if(desc -> exceptions.length() > 0)
	    {
		out.end();
		for(j = 0 ; j < desc -> exceptions.length() ; j++)
		{
		    CORBA_TypeCode_ptr exType =	desc -> exceptions[j].type;
		    CORBA_String_var s =
			getTypeString(scope, exType, GetTypeNormal);
		    out << "\ncatch(const " << s << "& _ob_except)";
		    out.start();
		    out << "\nCORBA_ULong _ob_cnt = _ob_offOut;";
		    out << "\nOBMarshalCount(_ob_except, _ob_cnt);";
		    out.sep();
		    out << "\n_ob_buf.alloc(_ob_cnt);";
		    out << "\nCORBA_Octet* _ob_o = _ob_buf.data + _ob_offOut;";
		    out << "\nOBMarshal(_ob_except, _ob_o);";
		    out.sep();
		    out << "\nreturn OBDispatchStatusExcept;";
		    out.end();
		}
	    }
	    
	    out.end();
	}

        default:
            break;
        }
    }
}
