
/*---------------------------------------------------------------------------*/
/* udpsocket.h                                                               */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

// Socket-Status defines
#define SST_CLOSED					0
#define	SST_OPENED					1
#define SST_CONNECTION_INITIATED	2
#define SST_CONNECTED				3
#define SST_DO_SHUTDOWN				4

#define SST_BIND_START              5
#define SST_BIND_DONE               6
#define SST_BIND_FAILED             8
#define SST_ADDRESS_INVALID        11

class UDPSocketContext : public ISocketContext {
public:
};

class UDPSocketProvider : public ISocketProvider {
    friend class UDPSocket;
    virtual class UDPSocket * NewSocket(IIoMux * const iomux, USocket * const user, IInstanceLog * const log);
public:
    virtual ISocketContext * CreateSocketContext(class IInstanceLog * const log) override;
    virtual ISocket * CreateSocket(IIoMux * const iomux, USocket * const user, IInstanceLog * const log, bool useIPv6, class ISocketContext * socketContext = NULL) override;
};

class UDPSocket : public ISocket, public UIoContext, public UIoExec {
protected:
    bool useIPv6;
    IIoMux * const iomux;
    USocket * user;
    IInstanceLog * const log;
    UDPSocketProvider * const provider;
    int socketStatus;

    struct sockaddr_storage connectedAddr;
    struct sockaddr_storage localAddr;  // the local address to which the socket is bound, might be 0.0.0.0 for all interfaces
    IPaddr localIfAddr;                 // the local interface IP address on which a packet was received, which is not equal to localAddr

    void * recvBuf;
    size_t recvBufLen;
    int execCnt;
    bool shutDown;
    bool isMulticast;
    bool connected;
    struct ip_mreq mcReq;
    struct ipv6_mreq mcReqIPv6;    

    shutdownreason_t shutdownReason;

    bool ConnectIPv4(const char * address);
    bool ConnectIPv6(const char * address);
    bool BindIPv4(const char * address, word port);
    bool BindIPv6(const char * address, word port);

    void ReceiveData();
    void DoShutdown();

    inline void UserSocketConnectComplete();
    virtual void UserSocketBindResult(const char * localAddr, word localPort);
    virtual void UserSocketShutdown(shutdownreason_t reason);
    inline void UserSocketSendResult();
    inline void UserSocketRecvFromResult(void * buf, size_t len, IPaddr srcAddr, word srcPort);
    inline void UserSocketRecvCanceled(void * buf);

    bool IsMulticast(const char * addr);

public:
    UDPSocket(IIoMux * const iomux, USocket * const user, IInstanceLog * const log, UDPSocketProvider * provider);
    virtual ~UDPSocket();
    virtual void OpenSocket(bool useIPv6);

    void Connect(const char *address, const char * serverName) override;
    void GetLocalAddress(char * localAddr, unsigned size, word * localPort) override;
    void Bind(const char * localAddr = NULL, word localPort = 0) override;
    void Listen() override {};
    void Accept(USocket * const remoteUser) override {};
    void Shutdown() override;
    void Send(const void * buf, size_t len) override;
    void SendTo(const void * buf, size_t len, IPaddr dstAddr, word dstPort) override;
    void Recv(void * buf, size_t len, bool recvPartial = false) override;
    void SetQoS(qostraffictype_t type) override;
    void SetUser(class USocket * const user, bool * createdByServer, const char ** remoteAddress, const char ** serverName) override { this->user = user; }

    void IoContextNotify(bool indRX, bool indTX) override;
    void IoExec(void * context) override;
};
