/*!*********************************************************************

    @file         ProcRTE_LocalCommunication.c
    
    @author       DanielD

    @brief        simulated local communication from
                    proc server to kernel

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2003-2005 SAP AG

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

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end



\endif
***********************************************************************/

#include "RunTime/ProcServer/ProcRTE_LocalCommunication.h"

#if defined (WIN32)
#include <windows.h>
#endif
#include "heo46.h"
#include "geo007_2.h"

 /*
 * ========== temporary implementation of proc server communication
 */
const int lenBytesC = 4;
typedef SOCKET procrte_handle;
const procrte_handle invalidHandleC = INVALID_SOCKET;
const int socketErrorC = SOCKET_ERROR;
static bool socketsInitialized = false;
static WSADATA wsaData;
#define INIT_SOCKET_LIB() if (!socketsInitialized) {\
    WSAStartup (MAKEWORD(1, 1), &wsaData);\
    socketsInitialized = true;}
#define LASTERROR GetLastError()
 
/*----------------------------------------*/

typedef struct t_procserver_class {
    TOSCM_COMM_VMT      *vmt;
    char                *packetMem;
    char                *requestMem;
    procrte_handle       handle;
} t_procserver_class;
 
/*----------------------------------------*/

static tsp01_CommErr
procserver_connect (
    TOSCM_COMM_CLASS *commClass,
    CONNECT_PARAM_REC*pConnParam,
    PSZ               pszServerDB,
    PSZ               pszDBRoot,
    PSZ               pszServerPgm,
    tsp00_ErrText     errtext)
{
#undef MF__
#define MF__ MOD__"procserver_connect"
    t_procserver_class *self = (t_procserver_class*) commClass;
    const char * portString = pszServerDB + 2;
    const char * sizeString = strchr (portString, ':');
    int          port;
    int          packetSize;
    int          allocatedSize;
    APIRET       rc;
    unsigned int ulCnt;
    
    /*
     * parse url
     */
    port = atoi (portString);
    packetSize = atoi (sizeString);
    /*
     * alloc packet
     */
    allocatedSize = 0;
    pConnParam->ulMinReplySize = 100;
    pConnParam->ulPacketSize = packetSize + pConnParam->ulMinReplySize;
    pConnParam->ulMaxDataLen = pConnParam->ulPacketSize
        - (2 * RTE_HEADER_SIZE);
    allocatedSize += pConnParam->ulPacketCnt * pConnParam->ulPacketSize;
    rc = ALLOC_MEM ((PPVOID)&self->packetMem, allocatedSize);
    if( rc != NO_ERROR ) {
        sql46c_build_error_string ( errtext, ERRMSG_ALLOC_MEMORY, rc );
        return commErrNotOk_esp01;
    }
    for ( ulCnt = 0; ulCnt < pConnParam->ulPacketCnt; ulCnt++ )
    {
      pConnParam->pCommPacketList[ulCnt] = (PCOMM_PACKET_REC)
              (self->packetMem + (ulCnt * pConnParam->ulPacketSize));
    }
    /*
     * connect to socket
     */
    INIT_SOCKET_LIB();
    {
        unsigned long destAddr = inet_addr ("127.0.0.1");
        struct sockaddr_in destSockAddr;

        memcpy (&destSockAddr.sin_addr, &destAddr, sizeof (destAddr));
        destSockAddr.sin_port = htons ((u_short) port);
        destSockAddr.sin_family = AF_INET;

        self->handle = socket (AF_INET, SOCK_STREAM, 0);
        if (self->handle == invalidHandleC) {
            strcpy (errtext, "socket () failed");
            return commErrNotOk_esp01;
        }
        rc = connect (self->handle, (const struct sockaddr *)&destSockAddr, 
            sizeof (destSockAddr));
        if (rc == socketErrorC) {
            closesocket (self->handle);
            self->handle = invalidHandleC;
            strcpy (errtext, "connect () failed");
            return commErrNotOk_esp01;
        }
    }
    return commErrOk_esp01;
}

/*----------------------------------------*/

static tsp01_CommErr
procserver_release (
    TOSCM_COMM_CLASS *commClass)
{
#undef MF__
#define MF__ MOD__"procserver_release"
    t_procserver_class * self = (t_procserver_class*) commClass;
    /*
     * close socket
     */
    closesocket (self->handle);
    /*
     * free packet
     */
    FREE_MEM (self->packetMem);
    FREE_MEM (self);
    return commErrOk_esp01;
}

/*----------------------------------------*/

