/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * 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.
 * -------------------------------------------------------------------
 */
/*!
** Socket implementation for PV socket server
*/

#include "oscl_socket_tuneables.h"
#if(PV_SOCKET_SERVER)
#include "oscl_socket_imp_pv.h"
#include "oscl_socket_serv_imp_pv.h"
#include "oscl_socket_method.h"

//Stats macros for use with OsclSocketI
#if(PV_OSCL_SOCKET_STATS_LOGGING)
#define ADD_STATS(x,y) iStats.Add(x,y)
#define CONSTRUCT_STATS(x,y) iStats.Construct(x,y)
#else
#define ADD_STATS(x,y)
#define CONSTRUCT_STATS(x,y)
#endif

OsclSocketI::OsclSocketI(Oscl_DefAlloc &a) : OsclSocketIBase(a)
{
    iSocketServ = NULL;
    InitSocket(false);

    iSockServAcceptRequest.iSocketI = this;
    iSockServConnectRequest.iSocketI = this;
    iSockServRecvRequest.iSocketI = this;
    iSockServRecvFromRequest.iSocketI = this;
    iSockServSendRequest.iSocketI = this;
    iSockServSendToRequest.iSocketI = this;
    iSockServShutdownRequest.iSocketI = this;
    iLogger = PVLogger::GetLoggerObject("osclsocket");
}

TPVSocketEvent OsclSocketI::ThreadLogoff()
{
    iLogger = NULL;
    iSocketServ = NULL;
#if(PV_OSCL_SOCKET_STATS_LOGGING)
    iStats.ThreadLogoff();
#endif
    return EPVSocketSuccess;
}

TPVSocketEvent OsclSocketI::ThreadLogon(OsclSocketServI* aServ)
{
    iLogger = PVLogger::GetLoggerObject("osclsocket");
    iSocketServ = aServ;
#if(PV_OSCL_SOCKET_STATS_LOGGING)
    iStats.ThreadLogon();
#endif
    return EPVSocketSuccess;
}
OsclSocketI::~OsclSocketI()
{
    Close();
}

OsclSocketI* OsclSocketI::NewL(Oscl_DefAlloc &a)
{
    OsclAny*p = a.ALLOCATE(sizeof(OsclSocketI));
    OsclError::LeaveIfNull(p);
    OsclSocketI *self = OSCL_PLACEMENT_NEW(p, OsclSocketI(a));
    OsclError::LeaveIfNull(self);
    return self;
}

int32 OsclSocketI::Close()
{
    int sockerr = 0;

    //cleanup the OS-level socket
    if (iSocketValid)
    {
#ifdef OsclCloseSocket
        //call the platform socket close routine...
        bool ok;
        OsclCloseSocket(iSocket, ok, sockerr);
#else
        sockerr = PVSOCK_ERR_NOT_IMPLEMENTED;
#endif
    }

    InitSocket(false);
    return sockerr;

}

bool OsclSocketIBase::HasAsyncBind()
{
    return false;
}

int32 OsclSocketI::Bind(OsclNetworkAddress& anAddr)
{
#ifdef OsclBind
    TOsclSockAddr addr;
    MakeAddr(anAddr, addr);
    int err;
    bool ok;
    OsclBind(iSocket, addr, ok, err);
    if (!ok)
        return err;
#if (PV_OSCL_SOCKET_1MB_RECV_BUF)
    int bufsize = (1024 * 1024);
    OsclSetRecvBufferSize(iSocket, bufsize, ok, err);
#endif
    return OsclErrNone;
#else
    return PVSOCK_ERR_NOT_IMPLEMENTED;
#endif
}

