/*
_____       _    _    Corso   Italia,  178
(_|__   .  (_   |_|_  56125           Pisa
(_|_) |)|(()_)()| |   tel.  +39  050 46380
  |   |               picosoft@picosoft.it

 picoiiop - network support for picoSQL

 Copyright (C) Picosoft s.r.l. 1994-2002

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
# ifdef WIN32
# include <io.h>
# else
# include <unistd.h>
# endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include "listener.h"
#include "message.h"
#include "giop.h"
#include "log.h"

static char *rcsid = "$Id: listener.c,v 1.2 2002/05/06 10:22:12 picoSoft Exp $";
static char *rcsidh = listener_h;

Listener *
Listener_new()
{
   Listener *this = (Listener *)malloc (sizeof(Listener));
   if (this) {
      this->methods = 0;
      this->obj = 0;
      this->conn = 0;
   }
   return this;
}

void
Listener_delete(Listener *this)
{
   if (this) {
      if (this->conn)
         Connection_delete (this->conn);
      free(this);
   }
}

/* Vanno gestiti i miliardi di errori che possono avvenire */

static CORBA_Boolean
Listener_getMessages(Listener *this, Exception *ex)
{
   CORBA_Boolean Return = CORBA_True;
   CorbaMethod *mt;
   MessageHeader he;
   int argc = 0;
   void **argv = 0;
   Message *msg = Message_new();

   if (this->methods == 0) {
      Exception_set (ex, ErrorCode_NOT_INIT, "Invoke object not initialized");
      return CORBA_False;
   } 
   /* Qui ci dovrebbero essere i signal per trappare problemi e reinviare
    * un messaggio d'eccezione */

   if (!Connection_recv (this->conn, ex, msg->logicBuffer, GIOP_HEADERSIZE)) {
      Exception_log (ex);
      Return = CORBA_False;
   } else {
      if (!GiopCheckHeader (&he, msg)) {
         Exception_set (ex, ErrorCode_INVALID_HEADER, "Error in reception");
         Exception_log (ex);
         Return = CORBA_False;
      } else if (he.type == MessageError) {
         /* ... */
         Return = CORBA_False;
         Log ("type==MessageError");
      } else {
         unsigned char *buf = Message_getBuffer (msg,
                                                 he.size + GIOP_HEADERSIZE,
                                                 he.byteOrder);
         CORBA_Principal *objectKey = 0;
         CORBA_String operation = 0;
         CORBA_Principal *principal = 0;
         CORBA_ULong nContext;
         CorbaArg *arg;

         Log ("maj vers=%d", he.giopVersion.major);
         Log ("min vers=%d", he.giopVersion.minor);
         Log ("byteOrds=%d", he.byteOrder);
         Log ("type    =%d", he.type);
         Log ("size    =%d", he.size);

         buf += GIOP_HEADERSIZE;
         if (!Connection_recv (this->conn, ex, buf, he.size)) {
            Exception_log (ex);
            Return = CORBA_False;
         } else {
            ExceptionType excType = SYSTEM_EXCEPTION;
            Message_getULong (msg, &nContext);
            for ( ; nContext > 0; nContext--) {
               CORBA_ULong contextId;
               CORBA_Principal *context = 0;
               Message_getULong (msg, &contextId);
               Message_decode (msg, ex, &TCD_Principal,
                               (void **)&context, CORBA_False);
               /* Non so cosa fare dei contesti per cui ^^^
               CORBA_Object_delete (context, &TCD_Principal); */
            }
            Message_getULong (msg, &this->requestId);
            Message_getBoolean (msg, &this->responseExpected);
            Message_decode (msg, ex, &TCD_Principal, (void **)&objectKey,
                            CORBA_True);
            Message_decode (msg, ex, &TCD_string, (void **)&operation,
                            CORBA_True);
            Message_decode (msg, ex, &TCD_Principal, (void **)&principal,
                            CORBA_True);
            Log ("requsetId = %ld", this->requestId);
            Log ("respExpec = %d", this->responseExpected);
            Log ("key       = %ld,[%s]", objectKey->size, objectKey->data);
            Log ("operation = [%s]", operation);
            for (mt = this->methods; mt->name; mt++) {
               if (strcmp (operation, mt->name) == 0) {
                  if (mt->args) {
                     for (argc = 0, arg = mt->args ;
                          arg->type != &TCD_KIND_LAST && arg->mode != END_PARAM;
                          arg++, argc++)
                        ;
                     argv = (void **) malloc (argc * sizeof(void*));
                     memset ((char *) argv, 0, argc * sizeof(void*));
                     for (argc = 0, arg = mt->args ;
                          arg->type != &TCD_KIND_LAST && arg->mode != END_PARAM;
                          arg++, argc++)
                        if (arg->mode == PARAM_IN || arg->mode == PARAM_INOUT)
                           Message_decode (msg, ex, arg->type, &(argv[argc]),
                                           CORBA_True);
                        else
                           argv[argc] = 0;
                  }
                  Log ("# args    = %d", argc);
                  mt->meth (argv);
                  excType = NO_EXCEPTION;
                  break;
               }
            }
            if (this->responseExpected) {
               CORBA_ULong offsLenMess;
               Message_delete (msg);
               msg = Message_new ();
               offsLenMess = GiopHeader (msg, Reply);
               Message_putLong (msg, 0);           /* service context */
               Message_putULong (msg, this->requestId); /* request id */
               Message_putULong (msg, excType);
               if (excType == NO_EXCEPTION) {
                  if (mt->args) {
                     for (argc = 0, arg = mt->args ;
                          arg->type != &TCD_KIND_LAST && arg->mode!=END_PARAM;
                          arg++, argc++)
                     if (arg->mode == PARAM_OUT || arg->mode == PARAM_INOUT)
                        if (!Message_encode (msg, ex, arg->type, argv[argc]))
                            break;
                  }
               } else {
                  Message_putString(msg,"IDL:omg.org/CORBA/BAD_OPERATION:1.0");
                  Message_putULong (msg, 0); /* maj ver */
                  Message_putULong (msg, 1); /* min ver */
               }

               *((CORBA_ULong *)(&msg->logicBuffer[offsLenMess])) =
                                 Message_getSize(msg) - GIOP_HEADERSIZE;
               
               Log ("offsLenMess = %ld",
                    *((CORBA_ULong *)(&msg->logicBuffer[offsLenMess])));

               if (!Connection_send (this->conn, ex, 
                                     msg->logicBuffer, Message_getSize(msg))) {
                   Return = CORBA_False;
                   Exception_log (ex);
               }
            }
            if (mt->args) {
               for (argc = 0, arg = mt->args ;
                    arg->type != &TCD_KIND_LAST && arg->mode != END_PARAM;
                    arg++, argc++)
                  CORBA_Object_delete (argv[argc], arg->type);
               free (argv);
            } 
            CORBA_Object_delete (principal, &TCD_Principal);
            CORBA_Object_delete (operation, &TCD_string);
            CORBA_Object_delete (objectKey, &TCD_Principal);
            if (CORBA_shutdown) {
               Connection_delete (this->conn);
               Return = CORBA_False;
            }
         }
      }
   }
   if (msg)
      Message_delete (msg);
   return Return;
}

void
Listener_go (Listener *this, Exception *ex, CorbaMethod *meth, IiopObj *obj)
{
   if (meth)
      this->methods = meth;
   this->obj = obj;
   if (this->conn)
      Connection_delete (this->conn);
   this->conn = Connection_new (obj);
   if (this->conn) {
      while (Listener_getMessages(this, ex))
         ;
   } else
      Exception_set (ex, ErrorCode_COMM_ERROR, "Cannot create connection");
}


