////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <vaNetwork/SocketSelector.h>
#include <vaNetwork/Socket.h>
#include <vaNetwork/SocketImpl.h>
#include <vaNetwork/Err.h>
#include <utility>

#ifdef _MSC_VER
    #pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
#endif


namespace vaNetwork {


////////////////////////////////////////////////////////////
struct SocketSelector::SocketSelectorImpl
{
    fd_set AllSockets;   ///< Set containing all the sockets handles
    fd_set SocketsReady; ///< Set containing handles of the sockets that are ready
    int    MaxSocket;    ///< Maximum socket handle
};


////////////////////////////////////////////////////////////
SocketSelector::SocketSelector() :
myImpl(new SocketSelectorImpl)
{
    Clear();
}


////////////////////////////////////////////////////////////
SocketSelector::SocketSelector(const SocketSelector& copy) :
myImpl(new SocketSelectorImpl(*copy.myImpl))
{

}


////////////////////////////////////////////////////////////
SocketSelector::~SocketSelector()
{
    delete myImpl;
}


////////////////////////////////////////////////////////////
void SocketSelector::Add(Socket& socket)
{
    FD_SET(socket.GetHandle(), &myImpl->AllSockets);

    int size = static_cast<int>(socket.GetHandle());
    if (size > myImpl->MaxSocket)
        myImpl->MaxSocket = size;
}


////////////////////////////////////////////////////////////
void SocketSelector::Remove(Socket& socket)
{
    FD_CLR(socket.GetHandle(), &myImpl->AllSockets);
    FD_CLR(socket.GetHandle(), &myImpl->SocketsReady);
}


////////////////////////////////////////////////////////////
void SocketSelector::Clear()
{
    FD_ZERO(&myImpl->AllSockets);
    FD_ZERO(&myImpl->SocketsReady);

    myImpl->MaxSocket = 0;
}


////////////////////////////////////////////////////////////
bool SocketSelector::Wait(float timeout)
{
    // Setup the timeout
    timeval time;
    time.tv_sec  = static_cast<long>(timeout);
    time.tv_usec = (static_cast<long>(timeout * 1000) % 1000) * 1000;

    // Initialize the set that will contain the sockets that are ready
    myImpl->SocketsReady = myImpl->AllSockets;

    // Wait until one of the sockets is ready for reading, or timeout is reached
    int count = select(myImpl->MaxSocket + 1, &myImpl->SocketsReady, NULL, NULL, timeout > 0 ? &time : NULL);

    return count > 0;
}


////////////////////////////////////////////////////////////
bool SocketSelector::IsReady(Socket& socket) const
{
    return FD_ISSET(socket.GetHandle(), &myImpl->SocketsReady) != 0;
}


////////////////////////////////////////////////////////////
SocketSelector& SocketSelector::operator =(const SocketSelector& right)
{
    SocketSelector temp(right);

    std::swap(myImpl, temp.myImpl);

    return *this;
}


}
