#include "stdafx.h"
#include "util.h"
#include <time.h>
#include "thread.h"
#include <iostream>

sockaddr_in AddrFromString( std::string str )
{
	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));

	if (str.empty())
		throw std::runtime_error("Empty address");
	std::string::size_type leftBracketPos = str.find('[');
	std::string::size_type rightBracketPos = str.find(']');
	/*switch (leftBracketPos)
	{
	case 0:
	// ipv6 style address string... we can't handle this yet
	// TODO: ipv6 support
	default:
	throw std::runtime_error( "Invalid address: " + str );
	case std::string::npos:
	if (rightBracketPos != std::string::npos)
	throw std::runtime_error( "Invalid address (contains ']'): " + str );
	break;
	}*/

	if(leftBracketPos == std::string::npos)
	{
		if (rightBracketPos != std::string::npos)
			throw std::runtime_error( "Invalid address (contains ']'): " + str );
	}
	else throw std::runtime_error( "Invalid address: " + str );

	std::string::size_type lastColonPos = str.find(':');
	std::string port;
	if (lastColonPos != std::string::npos && (lastColonPos > rightBracketPos || rightBracketPos == std::string::npos))
	{
		port = str.substr( lastColonPos+1 );
		str = str.substr( 0, lastColonPos );
	}
	// TODO: ipv6 stripping of []

	struct addrinfo * result = NULL;
	struct addrinfo hints;
	memset( &hints, 0, sizeof(hints) );
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	int err = getaddrinfo( str.c_str(), port.empty()? NULL : port.c_str(), &hints, &result );
	std::string errorMsg = "Unable to resolve address";
	if (!err)
	{
		for (struct addrinfo * p = result; p; p = p->ai_next)
		{
			switch (p->ai_addr->sa_family)
			{
			case AF_INET:
				{
					sockaddr_in * pAddr = (sockaddr_in *) p->ai_addr;
					addr = *pAddr;
					errorMsg = std::string();
				}
				break;

			default:
				errorMsg = "Unhandled address family in name lookup";
			}
		}
	}
	else
	{
		errorMsg = (char*) gai_strerror(err);
	}

	if (result)
		freeaddrinfo( result );

	if (!errorMsg.empty())
		throw std::runtime_error(errorMsg);

	return addr;
}

Socket::Socket(SOCKET s, int pollTime) : m_socket(s), m_bufferSize(0), m_bufferPos(0), m_pollTime(pollTime), m_cumRead(0)
{
	u_long nonBlock = 1;
	ioctlsocket(s, FIONBIO, &nonBlock);
}

Socket::~Socket()
{
	{
		SCOPED_GLOBAL_LOCK;
		std::cerr << " read " << m_cumRead << " bytes\n";
	}
	closesocket(m_socket);
}

uint8 Socket::ReadByte()
{
	if (m_bufferPos == m_bufferSize)
		Fetch();
	m_cumRead++;
	return m_buffer[m_bufferPos++];
}

void Socket::Fetch()
{
	time_t start = time(NULL);

	while (true)
	{
		if (start+m_pollTime < time(NULL))
			throw std::runtime_error("Timeout on socket");

		fd_set fds;
		FD_ZERO(&fds);
		FD_SET(m_socket, &fds);
		timeval timeout;
		timeout.tv_sec = m_pollTime;
		timeout.tv_usec = 0;
		// not portable to *nix
		int n = select(SOCKET(0), &fds, NULL, NULL, &timeout);
		switch (n)
		{
		case 0:
			throw std::runtime_error("Timeout on socket");
		case SOCKET_ERROR:
			throw std::runtime_error("Error waiting for read on socket");
		}
		int r = recv( m_socket, (char*) m_buffer, BUFFER_SIZE, 0 );
		if (r == SOCKET_ERROR)
		{
			if (WSAGetLastError() == WSAEWOULDBLOCK)
			{
				Sleep(1000);
				continue;
			}
			throw std::runtime_error("Error in WSARecv");
		}
		else if (r)
		{
			m_bufferSize = r;
			m_bufferPos = 0;
			break;
		}
		else
		{
			if (WSAGetLastError() != WSAEWOULDBLOCK)
			{
				Sleep(1000);
				continue;
			}
			throw std::runtime_error("No bytes received by recv");
		}
	}
}

