#include "winlean.h"
#include "winsock2.h"
#include <stdlib.h>
#include <stdio.h>

#define DEF_PACKET_SIZE 32
#define MAX_PACKET 1024

#define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s))
#define xfree(p)   HeapFree (GetProcessHeap(), 0, (p))
//
// This event is set when a control socket is closed before the end of a test.
// If the user kills the manager script then its end of the control socket will
// be closed.  We catch that and use this event to notify any worker threads
// associated with that control socket to stop what they're doing.
// 
_declspec (thread) void * MC_MyCS_StopEvent;

// print an error message and exit the program   
void die (char *format,...) {
    va_list args;
    char buffer[2048], *ptr=buffer;

    va_start(args,format);
    ptr += vsprintf (ptr, format, args);
    va_end (args);
    *(ptr++) = '\n';
    *(ptr++) = '\0';

    printf( buffer);

    exit(1);
}
int InitChildLayer(void) {
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    int major, minor;

    wVersionRequested = MAKEWORD( 2, 0 );

    err = WSAStartup( wVersionRequested, &wsaData );
    if ( err != 0 ) {
    // Tell the user that we couldn't find a useable   
    // WinSock DLL.                                    
    die ("Couldn't find a WinSock DLL!\n");
    }

    // Confirm that the WinSock DLL supports 2.0.          
    // Note that if the DLL supports versions greater      
    // than 2.0 in addition to 2.0, it will still return   
    // 2.0 in wVersion since that is the version we        
    // requested.                                          

    major = LOBYTE( wsaData.wVersion );
    minor = LOBYTE( wsaData.wVersion );

    if ( major < 1 || ( major == 1 && minor < 1 ) ) {
    // Tell the user that we couldn't find a useable   
    // WinSock DLL.                                    
    WSACleanup( );
    die ("Require at least WinSock 1.1, your version is %d.%d!\n",
         major, minor);
    }
    return 0;
}
static USHORT
checksum(USHORT *buffer, int size)
{
    unsigned long cksum=0;

    while(size > 1)
    {
        cksum += *buffer++;
        size -= sizeof(USHORT);
    }

    if (size)
        cksum += *(UCHAR*)buffer; // if there's an odd number of bytes   

    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);
    return (USHORT)(~cksum);
}


//
// MC_GetLastErrorText
//
//  PURPOSE: copies error message text to string
//
//  PARAMETERS:
//    lpszBuf - destination buffer
//    dwSize - size of buffer
//
//  RETURN VALUE:
//    destination buffer
// 
LPSTR
MC_GetLastErrorText(LPSTR lpszBuf, DWORD dwSize, DWORD errnum)
{
    DWORD dwRet;
    LPTSTR lpszTemp = NULL;

    dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
                           NULL,
                           errnum,
                           LANG_NEUTRAL,
                           (LPTSTR)&lpszTemp,
                           0,
                           NULL );

    if ( !dwRet)
        sprintf(lpszBuf, "error number %d", errnum); // no message so use errnum   
    else
    {
        lpszTemp[lstrlen(lpszTemp)-2] = '\0';  // remove cr and newline characters   
        sprintf(lpszBuf, "%s (0x%x)", lpszTemp, errnum);
    }

    if (lpszTemp)
        LocalFree((HLOCAL) lpszTemp );

    return lpszBuf;
}


static void
MC_ErrMsg(LPSTR buf, DWORD bufsize, LPCSTR msg)
{
     DWORD tmp;

     tmp = strlen(strcpy(buf, msg));
     MC_GetLastErrorText(buf + tmp, bufsize - tmp, WSAGetLastError());
}

//
//  FUNCTION: MC_Recv
//
//  PURPOSE: A replacement for the Winsock recv() routine.  This version uses
//           overlapped I/O to look for data on a socket and also watch
//           for the Stop event.
//
//  PARAMETERS:
//    sock - read from this socket.
//    buffer - place incoming data into this buffer.
//    nbytes - the amount of storage in the buffer.
//
//  RETURN VALUE:
//    -1 - error.
//    0  - the socket connection has been closed.
//    >0 - the number of bytes read.
// 
int
MC_Recv(SOCKET sock, char *buffer, int nbytes)
{
    DWORD          bytesreceived;
    DWORD          flags = 0;
    WSAOVERLAPPED  ov;
    WSABUF         wsabuffer[1];
    HANDLE         ourevents[2];

    ov.hEvent = NULL;
    ov.Offset = ov.OffsetHigh = 0;
    wsabuffer[0].buf = buffer;
    wsabuffer[0].len = nbytes;

    while(1)
    {
        if (WSARecv(sock, wsabuffer, 1, &bytesreceived, &flags, &ov, NULL) == SOCKET_ERROR)
        {
            switch (WSAGetLastError())
            {
            case WSAEWOULDBLOCK: continue;  // try again   
            case WSAENETRESET:
            case WSAECONNRESET:
            case WSAEDISCON:     return 0;  // connection closed   
            //case WSA_IO_PENDING: break;     /* okay */
            default:             return -1; // unexpected error   
            }
            ourevents[0] = (HANDLE) sock;
            ourevents[1] = MC_MyCS_StopEvent;
            if (WaitForMultipleObjects(2, ourevents, FALSE, INFINITE) != WAIT_OBJECT_0)
            {
                printf("MC_Recv(): Stop event or WaitForMultipleObjects() error\n");
                return -1; // Stop event or error   
            }
            else if (!WSAGetOverlappedResult(sock, &ov, &bytesreceived, FALSE, &flags))
                return -1; // error   
        }
        return bytesreceived; // if we got here its due to a successful read   
    }
}

