#ifdef _WIN32

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Wininet.h>

#include <string>

namespace http
{


std::string get(std::string host, int port, std::string page)
{
    HINTERNET hInternet = ::InternetOpenA("md639 MarsLander", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
    if (!hInternet)
        throw std::runtime_error("Couldn't open the internet.");

    HINTERNET hConnect = ::InternetConnectA(hInternet, host.c_str(), (INTERNET_PORT)port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL);
    if (!hConnect) {
        ::InternetCloseHandle(hInternet);
        throw std::runtime_error("Unable to connect to the server.");
    }

    // Should be application/json, but accept text as a backup
    LPCSTR aszAccept[] = {
        "application/json",
        "text/*",
        NULL
    };

    HINTERNET hRequest = ::HttpOpenRequestA(hConnect, NULL, page.c_str(), NULL, NULL, aszAccept, INTERNET_FLAG_NO_COOKIES, NULL);
    if (!hRequest) {
        ::InternetCloseHandle(hConnect);
        ::InternetCloseHandle(hInternet);
        throw std::runtime_error("Error opening request.");
    }

    BOOL r = ::HttpSendRequestA(hRequest, NULL, 0, NULL, 0);
    if (!r) {
        ::InternetCloseHandle(hRequest);
        ::InternetCloseHandle(hConnect);
        ::InternetCloseHandle(hInternet);

        throw std::runtime_error("Error sending request.");
    }

    DWORD cbContent;
    DWORD cbcbContent = sizeof(cbContent);
    r = HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH, &cbContent, &cbcbContent, NULL);
    if (!r) {
        int n = GetLastError();

        if (n == ERROR_HTTP_HEADER_NOT_FOUND) {
            cbContent = 1024;
        }
        else {
            ::InternetCloseHandle(hRequest);
            ::InternetCloseHandle(hConnect);
            ::InternetCloseHandle(hInternet);

            throw std::runtime_error("Couldn't query content length.");
        }
    }

    std::string ret = "";
    char* lpBuffer = new char[cbContent+1];

    DWORD cbRead = 0;
    do {
        ::InternetReadFile(hRequest, lpBuffer, cbContent, &cbRead);

        ret.append(lpBuffer, cbRead);
    } while (cbRead != 0);

    delete[] lpBuffer;

    ::InternetCloseHandle(hRequest);
    ::InternetCloseHandle(hConnect);
    ::InternetCloseHandle(hInternet);

    return ret;
}


}


#else

#include <string>
#include <stdexcept>
#include <sstream>

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>

namespace http
{


int create_tcp_socket();
std::string get_ip( std::string host );
std::string build_get_query( std::string host, std::string page );


std::string get( std::string host, int port, std::string page )
{
    sockaddr_in* remote;
    int sock;
    int tmpres;
    std::string ip;
    std::string get;
    std::string buf;
    buf.resize( BUFSIZ + 1 );

    sock = create_tcp_socket();
    ip = get_ip( host );

    remote = new sockaddr_in;
    remote->sin_family = AF_INET;

    tmpres = inet_pton( AF_INET, ip.c_str(), &( remote->sin_addr.s_addr ) );
    if ( tmpres < 0 ) {
        throw std::runtime_error( "Can't set remote->sin_addr.s_addr" );
    }
    else if ( tmpres == 0 ) {
        throw std::invalid_argument( "IP Address not valid." );
    }
    remote->sin_port = htons( port );

    if ( connect( sock, ( sockaddr* )remote, sizeof( sockaddr ) ) < 0 ) {
        throw std::runtime_error( "Could not connect to socket." );
    }
    get = build_get_query( host, page );

    //Send the query to the server
    unsigned int sent = 0;
    while ( sent < get.length() ) {
        tmpres = send( sock, get.c_str() + sent, get.length() - sent, 0 );
        if ( tmpres == -1 ) {
            throw std::runtime_error( "Could not send request." );
        }

        sent += tmpres;
    }

    //now it is time to receive the page
    unsigned int htmlstart = 0;
    bool seen = false;
    std::ostringstream oss;

    while ( ( tmpres = recv( sock, &buf[0], BUFSIZ, 0 ) ) > 0 ) {
        if ( !seen ) {
            htmlstart = buf.find( "\r\n\r\n" );

            if ( htmlstart != std::string::npos ) {
                seen = true;

                oss << &buf.c_str()[htmlstart + 4];
            }
        }
        else {
            oss << buf;
        }

        buf.clear();
    }

    delete remote;
    close( sock );

    return oss.str();
}


int create_tcp_socket()
{
    int sock;

    sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
    if ( sock < 0 ) {
        throw std::runtime_error( "Can't create TCP socket" );
    }

    return sock;
}


std::string get_ip( std::string host )
{
    hostent* hent;
    const int iplen = 15;
    std::string ip;
    ip.resize( iplen + 1 );

    hent = gethostbyname( host.c_str() );
    if ( !hent ) {
        throw std::runtime_error( "Unable to get IP." );
    }

    if ( !inet_ntop( AF_INET, hent->h_addr_list[0], &ip[0], iplen ) ) {
        throw std::runtime_error( "Unable to resolve host." );
    }

    return ip;
}

std::string build_get_query( std::string host, std::string page )
{
    std::ostringstream oss;

    oss << "GET " << ( ( page[0] == '/' ) ? "" : "/" ) << page << " HTTP/1.0\r\n"
        << "Host: " << host << "\r\n"
        << "User-Agent: md639 MarsLander\r\n\r\n";

    return oss.str();
}


}

#endif