static tsp01_CommErr
procserver_request (
    TOSCM_COMM_CLASS *commClass,
    void             *sql_packet,
    tsp00_Int4          length,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"procserver_request"
    t_procserver_class *self = (t_procserver_class*) commClass;
    /*
     * send length
     */
    int rc;
    bool ok = false;
    unsigned char lenbuf [sizeof (tsp00_Int4)];
    
    self->requestMem = sql_packet;
    // ProcRTE_Runtime::log ("sending %d bytes", length);
    fprintf (stdout, "sending %d bytes", length);
    lenbuf [3] = length % 256;
    lenbuf [2] = (length >>  8) % 256;
    lenbuf [1] = (length >> 16) % 256;
    lenbuf [0] = (length >> 24) % 256;
    rc = send (self->handle, (const char *) lenbuf, sizeof (lenbuf), 0);
    /*
     * send data
     */
    if (rc == sizeof (lenbuf)) {
        if (length > 0) {
            rc = send (self->handle, sql_packet, length, 0);
            if (rc == length) {
                ok = true;
            }
            else {
                ok = false;
                strcpy (errtext, "send data failed");
            }
        }
        else {
            ok = true;
        }
    }
    else {
        strcpy (errtext, "send length failed");
    }
    if (ok) {
        return commErrOk_esp01;
    }
    else {
        return commErrNotOk_esp01;
    }
}

/*----------------------------------------*/

static tsp01_CommErr
procserver_receive (
    TOSCM_COMM_CLASS *commClass,
    void            **sql_packet,
    tsp00_Int4         *length,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"procserver_receive"
    t_procserver_class *self = (t_procserver_class*) commClass;
    int rc;
    unsigned char lenbuf [sizeof (tsp00_Int4)];
    int bytesRemaining;
    int bytesRead = 0;
    char * receivePtr;
    
    rc = recv (self->handle, (char *) lenbuf, sizeof (lenbuf), 0);
    if (rc != sizeof (lenbuf)) {
        strcpy (errtext, "recv of length failed");
        return commErrNotOk_esp01;
    }
    bytesRemaining = (lenbuf [0] << 24)
                   + (lenbuf [1] << 16)
                   + (lenbuf [2] <<  8)
                   + (lenbuf [3]);
    if (self->requestMem == NULL) {
        strcpy (errtext, "no previous sqlarequest");
        return commErrNotOk_esp01;
    }
    receivePtr = self->requestMem;
    while ((rc > 0) && (bytesRemaining > 0)) {
        rc = recv (self->handle, receivePtr, bytesRemaining, 0);
        if (rc < 0) {
            int osRC = GetLastError ();
            strcpy (errtext, "recv data failed");
            return commErrNotOk_esp01;
        }
        if (rc > 0) {
            bytesRemaining -= rc;
            bytesRead += rc;
            receivePtr += rc;
        }
    }
    *sql_packet = self->requestMem - offsetof (COMM_PACKET_REC, pDataPart);
    *length = bytesRead;
    self->requestMem = NULL;
    return commErrOk_esp01;
}

/*----------------------------------------*/

static tsp01_CommErr
procserver_replyavailable (
    TOSCM_COMM_CLASS *commClass,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"procserver_replyavailable"
    t_procserver_class *self = (t_procserver_class*) commClass;
    strcpy (errtext, "not implemented");
    return commErrNotOk_esp01;
}

/*----------------------------------------*/

static tsp01_CommErr
procserver_cancel (
    TOSCM_COMM_CLASS *commClass,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"procserver_cancel"
    t_procserver_class *self = (t_procserver_class*) commClass;
    strcpy (errtext, "not implemented");
    return commErrNotOk_esp01;
}

/*----------------------------------------*/

static TOSCM_COMM_VMT procserverVMT = {
    "ProcServer-Communication (vos03u.c)",
    procserver_connect,
    procserver_release,
    procserver_request,
    procserver_receive,
    procserver_replyavailable,
    procserver_cancel
};

/*----------------------------------------*/

static TOSCM_COMM_CLASS *
procserver_constructor (
    void          * commInfo,
    tsp00_ErrText   errtext,
    tsp01_CommErr * returncode)
{
#undef MF__
#define MF__ MOD__"procserver_constructor"
    t_procserver_class  *self;
    APIRET              rc;

    // alloc self
    rc = ALLOC_MEM ((PPVOID)&self, sizeof(t_procserver_class) );
    if( rc != NO_ERROR ) {
        sql46c_build_error_string ( errtext, ERRMSG_ALLOC_MEMORY, rc );
        *returncode = commErrNotOk_esp01;
        return NULL;
    }
    memset (self, '\0', sizeof (t_procserver_class));
    self->vmt = &procserverVMT;
    *returncode = commErrOk_esp01;
    return (TOSCM_COMM_CLASS*) self;
}

/*----------------------------------------*/

void
procRTE_installConstructor ()
{
    extern TOSCM_COMMSTRUCTOR_FT * sql03c_constructorPointer;
    sql03c_constructorPointer = procserver_constructor;
}