static DWORD  WINAPI Gulp(LPVOID lpParam )
{
    int         totalBytes;
    int         numPackets;
    int         bytesRead;
    char        buffer[4096];
    SOCKET      sockfd;

    INT64 startCounter, endCounter, frequency;

    totalBytes = 0;
    numPackets = 0;
    sockfd = *(SOCKET *) lpParam;

    QueryPerformanceCounter((PLARGE_INTEGER)&startCounter);
    do {
        bytesRead = MC_Recv(sockfd, buffer,  4096);
        if (bytesRead > 0 ) {
            numPackets++;
            totalBytes += bytesRead;
        }
    } while (bytesRead > 0) ;
    QueryPerformanceCounter((PLARGE_INTEGER)&endCounter);
    QueryPerformanceFrequency((PLARGE_INTEGER)&frequency);

    printf(" Read %d bytes / %d KB / %d MB in %d receives\n",
           totalBytes, totalBytes / 1024, totalBytes / 1048576, numPackets);

    if (endCounter != startCounter) {
        double tau = (double)(endCounter - startCounter) / (double)frequency;
        double bps = totalBytes * 8.0 / tau;

        printf("Transferred in %.3e seconds => %.3e bps\n",
               tau, bps);
    }
    else {
        printf("Transfer too quick to measure with this clock\n");
    }

    return 1;
}

static DWORD listener (int port, LPSTR retmsg, DWORD size_retmsg)
{
    struct sockaddr_in  sock_addr;
    DWORD       tmp;
    int         server_status = 0;
    int         ret_count = 0;
    SOCKET      listenSockfd;
    SOCKET      sockfd;
    HANDLE      hThread;
    DWORD       dwThreadId;


    memset((void *) &sock_addr, '\0', sizeof(sock_addr));
    sock_addr.sin_family = AF_INET;
    sock_addr.sin_addr.S_un.S_addr = INADDR_ANY;
    sock_addr.sin_port = htons((short) (port > 0 ? port : 80));

    if ((listenSockfd = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0)) == INVALID_SOCKET)
    {
        MC_ErrMsg(retmsg, size_retmsg, "500 Unable to create socket: ");
        printf(retmsg);
        return -1;
    }
    if ((listenSockfd = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0)) == INVALID_SOCKET)
    {
        MC_ErrMsg(retmsg, size_retmsg, "500 Unable to create socket: ");
        printf(retmsg);
        return -1;
    }

    tmp = bind(listenSockfd, (struct sockaddr *) &sock_addr, sizeof(sock_addr));
    if (tmp == SOCKET_ERROR)
    {
        MC_ErrMsg(retmsg, size_retmsg, "500 Unable to bind to socket: ");
        printf(retmsg);
        return -1;
    }

    tmp = listen( listenSockfd,10);
    if (tmp == SOCKET_ERROR)
    {
        MC_ErrMsg(retmsg, size_retmsg, "500 Unable to listen on socket: ");
        printf(retmsg);
        return -1;
    }
    do {
        printf("\n listening\n");
        sockfd  = accept(listenSockfd,0,0);
        if (sockfd == SOCKET_ERROR)
        {
            MC_ErrMsg(retmsg, size_retmsg, "500 Unable to accept on socket: ");
            printf(retmsg);
            return -1;
        }
        printf(" ...Accepted connection\n");

        hThread = CreateThread(
            NULL,                        // default security attributes
            0,                           // use default stack size
            Gulp,                        // thread function
            &sockfd,                // argument to thread function
            0,                           // use default creation flags
            &dwThreadId);                // returns the thread identifier

    // Check the return value for success.

    if (hThread == NULL)
    {
        printf("CreateThread failed." );
    }
    else
    {
        CloseHandle( hThread );
    }

    } while (TRUE);
}




int main(int argc, char *argv[]) {

    char retmsg[1024];
    int foo;

    if (argc < 2 ) {
        printf("gulpserver port\n");
        printf("    listens on port for TCP blast client \n");
        return -1;
    }
    foo = atoi(argv[1]);
    InitChildLayer();
    printf (" Listening on port %d ...",foo);
    listener(foo, retmsg, 1024);
    WSACleanup( );

}


