/*
 * socket_handling_win.h
 *
 */

// Thanks MSDN for this Windows socket server code...
// http://msdn.microsoft.com/en-us/library/ms737593(VS.85).aspx
// Be sure to link with Ws2_32.lib
#define DEFAULT_BUFLEN 1024
#define STARTING_PORT 2000

DWORD WINAPI socket_server( LPVOID lpParam ) 
{
    WSADATA wsaData;
    SOCKET ListenSocket = INVALID_SOCKET,
           ClientSocket = INVALID_SOCKET;
    struct addrinfo *result = NULL,
                    hints;
    char recvbuf[DEFAULT_BUFLEN];
	char outgoing_data[DEFAULT_BUFLEN];
    int iResult, iSendResult;
    int recvbuflen = DEFAULT_BUFLEN;

	int portno = STARTING_PORT;
	char portstring[42]; // to hold the string version of the port number (portno)
	int number_of_ports_to_try = 42;  
	bool socket_bind_success = false;
    
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

	for (int i=0 ; i<number_of_ports_to_try ; ++i)
	{
		// Resolve the server address and port
		_snprintf_s(portstring, 42, 40, "%d", portno);
		iResult = getaddrinfo(NULL, portstring, &hints, &result);
		if ( iResult != 0 ) {
			printf("getaddrinfo failed: %d\n", iResult);
			WSACleanup();
			return 1;
		}

		// Create a SOCKET for connecting to server
		ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
		if (ListenSocket == INVALID_SOCKET) {
			printf("socket failed: %ld\n", WSAGetLastError());
			freeaddrinfo(result);
			WSACleanup();
			return 1;
		}

		// Setup the TCP listening socket
		iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
		if (iResult == SOCKET_ERROR) {
			printf("bind failed: %d\n", WSAGetLastError());
			freeaddrinfo(result);
			closesocket(ListenSocket);
			//WSACleanup();
			//return 1;
			portno += 1;
			printf("Trying next at port number %d.\n", portno);
		}
		else
		{
			socket_bind_success = true;
			break; // found a working port!
		}

	} // end of port-trying loop...

	if (! socket_bind_success)
	{
		printf("bind failed on all ports: %d\n", WSAGetLastError());
		freeaddrinfo(result);
		closesocket(ListenSocket);
		WSACleanup();
		return 1;
	}
	else
	{
		printf("bind succeeded at port # %d.\n", portno);
	}
    freeaddrinfo(result);

    // re-connecting with the client...
	bool quit_server = false;
	while (true)
	{

	printf("Listening on port#%d.\n", portno);
    iResult = listen(ListenSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) {
        printf("listen failed: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }



    // Accept a client socket
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed: %d\n", WSAGetLastError());
        closesocket(ListenSocket);  WSACleanup();  return 1;
    }

    // No longer need server socket structure, maybe?
    //closesocket(ListenSocket);

    // Receive until the peer shuts down the connection
	iResult = 42;
    while (iResult > 0)
	{
        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
		recvbuf[iResult] = 0; // must null-terminate the string! (Wow!)
        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);
			printf("Message received: %s\n", recvbuf);

			// should we quit?
			if (strncmp ("quit", recvbuf, strlen("quit")) == 0)
            {
				// echo back the "quit"
                _snprintf_s(outgoing_data,DEFAULT_BUFLEN,iResult+strlen("%s"),"%s", recvbuf );
                iSendResult = send( ClientSocket, outgoing_data, strlen(outgoing_data), 0 );
				break;
            }

			if (strncmp ("serverquit", recvbuf, strlen("quit server")) == 0)
            {
				// echo back the "quit server"
                _snprintf_s(outgoing_data,DEFAULT_BUFLEN,iResult+strlen("%s"),"%s", recvbuf );
                iSendResult = send( ClientSocket, outgoing_data, strlen(outgoing_data), 0 );
				quit_server = true;
				break;
            }

			// reply to client's message
            /*
             * this is blocking communication on the client side - make sure to reply once for each message!!
             */
            if (strncmp ("answer", recvbuf, strlen("answer")) == 0)
            {
                _snprintf_s(outgoing_data,DEFAULT_BUFLEN,100,"The answer is %d.", 42 );
				iSendResult = send( ClientSocket, outgoing_data, strlen(outgoing_data), 0 );
                //n = write(newsockfd, "The answer is 42.", strlen("The answer is 42."), 0);
            }
            
            else  // messages not understood get an echo reply
            {
                _snprintf_s(outgoing_data,DEFAULT_BUFLEN,iResult+strlen("Echo -> %s."),"Echo -> %s.", recvbuf );
                //n = write(newsockfd, outgoing_data, strlen(outgoing_data));
				// Echo the buffer back to the sender
                iSendResult = send( ClientSocket, outgoing_data, strlen(outgoing_data), 0 );
            }


            if (iSendResult == SOCKET_ERROR) {
                printf("send failed: %d\n", WSAGetLastError());
                // clean up
				closesocket(ClientSocket); WSACleanup(); return 1;
            }
            printf("Bytes sent: %d\n", iSendResult);
        }
        else if (iResult == 0)
		{
            printf("Connection closing...\n");
		}
        else  
		{
            printf("recv failed: %d\n", WSAGetLastError());
            // clean up
			closesocket(ClientSocket); WSACleanup(); return 1;
        }

    }

    // shutdown the connection since we're done
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) 
	{
        printf("shutdown failed: %d\n", WSAGetLastError());
		// clean up
        closesocket(ClientSocket); WSACleanup(); return 1;
    }

	if (quit_server)
	{
		break; // break from re-connection loop
	}

	} // end of while(true) for client connections...

    // cleanup
    closesocket(ClientSocket);
    WSACleanup();
	return 0;
}



void server_thread()
{
	// threading code...
	DWORD dwThreadId, 
		  dwThrdParam = 1;           // Gotta love Win variable names!
	HANDLE hThread; 
	printf("Creating serverThread.\n");
	hThread = CreateThread(          // the thread for our windows server
		NULL,                        // default security attributes 
		0,                           // use default stack size  
		socket_server,               // function to run in its own thread
		&dwThrdParam,                // argument to thread function 
		0,                           // use default creation flags 
		&dwThreadId);                // returns the thread identifier 
	if (!hThread) printf("ERROR in CreateThread.\n"); // if hThread is NULL
}