int32 OsclSocketI::GetOsclSockOptLevelName(const TPVSocketOptionLevel aOptionLevel, const TPVSocketOptionName aOptionName, int32& aOsclOptionLevel, int32& aOsclOptionName)
{
    int32 retval = OsclErrNone;
    switch (aOptionLevel)
    {
        case EPVIPProtoIP:
        {
            aOsclOptionLevel = OSCL_SOL_IP;
            switch (aOptionName)
            {
                case EPVIPMulticastTTL:
                    aOsclOptionName =   OSCL_SOCKOPT_IP_MULTICAST_TTL;
                    break;
                case EPVIPAddMembership:
                    aOsclOptionName =   OSCL_SOCKOPT_IP_ADDMEMBERSHIP;
                    break;
                case EPVIPTOS:
                    aOsclOptionName =   OSCL_SOCKOPT_IP_TOS;
                    break;
                default:
                    retval = PVSOCK_ERR_BAD_PARAM;
            }
        }
        break;
        case EPVIPProtoTCP:
        {
            aOsclOptionLevel = OSCL_SOL_TCP;
            retval = PVSOCK_ERR_BAD_PARAM; // None of socket options at TCP level supported in OSCL presently
        }
        break;
        case EPVSocket:
        {
            aOsclOptionLevel = OSCL_SOL_SOCKET;
            switch (aOptionName)
            {
                case EPVSockReuseAddr:
                    aOsclOptionName = OSCL_SOCKOPT_SOL_REUSEADDR;
                    break;
                default:
                    retval = PVSOCK_ERR_BAD_PARAM;
            }
        }
        break;
        default:
            retval = PVSOCK_ERR_BAD_PARAM;
    }
    return retval;
}

int32 OsclSocketI::SetSockOpt(TPVSocketOptionLevel aOptionLevel, TPVSocketOptionName aOptionName, OsclAny* aOptionValue, int32 aOptionLen)
{
    int32 retval = OsclErrNone;
#ifdef OsclSetSockOpt
    int32 sockOptLevel, sockOptName;
    if (PVSOCK_ERR_BAD_PARAM != GetOsclSockOptLevelName(aOptionLevel, aOptionName, sockOptLevel, sockOptName))
    {
        if ((PVSOCK_ERR_NOT_SUPPORTED != sockOptLevel) && (PVSOCK_ERR_NOT_SUPPORTED != sockOptName))
        {
            int32 err = 0;
            bool ok;
            OsclSetSockOpt(iSocket, sockOptLevel, sockOptName, aOptionValue, aOptionLen, ok, err);
            if (!ok)
                retval = err;
        }
        else
            retval = PVSOCK_ERR_NOT_SUPPORTED;
    }
    else
        retval = PVSOCK_ERR_BAD_PARAM;
#else
    retval = PVSOCK_ERR_NOT_IMPLEMENTED;
#endif
    return retval;
}

int32 OsclSocketI::GetPeerName(OsclNetworkAddress& peerName)
{
#ifdef OsclGetPeerName
    TOsclSockAddr peerAddr;
    int32 peerAddrLen = sizeof(peerAddr);
    int32 err = 0;
    bool ok;
    OsclGetPeerName(iSocket, peerAddr, peerAddrLen, ok, err);
    if (!ok)
        return err;

    MakeAddr(peerAddr, peerName);
    return OsclErrNone;

#else
    return PVSOCK_ERR_NOT_IMPLEMENTED;
#endif
}
int32 OsclSocketI::Join(OsclNetworkAddress& anAddr)
{
#ifdef OsclJoin
    TOsclSockAddr addr;
    MakeAddr(anAddr, addr);
    int err;
    bool ok;
    OsclJoin(iSocket, addr, ok, err);
    if (!ok)
        return err;
    return OsclErrNone;
#else
    OSCL_UNUSED_ARG(anAddr);
    OSCL_LEAVE(OsclErrNotSupported);
    return 0;
#endif
}

bool OsclSocketIBase::HasAsyncListen()
{
    return false;
}

int32 OsclSocketI::Listen(uint32 qSize)
{
#ifdef OsclListen
    //note: it's ok to do multiple listens to update the queue size
    bool ok;
    int err;
    OsclListen(iSocket, qSize, ok, err);
    if (!ok)
        return err;
    return OsclErrNone;
#else
    OSCL_UNUSED_ARG(qSize);
    return PVSOCK_ERR_NOT_IMPLEMENTED;
#endif
}

int32 OsclSocketI::SetRecvBufferSize(uint32 size)
{
#ifdef OsclSetRecvBufferSize
    int err;
    int ok;
    OsclSetRecvBufferSize(iSocket, size, ok, err);
    if (!ok)
        return err;
    return OsclErrNone;
#else
    OSCL_UNUSED_ARG(size);
    OSCL_LEAVE(OsclErrNotSupported);
    return 0;
#endif
}

inline void OsclSocketI::Send(SendParam &param, OsclSocketRequestAO& ao)
{
    if (!IsReady(ao))
        return;
    //send/recv behavior is undefined when socket is
    //not connected, so disallow it.
    if (!IsConnected(ao))
        return ;
    iSockServSendRequest.Activate(&param, ao);
}

