/*<ATGCOPYRIGHT>
 * Copyright (C) 2000 Art Technology Group, Inc.
 * All Rights Reserved.  No use, copying or distribution of this
 * work may be made except in accordance with a valid license
 * agreement from Art Technology Group.  This notice must be
 * included on all copies, modifications and derivatives of this
 * work.
 *
 * Art Technology Group (ATG) MAKES NO REPRESENTATIONS OR WARRANTIES 
 * ABOUT THE SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. ATG SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, 
 * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * "Dynamo" is a trademark of Art Technology Group, Inc.
 </ATGCOPYRIGHT>*/

#include "DynSock.h"
#include "DRPTypes.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef WIN32
#include <winsock.h>
#include <io.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#endif

#define DYNSOCKET_VERSION "dynSocket1.0"

/**************************
 * Communication Routines *
 **************************/

/**
 * dynSock_int(pInitData)
 *
 * Initialize any dynSocket global structures
 * Returns 0 on sucess
 */
int dynSocket_init(void *pInitData)
{
    return 0;
}

/**
 * dynSocket *dynSocket_new(pInfo) 
 *
 * Creates a new dynSocket using information contained in pInfo.
 * Returns a pointer to the new dynSocket if successful,
 * ATG_INVALID_DYNSOCKET otherwise.
 *
 * NOTE: NEVER call any of the other dynSocket_xxx calls with a
 * pointer to an invalid dynSocket, i.e. the pointer has a value of
 * ATG_INVALID_DYNSOCKET.
 */
#ifdef _ISAPI
dynSocket *dynSocket_new(HANDLE gHeap, dynSocketInfo * pInfo)
{
#else
dynSocket *dynSocket_new(dynSocketInfo * pInfo)
{
#endif
    dynSocket *dynSock = NULL;
    socket_t s = ATG_INVALID_SOCKET;

    /* Create a DynSocket */
#ifdef _ISAPI
    dynSock = (dynSocket *) HeapAlloc(gHeap, 0, sizeof(dynSocket));
#else
    dynSock = (dynSocket *) malloc(sizeof(dynSocket));
#endif
    if (dynSock == NULL)
        return ATG_INVALID_DYNSOCKET;

    /* Zero out memory */
    memset(dynSock, 0, sizeof(dynSocket));

    /*Set Version */
    strcpy(dynSock->version, DYNSOCKET_VERSION);

    /*Set Socket Info  */
    dynSock->info = pInfo;

    /* Create the socket */
    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s == ATG_INVALID_SOCKET) {
#ifdef _ISAPI
        (void) HeapFree(gHeap, 0, dynSock);
#else
        free(dynSock);
#endif
        return ATG_INVALID_DYNSOCKET;
    }
    dynSock->socket_data = (void *) s;
    dynSock->connected = 0;

    /* Manipulate operating characteristics of 
     * socket with ioctl here 
     */

    return dynSock;
}

/**
 * int dynSocket_connect(pDynSocket,pRemoteAddr,pLen)
 *
 * Connects pDynSocket to pRemoteAddr. Use dySocket_close(pDynSocket)
 * to close this dynSocket.
 * On success zero is returned. Otherwise ATG_SOCKET_ERROR is returned.
 */

int dynSocket_connect(dynSocket * pDynSocket,
                      struct sockaddr *pRemoteAddr, int pLen)
{
    int ret = 0;

    /* Get the socket */
    socket_t s = (socket_t) pDynSocket->socket_data;

    /* Connect to the remote host */
    ret = connect(s, pRemoteAddr, pLen);
    if (ret == ATG_SOCKET_ERROR) {
        return ret;
    }
    memcpy(&(pDynSocket->remote_addr), pRemoteAddr,
           sizeof(struct sockaddr));
    pDynSocket->remote_addr_len = pLen;
    pDynSocket->connected = 1;

    return ret;
}

/**
 * int dynSocket_close(pDynSocket)
 *
 * Close a pDynSocket and does clean up.
 * On success returns zero, or ATG_SOCKET_ERROR if an error occurred.
 */
