/*
 * zUdpClient.c
 * 
 * The Zen API 
 * Copyright (C) 2008-2009 by LMI Technologies Inc.
 * 
 * Licensed under The MIT License.
 * Redistributions of files must retain the above copyright notice.
 */ 
#include <z/zzUdpClient.h>
#include <z/zzSocket.h>
#include <stdlib.h>
#include <memory.h>

ZAPI_EXPORT(zStatus) zUdpClient_WaitAny(const zUdpClient* clients, z32u count, zBool* status, z64u timeout)
{
    zSocket sockets[zSOCKET_MAX_WAIT_GROUP];
    z32u i;
    
    for (i = 0; i < count; i++)
    {
        zUdpClientClass* obj = zHANDLE_POINTER(clients[i]);
        sockets[i] = obj->socket;
    }
    
    return zSocket_WaitAny(sockets, count, status, timeout);
}

ZAPI_EXPORT(zStatus) zUdpClient_Construct(zUdpClient* client)
{
    zUdpClientClass* obj = zNULL; 
    zSocket socket = zNIL; 
    zByte* writeBuffer = zNULL; 
    zByte* readBuffer = zNULL; 
    zStatus exception; 

    zEXCEPT_TRY
    {
        zEXCEPT_CHECK(zCalloc(&obj, 1, sizeof(zUdpClientClass))); 
        zEXCEPT_CHECK(zMalloc(&writeBuffer, zUDP_CLIENT_BUFFER_SIZE)); 
        zEXCEPT_CHECK(zMalloc(&readBuffer, zUDP_CLIENT_BUFFER_SIZE)); 

        zEXCEPT_CHECK(zSocket_Construct(&socket, zSOCKET_TYPE_UDP)) ;
        
        obj->socket = socket; 
        obj->readBuffer = readBuffer; 
        obj->writeBuffer = writeBuffer; 
        obj->readCapacity = zUDP_CLIENT_BUFFER_SIZE;
        obj->writeCapacity = zUDP_CLIENT_BUFFER_SIZE;

        zHANDLE(client, obj); 
    }
    zEXCEPT_CATCH(&exception)
    {
        if (!zIS_NULL(obj))             zFree(obj); 
        if (!zIS_NULL(readBuffer))      zFree(readBuffer); 
        if (!zIS_NULL(writeBuffer))     zFree(writeBuffer); 
        if (!zIS_NIL(socket))           zSocket_Destroy(socket); 

        zEXCEPT_SET(exception); 
    }
    zEXCEPT_FINALLY
    {
        zEXCEPT_CLOSE(); 
    }
    
    return zOK;
}

ZAPI_EXPORT(zStatus) zUdpClient_Destroy(zUdpClient client)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);
        
    if (!zIS_NULL(obj))
    {
        zCHECK(zSocket_Destroy(obj->socket));    
        zCHECK(zFree(obj->readBuffer));
        zCHECK(zFree(obj->writeBuffer));                

        zCHECK(zFree(obj));            
    }
    
    return zOK;
}

ZAPI_EXPORT(zStatus) zUdpClient_Bind(zUdpClient* client, zIPAddress address, z32u port)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);
       
    return zSocket_Bind(obj->socket, address, port);
}

ZAPI_EXPORT(zStatus) zUdpClient_Wait(zUdpClient client, z64u timeout)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);
    
    return zSocket_Wait(obj->socket, timeout);    
}

ZAPI_EXPORT(zStatus) zUdpClient_Receive(zUdpClient client, zIPEndPoint* endPoint, zUIntPtr* received)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);
    
    zCHECK(zSocket_ReadFrom(obj->socket, endPoint, obj->readBuffer, obj->readCapacity, &obj->readEnd));    
    obj->readBegin = 0; 

    if (!zIS_NULL(received))
    {
        *received = obj->readEnd;
    }
    
    return zOK;
}

ZAPI_EXPORT(zStatus) zUdpClient_Send(zUdpClient client, zIPAddress address, z32u port)
{    
    zUdpClientClass* obj = zHANDLE_POINTER(client);
    
    zCHECK(zSocket_WriteTo(obj->socket, address, port, obj->writeBuffer, obj->writeEnd)); 

    obj->writeEnd = 0; 

    return zOK; 
}
   
ZAPI_EXPORT(zStatus) zUdpClient_Read(zUdpClient client, void* buffer, zUIntPtr size)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);
    
    if (size > (obj->readEnd - obj->readBegin))
    {
        return zERROR_STREAM; 
    }
       
    memcpy(buffer, &obj->readBuffer[obj->readBegin], size); 
    obj->readBegin += size; 
         
    return zOK;
}

ZAPI_EXPORT(zStatus) zUdpClient_Write(zUdpClient client, const void* buffer, zUIntPtr size)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);

    if (size > (obj->writeCapacity - obj->writeEnd))
    {
        return zERROR_STREAM; 
    }

    memcpy(&obj->writeBuffer[obj->writeEnd], buffer, size); 
    obj->writeEnd += size; 
    
    return zOK;
}

ZAPI_EXPORT(zStatus) zUdpClient_EnableBroadcast(zUdpClient client, zBool broadcast)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);
	
    return zSocket_EnableBroadcast(obj->socket, broadcast);
}

ZAPI_EXPORT(zStatus) zUdpClient_EnableReuseAddress(zUdpClient client, zBool reuse)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);
	
    return zSocket_EnableReuseAddress(obj->socket, reuse);
}

ZAPI_EXPORT(zStatus) zUdpClient_SetWriteBuffer(zUdpClient client, zUIntPtr size)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);
        
    return zSocket_SetWriteBuffer(obj->socket, size);
}

ZAPI_EXPORT(zStatus) zUdpClient_SetReadBuffer(zUdpClient client, zUIntPtr size)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);
        
    return zSocket_SetReadBuffer(obj->socket, size);
}

ZAPI_EXPORT(zSocket) zUdpClient_Socket(zUdpClient client)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);
        
    return zSocket_Handle(obj->socket);
}

ZAPI_EXPORT(zStatus) zUdpClient_LocalEndPoint(zUdpClient client, zIPEndPoint* endPoint)
{
    zUdpClientClass* obj = zHANDLE_POINTER(client);
        
    return zSocket_LocalEndPoint(obj->socket,endPoint);  
}
