/*
 *                         OpenSplice DDS
 *
 *   This software and documentation are Copyright 2006 to TO_YEAR PrismTech
 *   Limited, its affiliated companies and licensors. All rights reserved.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */
#include "idl.h"
#include "xbe_dispatchable.h"
#include "xbe_utils.h"
#include "xbe_dispatcher.h"
#include "xbe_argument.h"
#include "xbe_argument2.h"

// constructor for ops whose invoke name is the same as their servant name

be_Dispatcher::be_Dispatcher
(
   const be_CppName& cppOpName,
   const be_CppName& implClassName,
   const be_ArgumentList& arguments,
   be_ServerImplementation& source,
   be_DispatchableType* returnType
)
:
   m_servantName(cppOpName),
   m_invokeName(cppOpName),
   m_implClassName(implClassName),
   m_arglist(arguments),
   m_os(source.Stream()),
   m_tab(source),
   m_returnType(returnType),
   m_opReturnsValue(returnType != NULL
      && !str_eq(returnType->TypeName(), "void")),
   m_getargCount(m_arglist.NumArgsSentFromClient()),
   m_putargCount(m_arglist.NumArgsSentFromServer() + m_opReturnsValue)
{}

// constructor for ops with invoke name different than servant name

be_Dispatcher::be_Dispatcher
(
   const be_CppName& servantName,
   const be_CppName& invokeName,
   const be_CppName& implClassName,
   const be_ArgumentList& arguments,
   be_ServerImplementation& source,
   be_DispatchableType* returnType
)
:
   m_servantName(servantName),
   m_invokeName(invokeName),
   m_implClassName(implClassName),
   m_arglist(arguments),
   m_os(source.Stream()),
   m_tab(source),
   m_returnType(returnType),
   m_opReturnsValue(returnType != NULL
      && !str_eq(returnType->TypeName(), "void")),
   m_getargCount(m_arglist.NumArgsSentFromClient()),
   m_putargCount(m_arglist.NumArgsSentFromServer() + m_opReturnsValue)
{}

void be_Dispatcher::Generate ()
{
   DeclareDispatcherBody ();
   m_os << m_tab << "{" << nl;
   m_tab.indent ();

   DeclareStackVariables ();
   m_os << nl;

   MakeGetargArray ();
   UnmarshalRequest ();
   m_os << nl;

   CallOperation ();
   m_os << nl;

   MakePutargArray ();
   MarshalReply ();

   m_tab.outdent ();
   m_os << m_tab << "}" << nl << nl;
}

void be_Dispatcher::DeclareDispatcherBody ()
{
   m_os << m_tab << "void " << m_implClassName << "::_dispatch_" << m_invokeName << nl;
   m_os << m_tab << "(" << nl;
   m_tab.indent ();
   m_os << m_tab << "void * _servant_," << nl;
   m_os << m_tab << "DDS::Codec::Request & _request_" << nl;
   if (XBE_Ev::generate ())
   {
      m_os << m_tab << XBE_Ev::arg (XBE_ENV_ARGN, false) << nl;
   }
   m_tab.outdent ();
   m_os << m_tab << ")" << nl;
}

void be_Dispatcher::DeclareStackVariables ()
{
   be_ArgumentList::const_iterator iter;

   m_os << m_tab << m_implClassName << " * _srv_ = (" 
        << m_implClassName << "*) _servant_;" << nl;

   for (iter = m_arglist.begin(); iter != m_arglist.end(); ++iter)
   {
      const be_Argument& arg = *iter;
      arg.DeclareForDispatcher (m_os, m_tab, m_implClassName);
      arg.InitializeInDispatcher (m_os, m_tab);
   }

   if (m_opReturnsValue)
   {
      const be_Argument returnValue ("_ret_", *m_returnType, VT_Return);
      returnValue.DeclareForDispatcher (m_os, m_tab, m_implClassName);
   }
}