inline void OsclSocketI::SendSuccess(SendParam &param)
{
    //nothing needed
    OSCL_UNUSED_ARG(param);
}

inline void OsclSocketI::CancelSend()
{
    iSockServSendRequest.CancelRequest();
}

inline void OsclSocketI::SendTo(SendToParam &param, OsclSocketRequestAO& ao)
{
    if (!IsReady(ao))
        return;
    iSockServSendToRequest.Activate(&param, ao);
}

inline void OsclSocketI::SendToSuccess(SendToParam &param)
{
    //nothing needed
    OSCL_UNUSED_ARG(param);
}

inline void OsclSocketI::CancelSendTo()
{
    iSockServSendToRequest.CancelRequest();
}

inline void OsclSocketI::Recv(RecvParam& param, OsclSocketRequestAO& ao)
{
    if (!IsReady(ao))
        return;
    //send/recv behavior is undefined when socket is
    //not connected, so disallow it.
    if (!IsConnected(ao))
        return ;
    iSockServRecvRequest.Activate(&param, ao);
}

inline void OsclSocketI::RecvSuccess(RecvParam& param)
{
    //nothing needed
    OSCL_UNUSED_ARG(param);
}

inline void OsclSocketI::CancelRecv()
{
    iSockServRecvRequest.CancelRequest();
}

inline void OsclSocketI::RecvFrom(RecvFromParam &param, OsclSocketRequestAO& ao)
{
    if (!IsReady(ao))
        return;
    iSockServRecvFromRequest.Activate(&param, ao);
}

inline void OsclSocketI::RecvFromSuccess(RecvFromParam& param)
{
    //nothing needed
    OSCL_UNUSED_ARG(param);
}

inline void OsclSocketI::CancelRecvFrom()
{
    iSockServRecvFromRequest.CancelRequest();
}

inline void OsclSocketI::Connect(ConnectParam &param, OsclSocketRequestAO& ao)
{
    if (!IsReady(ao))
        return;
    iSockServConnectRequest.Activate(&param, ao);
}

inline void OsclSocketI::CancelConnect()
{
    iSockServConnectRequest.CancelRequest();
}

inline void OsclSocketI::Accept(AcceptParam& param, OsclSocketRequestAO& ao)
{
    if (!IsReady(ao))
        return;
    iSockServAcceptRequest.Activate(&param, ao);
}

inline void OsclSocketI::CancelAccept()
{
    iSockServAcceptRequest.CancelRequest();
}

inline void OsclSocketI::Shutdown(ShutdownParam &param, OsclSocketRequestAO& ao)
{
    if (!IsReady(ao))
        return;
    iSockServShutdownRequest.Activate(&param, ao);
}

inline void OsclSocketI::CancelShutdown()
{
    iSockServShutdownRequest.CancelRequest();
}

bool OsclSocketI::MakeAddr(OsclNetworkAddress& in, TOsclSockAddr& addr)
{
    //convert OsclNetworkAddress to TOsclSockAddr.
#ifdef OsclMakeSockAddr
    bool ok;
    OsclMakeSockAddr(addr, in.port, in.ipAddr.Str(), ok);
    return ok;
#else
    //default-- for older configurations that don't have the OsclMakeSockAddr macro.
    addr.sin_family = OSCL_AF_INET;
    addr.sin_port = htons(in.port);
    addr.sin_addr.s_addr = inet_addr((const char*)in.ipAddr.Str());
    return (addr.sin_addr.s_addr != INADDR_NONE);
#endif
}

void OsclSocketI::MakeAddr(TOsclSockAddr& in, OsclNetworkAddress& addr)
{
#ifdef OsclUnMakeSockAddr
    //convert TOsclSockAddrOut to OsclNetworkAddress
    char* str;
    OsclUnMakeSockAddr(in, str);
    addr.ipAddr.Set(str);
#else
    addr.ipAddr.Set(inet_ntoa(in.sin_addr));
#endif
}

bool OsclSocketI::MakeMulticastGroupInformation(OsclIpMReq& in, TIpMReq& addr)
{
    bool retval = true;
#ifdef OsclMakeInAddr
    OsclMakeInAddr(addr.imr_interface, in.interfaceAddr.Str(), retval);
    if (retval)
    {
        OsclMakeInAddr(addr.imr_multiaddr, in.multicastAddr.Str(), retval);
    }
#else
    retval = false;
#endif
    return retval;
}

