/*
 *  RoboPeak Project
 *  HAL Layer - Socket Interface
 *  Copyright 2009 - 2013 RoboPeak Project
 */

#pragma once

#include <vector>

namespace rp{ namespace net {

class _single_thread SocketAddress 
{

public:
    enum address_type_t {
        ADDRESS_TYPE_UNSPEC = 0,
        ADDRESS_TYPE_INET   = 1,
        ADDRESS_TYPE_INET6  = 2,
    };

public:



    SocketAddress();
    SocketAddress(const char * addrString, int port, address_type_t = ADDRESS_TYPE_INET);
    // do not use this function, internal usage
    SocketAddress(void * platform_data);
    SocketAddress(const SocketAddress &);
    
    SocketAddress & operator = (const SocketAddress &);

    virtual ~SocketAddress();
    
    virtual int getPort() const;
    virtual u_result setPort(int port);

    virtual u_result setAddressFromString(const char * address_string,  address_type_t = ADDRESS_TYPE_INET);
    virtual u_result getAddressAsString(char * buffer, size_t buffersize) const;
    
    virtual address_type_t getAddressType() const; 

    virtual u_result getRawAddress(_u8 * buffer, size_t bufferSize) const;

    const void * getPlatformData() const {
        return _platform_data;
    }

    virtual void setLoopbackAddress(address_type_t = ADDRESS_TYPE_INET);
    virtual void setBroadcastAddressIPv4();
    virtual void setAnyAddress(address_type_t = ADDRESS_TYPE_INET);

public:
    static size_t LoopUpHostName(const char * hostname, const char * sevicename, std::vector<SocketAddress> &addresspool , bool performDNS = true, address_type_t = ADDRESS_TYPE_INET);

protected:
    void * _platform_data;
};



class SocketBase 
{
public:
    enum socket_family_t {
        SOCKET_FAMILY_INET  = 0,
        SOCKET_FAMILY_INET6 = 1,
        SOCKET_FAMILY_RAW   = 2,
    };


    enum socket_direction_mask {
        SOCKET_DIR_RD = 0x1,
        SOCKET_DIR_WR = 0x2,
        SOCKET_DIR_BOTH = (SOCKET_DIR_RD | SOCKET_DIR_WR),
    };

    enum {
        DEFAULT_SOCKET_TIMEOUT = 10000, //10sec
    };

    virtual ~SocketBase() {}
    virtual void dispose() = 0;
    virtual u_result bind(const SocketAddress & ) = 0;
    
    virtual u_result getLocalAddress(SocketAddress & ) = 0;
    virtual u_result setTimeout(_u32 timeout, socket_direction_mask msk = SOCKET_DIR_BOTH) = 0;

    virtual u_result waitforSent(_u32 timeout  = DEFAULT_SOCKET_TIMEOUT) = 0;
    virtual u_result waitforData(_u32 timeout  = DEFAULT_SOCKET_TIMEOUT)  = 0;
protected:
    SocketBase() {} 
};


class _single_thread StreamSocket : public SocketBase
{
public:

    enum {
        MAX_BACKLOG = 128,
    };

    static StreamSocket * CreateSocket(socket_family_t family = SOCKET_FAMILY_INET);
    
    virtual u_result connect(const SocketAddress & pairAddress) = 0;
    
    virtual u_result listen(int backlog = MAX_BACKLOG) = 0;
    virtual StreamSocket * accept(SocketAddress * pairAddress = NULL) = 0;
    virtual u_result waitforIncomingConnection(_u32 timeout  = DEFAULT_SOCKET_TIMEOUT) = 0;

    virtual u_result send(const void * buffer, size_t len) = 0;
    
    virtual u_result recv(void *buf, size_t len, size_t & recv_len) = 0;
    
    virtual u_result getPeerAddress(SocketAddress & ) = 0;

    virtual u_result shutdown(socket_direction_mask mask) = 0;

    virtual u_result enableKeepAlive(bool enable = true) = 0;
    
    virtual u_result enableNoDelay(bool enable = true) = 0;

protected:
    virtual ~StreamSocket() {} // use dispose();
    StreamSocket() {}
};

class _single_thread DGramSocket: public SocketBase
{

public:

    static DGramSocket * CreateSocket(socket_family_t family = SOCKET_FAMILY_INET);
       
    virtual u_result setPairAddress(const SocketAddress * pairAddress) = 0;
    virtual u_result sendTo(const SocketAddress * target, const void * buffer, size_t len) = 0;
    virtual u_result recvFrom(void *buf, size_t len, size_t & recv_len, SocketAddress * sourceAddr = NULL) = 0;
    virtual u_result clearRxCache() = 0;
    
protected:
    virtual ~DGramSocket() {} // use dispose();

    DGramSocket() {}
};

}}