void be_Dispatcher::MakeGetargArray ()
{
   if (m_getargCount == 0)
   {
      return ;
   }

   m_os << m_tab << "DDS::Codec::Param _out_[" << m_getargCount << "] =" << nl;
   m_os << m_tab << "{" << nl;
   m_tab.indent ();

   bool wroteOne = false;

   // make a getarg for each argument that's sent from the client

   be_ArgumentList::const_iterator iter;

   for (iter = m_arglist.begin(); iter != m_arglist.end(); iter++)
   {
      const be_Argument& arg = *iter;

      if (arg.IsSentFromClient())
      {
         if (wroteOne)
         {
            m_os << "," << nl;
         }

         // a getarg looks like this:
         //    { _tc_TypeName, &arg, DDS::PARAM_IN }
         //      m_typecode   m_value         m_mode

         m_os << m_tab << "{ "
            << arg.ScopedTypeCodeTypeName () << ", "
            << arg.GetargMvalue () << ", "
            << arg.IopParamMmode (TRUE)
            << " }";

         wroteOne = true;
      }
   }

   m_os << nl;
   m_tab.outdent();
   m_os << m_tab << "};" << nl;
}

void be_Dispatcher::UnmarshalRequest ()
{
   m_os << m_tab << "_request_.get_args ("
        << (m_getargCount ? "_out_" : "0") << ", " << m_getargCount
        << XBE_Ev::arg (XBE_ENV_VARN) << ");" << nl;
   if (XBE_Ev::generate ())
   {
      m_os << m_tab;
      XBE_Ev::check (m_os, NULL);
      m_os << nl;
   }
}

void be_Dispatcher::CallOperation ()
{
   m_os << m_tab;

   if (m_opReturnsValue)
   {
      m_os << "_ret_ = ";
   }

   m_os << "_srv_->" << m_servantName << " (";

   // arguments

   bool wroteOne = false;
   int getargIndex = 0;
   be_ArgumentList::const_iterator iter;

   for (iter = m_arglist.begin(); iter != m_arglist.end(); iter++)
   {
      const be_Argument& arg = *iter;

      if (wroteOne)
      {
         m_os << ", ";
      }

      m_os << arg.PassToServantMethod (getargIndex);

      wroteOne = true;

      if (arg.IsSentFromClient())
      {
         // if arg is sent from client, then it has an element in the
         // getarg array
         getargIndex++;
      }
   }

   // env

   if (!wroteOne)
   {
      m_os << XBE_Ev::arg (XBE_ENV_VAR1);
   }
   else
   {
      m_os << XBE_Ev::arg (XBE_ENV_VARN);
   }

   m_os << ");" << nl;
   if (XBE_Ev::generate ())
   {
      m_os << m_tab;
      XBE_Ev::check (m_os, NULL);
      m_os << nl;
   }
}

void be_Dispatcher::MakePutarg
(
   ostream& os,
   be_Tab& tab,
   const be_Argument& arg,
   int getargIndex
) const
{
   // a Putarg looks like this:
   //    { _tc_TypeName, &arg, DDS::PARAM_IN }
   //      m_typecode   m_value         m_mode

   os << tab << "{ "
      << arg.ScopedTypeCodeTypeName () << ", "
      << arg.PutargMvalue (getargIndex) << ", "
      << arg.IopParamMmode (TRUE)
      << " }";
}

void be_Dispatcher::MakePutargArray ()
{
   if (m_putargCount == 0)
   {
      return ;
   }

   bool wroteOne = false;
   int getargIndex = 0;

   m_os << m_tab << "DDS::Codec::Param _in_[" << m_putargCount << "] =" << nl;
   m_os << m_tab << "{" << nl;
   m_tab.indent ();

   // return value

   if (m_opReturnsValue)
   {
      MakePutarg (m_os, m_tab, be_Argument("_ret_", *m_returnType, VT_Return), getargIndex);

      wroteOne = true;
   }

   // make a putarg for each argument sent from the server

   be_ArgumentList::const_iterator iter;

   for (iter = m_arglist.begin(); iter != m_arglist.end(); iter++)
   {
      const be_Argument& arg = *iter;

      if (arg.IsSentFromServer())
      {
         if (wroteOne)
         {
            m_os << "," << nl;
         }

         MakePutarg (m_os, m_tab, arg, getargIndex);

         wroteOne = true;
      }

      if (arg.IsSentFromClient())
      {
         // if arg is sent from client, then it has an element in the
         // getarg array
         getargIndex++;
      }
   }

   m_os << nl;
   m_tab.outdent ();
   m_os << m_tab << "};" << nl;
}

void be_Dispatcher::MarshalReply ()
{
   m_os << m_tab << "_request_.put_args ("
        << (m_putargCount ? "_in_" : "0") << ", " << m_putargCount
        << XBE_Ev::arg (XBE_ENV_VARN) << ");" << nl;
}