#ifdef _ISAPI
int dynSocket_close(HANDLE gHeap, dynSocket * pDynSocket)
{
#else
int dynSocket_close(dynSocket * pDynSocket)
{
#endif
    socket_t s = (socket_t) (pDynSocket->socket_data);

    pDynSocket->connected = 0;
#ifdef _ISAPI
    (void) HeapFree(gHeap, 0, pDynSocket);
#else
    free(pDynSocket);
#endif

#ifdef WIN32
    return closesocket(s);
#else
    return close(s);
#endif
}

#ifndef WIN32
/**
 * int dynSocket_read(pDynSocket,pBuf,pCount)
 *
 * Only for UNIX.
 * Read data from dynSocket. Attempt to read up to count bytes from
 * pDynSocket into pBuf.
 * On Success, the number of bytes read is returned. Zero indicates
 * the end of file. On error -1 is returned.
 */
int dynSocket_read(dynSocket * pDynSocket, void *pBuf, size_t pCount)
{
    return read((socket_t) (pDynSocket->socket_data), pBuf, pCount);
}

/**
 * int dynSocket_write(pDynSocket,pBuf,pCount)
 *
 * Only for UNIX.
 * Write data to dynSocket. Attempt to write up to pCount bytes to
 * the pDynSocket from pBuf.
 * On success, the number of byte written are returned. Zero indicates
 * nothing was written. On error -1 is returned.
 */
int dynSocket_write(dynSocket * pDynSocket, const void *pBuf,
                    size_t pCount)
{
    return write((socket_t) (pDynSocket->socket_data), pBuf, pCount);
}
#endif

/**
 * dynSocket_recv(pDynSocket,pBuf,pLen,pFlags)
 *
 * Receive data from a dynSocket. Attepm to read up to pLen bytes from
 * pDynSocket into pBuf
 * On Success, the number of bytes reveived is returned. Zero indicates
 * the end of file. On error ATG_SOCKET_ERROR is returned.
 */
int dynSocket_recv(dynSocket * pDynSocket, void *pBuf, int pLen,
                   int pFlags)
{
    return recv((socket_t) (pDynSocket->socket_data), pBuf, pLen, pFlags);
}

/**
 * dynSocket_send(pDynSocket,pMsg,pLen,pFlags)
 *
 * Send data on dynSocket. Attempt to send up to pCount bytes to
 * the pDynSocket from pBuf.
 * On success, the number of byte sent are returned. Zero indicates
 * nothing was sent. On error ATG_SOCKET_ERROR is returned.
 */
int dynSocket_send(dynSocket * pDynSocket, const void *pMsg, int pLen,
                   int pFlags)
{
    return send((socket_t) (pDynSocket->socket_data), pMsg, pLen, pFlags);
}

/**********************
 * DynSock Properties *
 **********************/

/**
 * char *dynSocket_version(pDynSocket)
 *
 * Return version of pDynSocket
 */
char *dynSocket_version(dynSocket * pDynSocket)
{
    return pDynSocket->version;
}

/**
 * dynSocketInfo *dynSocket_info(pDynSocket)
 *
 * Return the info struct for pDynSocket
 */
dynSocketInfo *dynSocket_info(dynSocket * pDynSocket)
{
    return pDynSocket->info;
}

/**
 * void *dynSocket_socket_data(pDynSocket)
 *
 * Return the socket data for pDynSocket
 */
void *dynSocket_socket_data(dynSocket * pDynSocket)
{
    return pDynSocket->socket_data;
}

/**
 * int dynSocket_connected(pDynSocket) {
 *
 * Return the connected field for pDynSocket
 */
int dynSocket_connected(dynSocket * pDynSocket)
{
    return pDynSocket->connected;
}


/** 
 * struct sockaddr *dynSocket_remote_addr(pDynSocket)
 *
 * Return the remote address for pDynSocket 
 */
struct sockaddr *dynSocket_remote_addr(dynSocket * pDynSocket)
{
    return &(pDynSocket->remote_addr);
}

/**
 * int dynSocket_remote_addr_len(*pDynSocket) 
 *
 * Return remote address length for pDynSocket
 */
int dynSocket_remote_addr_len(dynSocket * pDynSocket)
{
    return pDynSocket->remote_addr_len;
}

/**
 * socket_t dynSocket_socket(pDynSocket)
 *
 * Return the raw socket for pDynSocket 
 */
socket_t dynSocket_socket(dynSocket * pDynSocket)
{
    return (socket_t) (pDynSocket->socket_data);
}