void OsclSocketI::MakeMulticastGroupInformation(TIpMReq& in, OsclIpMReq& addr)
{
#ifdef OsclUnMakeInAddr
    char interfaceAddr[PVNETWORKADDRESS_LEN] = {0};
    char multicastAddr[PVNETWORKADDRESS_LEN] = {0};
    char* bufferPtr = NULL;
    int32 buffLen;
    OsclUnMakeInAddr(in.imr_interface, bufferPtr);
    if (bufferPtr)
    {
        buffLen = oscl_strlen(bufferPtr);
        oscl_strncpy(interfaceAddr, bufferPtr, buffLen);
        interfaceAddr[buffLen] = '\0';
    }
    bufferPtr = NULL;
    buffLen = 0;
    OsclUnMakeInAddr(in.imr_multiaddr, bufferPtr);
    if (bufferPtr)
    {
        buffLen = oscl_strlen(bufferPtr);
        oscl_strncpy(multicastAddr, bufferPtr, buffLen);
        multicastAddr[buffLen] = '\0';
    }
    addr.interfaceAddr.Set(interfaceAddr);
    addr.multicastAddr.Set(multicastAddr);
#endif
}

int32 OsclSocketI::Open(OsclSocketServI& aServer, uint32 addrFamily, uint32 sockType, uint32 protocol)
//used to open a new tcp or udp socket.
{
#if(PV_OSCL_SOCKET_STATS_LOGGING)
    iStats.Construct(this, &aServer);
#endif

    bool ok;
    int err = 0;

    //create OS level socket.
#ifdef OsclSocket
    OsclSocket(iSocket, addrFamily, sockType, protocol, ok, err);
#else
    OSCL_UNUSED_ARG(addrFamily);
    OSCL_UNUSED_ARG(sockType);
    OSCL_UNUSED_ARG(protocol);
    ok = false;
    err = PVSOCK_ERR_NOT_IMPLEMENTED;
#endif

    InitSocket(ok);
    if (!ok)
        return err;

    if (protocol == OSCL_IPPROTO_UDP)
    {
        int32 bufsize = 65536;
        OsclSetRecvBufferSize(iSocket, bufsize, ok, err);
    }

    //set socket to non-blocking mode.
#ifdef OsclSetNonBlocking
    OsclSetNonBlocking(iSocket, ok, err);
#else
    ok = false;
    err = PVSOCK_ERR_NOT_IMPLEMENTED;
#endif

    if (!ok)
    {
        Close();
        return err;
    }
    return Open(aServer);
}

int32 OsclSocketI::Open(OsclSocketServI& aServer)
//used to open an accepted socket.
{
    CONSTRUCT_STATS(this, &aServer);

    iSocketServ = &aServer;

    //nothing needed-- the pv server doesn't need
    //to know about the socket yet.
    return OsclErrNone;
}

inline bool OsclSocketI::IsOpen()
//see whether socket has been opened successfully.
{
    return iSocketValid;
}

bool OsclSocketI::IsReady(OsclSocketRequestAO& ao)
//this routine does some error checks and will
//set socket error and complete the request for errors.
{
    //make sure this socket is open
    if (!IsOpen())
    {
        ao.iSocketError = PVSOCK_ERR_SOCK_NOT_OPEN;
        ao.PendComplete(OSCL_REQUEST_ERR_GENERAL);
        return false;
    }
    //make sure server is ok.
    if (!iSocketServ)
    {
        ao.iSocketError = PVSOCK_ERR_SOCK_NO_SERV;
        ao.PendComplete(OSCL_REQUEST_ERR_GENERAL);
        return false;
    }
    if (!iSocketServ->IsServConnected())
    {
        //report the error from the server, if any.
        ao.iSocketError = iSocketServ->iServError;
        if (ao.iSocketError == 0)
            ao.iSocketError = PVSOCK_ERR_SERV_NOT_CONNECTED;
        ao.PendComplete(OSCL_REQUEST_ERR_GENERAL);
        return false;
    }
    return true;
}

bool OsclSocketI::IsConnected(OsclSocketRequestAO& ao)
{
    if (!iSocketConnected)
    {
        ao.iSocketError = PVSOCK_ERR_SOCK_NOT_CONNECTED;
        ao.PendComplete(OSCL_REQUEST_ERR_GENERAL);
        return false;
    }
    return true;
}

void OsclSocketI::InitSocket(bool valid)
{
    iSocketValid = valid;
    iSocketConnected = false;
}

#endif //pv socket server








