/*
 ==============================================================================

 This file is part of the JUCETICE project - Copyright 2009 by Lucio Asnaghi.

 JUCETICE is based around the JUCE library - "Jules' Utility Class Extensions"
 Copyright 2007 by Julian Storer.

 ------------------------------------------------------------------------------

 JUCE and JUCETICE can be redistributed and/or modified under the terms of
 the GNU General Public License, as published by the Free Software Foundation;
 either version 2 of the License, or (at your option) any later version.

 JUCE and JUCETICE are distributed in the hope that they will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with JUCE and JUCETICE; if not, visit www.gnu.org/licenses or write to
 Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 Boston, MA 02111-1307 USA

 ==============================================================================

 This file is based around Niall Moody's OSC/UDP library, but was modified to
 be more independent and modularized, using the best practices from JUCE.

 ==============================================================================
*/

#ifdef WIN32
  #include <winsock.h>
#else
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <sys/select.h>
  #include <netinet/in.h>
  #include <netdb.h>
#endif

BEGIN_JUCE_NAMESPACE

//==============================================================================
UDPSocket::UDPSocket ()
  : port (0)
{
    SocketSetup::getInstance();

    sock = socket (AF_INET, SOCK_DGRAM, 0);
}

UDPSocket::UDPSocket (const String& address, const short port)
{
    this->address = address;
    this->port = port;

    SocketSetup::getInstance();

    sock = socket (AF_INET, SOCK_DGRAM, 0);
}

UDPSocket::~UDPSocket ()
{
#ifdef WIN32
    closesocket (sock);
#else
    close (sock);
#endif
}

//==============================================================================
void UDPSocket::setAddress (const String& address)
{
    this->address = address;
}

void UDPSocket::setPort (const short port)
{
    this->port = port;
}

//==============================================================================
void UDPSocket::bindSocket ()
{
    sockaddr_in localAddress;

    localAddress.sin_family = AF_INET;
    localAddress.sin_port = htons(port);
    localAddress.sin_addr.s_addr = INADDR_ANY;
    memset (&(localAddress.sin_zero), 0, 8);

    bind (sock, (sockaddr *)(&localAddress), sizeof(sockaddr_in));
}

//==============================================================================
void UDPSocket::sendData (char *data, const long size)
{
    sockaddr_in destAddress;
    hostent *he = gethostbyname ((const char*) address.toUTF8());

    destAddress.sin_family = AF_INET;
    destAddress.sin_port = htons (port);
    /*destAddress.sin_addr = *((struct in_addr *)gethostbyaddr(inet_addr(address.c_str()),
                                                             (address.length()+1),
                                                             0)->h_addr_list);*/
    /*tempint = inet_addr(address.c_str());
    destAddress.sin_addr = *(in_addr *)(&tempint);*/
    destAddress.sin_addr = *(in_addr *)he->h_addr;
    memset (&(destAddress.sin_zero), '\0', 8);

    sendto(sock,
           data,
           size,
           0,
           reinterpret_cast<sockaddr *>(&destAddress),
           sizeof(sockaddr));
}

//==============================================================================
char *UDPSocket::getData (long& size)
{
    int bytesReceived = 0;
    timeval timeToWait;
    fd_set setToCheck;
    sockaddr_in receivedAddress;

    timeToWait.tv_sec = 0;
    timeToWait.tv_usec = 2500; // 1/4 of a second.

    FD_ZERO(&setToCheck);
    FD_SET(sock, &setToCheck);

    //Use select so that we can timeout to check if we're supposed to stop the
    //thread yet.
#ifdef WIN32
    if(select(1, &setToCheck, NULL, NULL, &timeToWait) == SOCKET_ERROR)
    {
        switch(WSAGetLastError())
        {
            case WSANOTINITIALISED:
                strcpy(receiveBuffer, "Not Initialised");
                break;
            case WSAEFAULT:
                strcpy(receiveBuffer, "Fault");
                break;
            case WSAENETDOWN:
                strcpy(receiveBuffer, "Network subsystem has failed.");
                break;
            case WSAEINVAL:
                strcpy(receiveBuffer, "Invalid parameter.");
                break;
            case WSAEINTR:
                strcpy(receiveBuffer, "Blocking socket was cancelled.");
                break;
            case WSAEINPROGRESS:
                strcpy(receiveBuffer, "Blocking socket in progress.");
                break;
            case WSAENOTSOCK:
                strcpy(receiveBuffer, "Not a socket.");
                break;
        }
        size = -1;
        return receiveBuffer;
    }
#else
    if (select((sock+1), &setToCheck, NULL, NULL, &timeToWait) == -1)
    {
        /*switch(errno)
        {
            case EBADF:
                strcpy(receiveBuffer, "Invalid file descriptor.");
                break;
            case EINTR:
                strcpy(receiveBuffer, "Non blocked signal was caught.");
                break;
            case EINVAL:
                strcpy(receiveBuffer, "Invalid time value.");
                break;
            case ENOMEM:
                strcpy(receiveBuffer, "Not enough memory.");
                break;
        }*/
        size = -1;
        return receiveBuffer;
    }
#endif

    if(FD_ISSET(sock, &setToCheck))
    {
        size = sizeof(sockaddr_in);
#ifndef WIN32
        bytesReceived = recvfrom(sock,
                                 receiveBuffer,
                                 (MaxBufferSize-1),
                                 0,
                                 (sockaddr *)(&receivedAddress),
                                 (unsigned int *)(&size));
#else
        bytesReceived = recvfrom(sock,
                                 receiveBuffer,
                                 (MaxBufferSize-1),
                                 0,
                                 (sockaddr *)(&receivedAddress),
                                 (int *)(&size));
#endif

        if(bytesReceived > 0)
        {
            size = bytesReceived;
            return receiveBuffer;
        }
        else
        {
            size = -1;
            strcpy(receiveBuffer, "Received an empty packet?");
            return receiveBuffer;
        }
    }
    else
    {
        size = -1;
        return 0;
    }
}

//==============================================================================
SocketSetup *SocketSetup::getInstance()
{
    static SocketSetup instance;

    return &instance;
}

SocketSetup::SocketSetup()
{
#ifdef WIN32
    WSADATA wsaData;   // if this doesn't work

    WSAStartup(MAKEWORD(1, 1), &wsaData);
#endif
}

SocketSetup::~SocketSetup()
{
#ifdef WIN32
    WSACleanup();
#endif
}

END_JUCE_NAMESPACE
