/*  open2300  - win2300 library functions
 *  This file contains the common functions that are unique to
 *  windows. The entire file is ignored in case of Linux
 *  
 *  Version 1.11
 *  
 *  Control WS2300 weather station
 *  
 *  Copyright 2003-2006, Kenneth Lavrsen
 *  This program is published under the GNU General Public license
 */

#ifdef WIN32
#define DEBUG 0

#include "rw2300.h"

/********************************************************************
 * open_weatherstation, Windows version
 *
 * Input:   devicename (COM1, COM2 etc)
 * 
 * Returns: Handle to the weatherstation (type WEATHERSTATION)
 *
 ********************************************************************/
WEATHERSTATION open_weatherstation (char *device)
{
	WEATHERSTATION ws;
	DCB dcb;
	COMMTIMEOUTS commtimeouts;

	//Setup serial port

	ws = CreateFile( device,
	               GENERIC_READ | GENERIC_WRITE,
	               0,    // must be opened with exclusive-access
	               NULL, // no security attributes
	               OPEN_EXISTING, // must use OPEN_EXISTING
	               0,    // not overlapped I/O
	               NULL  // hTemplate must be NULL for comm devices
	               );
	                     
	if (ws == INVALID_HANDLE_VALUE)
	{
		printf ("\nUnable to open serial device");
		exit (0);
	}

	if (!GetCommState (ws, &dcb))
	{
		printf ("\nUnable to GetCommState");
		exit (0);
	}

	dcb.DCBlength = sizeof (DCB);
	dcb.BaudRate = BAUDRATE;
	dcb.fParity = FALSE;
	dcb.fOutxCtsFlow = FALSE;
	dcb.fOutxDsrFlow = FALSE;
	dcb.fDtrControl = DTR_CONTROL_DISABLE;
	dcb.fDsrSensitivity = FALSE;
	dcb.fOutX = FALSE;
	dcb.fInX = FALSE;
	dcb.fErrorChar = FALSE;
	dcb.fNull = FALSE;
	dcb.fRtsControl = RTS_CONTROL_ENABLE;
	dcb.fAbortOnError = FALSE;
	dcb.ByteSize = 8;
	dcb.Parity = NOPARITY;
	dcb.StopBits = ONESTOPBIT;
	dcb.fNull = false;

	if (!SetCommState (ws, &dcb))
	{
		printf ("\nUnable to SetCommState");
		exit (0);
	}

	commtimeouts.ReadIntervalTimeout = MAXDWORD;
	commtimeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
	commtimeouts.ReadTotalTimeoutConstant = 175;
	commtimeouts.WriteTotalTimeoutConstant = 0;
	commtimeouts.WriteTotalTimeoutMultiplier = 0;

	if (!SetCommTimeouts (ws, &commtimeouts))
	{
		printf ("\nUnable to SetCommTimeouts");
		exit (0);
	}

	return ws;
}


/********************************************************************
 * close_weatherstation, windows version
 *
 * Input: Handle to the weatherstation (type WEATHERSTATION)
 *
 * Returns nothing
 *
 ********************************************************************/
void close_weatherstation (WEATHERSTATION ws)
{
	CloseHandle (ws);
	return;
}

/********************************************************************
 * reset_06 WS2300 by sending command 06 (windows version) 
 * 
 * Input:   device number of the already open serial port
 *           
 * Returns: nothing, exits progrsm if failing to reset
 *
 ********************************************************************/
void reset_06(WEATHERSTATION serdevice)
{
	unsigned char command = 0x06;
	unsigned char answer;
	int i;

	for (i = 0; i < 100; i++)
	{

		PurgeComm(serdevice, PURGE_RXCLEAR);

		write_device(serdevice, &command, 1);

		// Occasionally 0, then 2 is returned.  If zero comes back, continue
		// reading as this is more efficient than sending an out-of sync
		// reset and letting the data reads restore synchronization.
		// Occasionally, multiple 2's are returned.  Read with a fast timeout
		// until all data is exhausted, if we got a two back at all, we
		// consider it a success
		
		while (1 == read_device(serdevice, &answer, 1))
		{
			if (answer == 2)
			{

				// clear anything that might come after the response
				PurgeComm(serdevice, PURGE_RXCLEAR);

				return;
			}
		}

		Sleep(5 * i);
	}
	printf("\nCould not reset\n");
	exit(EXIT_FAILURE);
}

/********************************************************************
 * read_device WIN32 emulation of Linux read() 
 * Reads data from the handle
 *
 * Inputs:  serdevice - opened file handle
 *          buffer - pointer to the buffer to read into
 *          size - number of bytes to read
 *
 * Output:  *buffer - modified on success
 * 
 * Returns: number of bytes read
 *
 ********************************************************************/
int read_device(WEATHERSTATION serdevice, unsigned char *buffer, int size)
{
	DWORD dwRead = 0;

	if (!ReadFile(serdevice, buffer, size, &dwRead, NULL))
	{
		return -1;
	}

	return (int) dwRead;
}

/********************************************************************
 * write_device WIN32 emulation of Linux write() 
 * Writes data to the handle
 *
 * Inputs:  serdevice - opened file handle
 *          buffer - pointer to the buffer to write from
 *          size - number of bytes to write
 *
 * Returns: number of bytes written
 *
 ********************************************************************/
int write_device(WEATHERSTATION serdevice, unsigned char *buffer, int size)
{
	DWORD dwWritten;

	if (!WriteFile(serdevice, buffer, size, &dwWritten, NULL))
	{
		return -1;
	}

	return (int) dwWritten;
}

/********************************************************************
 * sleep_short - Windows version
 * 
 * Inputs: Time in milliseconds (integer)
 *
 * Returns: nothing
 *
 ********************************************************************/
void sleep_short(int milliseconds)
{
	Sleep(milliseconds);
}

/********************************************************************
 * sleep_long - Windows version
 * 
 * Inputs: Time in seconds (integer)
 *
 * Returns: nothing
 *
 ********************************************************************/
void sleep_long(int seconds)
{
	Sleep(seconds*1000);
}

/********************************************************************
 * http_request_url - Windows version
 * 
 * Inputs: urlline - URL to Weather Underground with path and data
 *                   as a pointer to char array (string)
 *
 * Returns: 0 on success and -1 if fail.
 *
 * Action: Send a http request to Weather Underground
 *
 ********************************************************************/
int http_request_url(char *urlline)
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	SOCKET sockfd;
	struct hostent *hostinfo;
	struct sockaddr_in urladdress;
	char buffer[1024];
	int bytes_read;
	
	wVersionRequested = MAKEWORD( 1, 1 );
	
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 )
	{
		perror("Couldn't find a useable winsock.dll");
	    return(-1);
	}
	
	/* Confirm that the Windows Sockets DLL supports 1.1.*/
	
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
	         HIBYTE( wsaData.wVersion ) != 1 )
	{
	    WSACleanup();
		perror("Couldn't find a useable winsock.dll");
	    return(-1);   
	}

	if ( (hostinfo = gethostbyname(WEATHER_UNDERGROUND_BASEURL)) == NULL )
	{
		perror("Host not known by DNS server or DNS server not working");
		return(-1);
	}
	
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	
	if (sockfd == INVALID_SOCKET)
	{
		perror("Cannot open socket");
		return(-1);
	}

	memset(&urladdress, 0, sizeof(urladdress));
	urladdress.sin_family = AF_INET;
	urladdress.sin_port = htons(80); /*default HTTP Server port */

	urladdress.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list;

	if (connect(sockfd,(struct sockaddr*)&urladdress,sizeof(urladdress)) != 0)
	{
		perror("Cannot connect to host");
		return(-1);
	}
	
	sprintf(buffer, "%s", urlline);
	send(sockfd, buffer, strlen(buffer), 0);

	/* While there's data, read and print it */
	do
	{
		memset(buffer, 0, sizeof(buffer));
		bytes_read = recv(sockfd, buffer, sizeof(buffer), 0);
		if ( bytes_read > 0 )
			if (DEBUG) printf("%s", buffer);
	}
	while ( bytes_read > 0 );

	/* Close socket and clean up winsock */
	closesocket(sockfd);
	
	WSACleanup();
	
	return(0);
}

/********************************************************************
 * citizen_weather_send - Linux version
 * 
 * Inputs: config structure (pointer to) - containing CW ID
 *         datastring (pointer to) - containing all the data
 *
 * Returns: 0 on success and -1 if fail.
 *
 * Action: Send a http request to Weather Underground
 *
 ********************************************************************/
int citizen_weather_send(struct config_type *config, char *aprsline)
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	SOCKET sockfd = INVALID_SOCKET; // Prevent warning
	struct hostent *hostinfo;
	struct sockaddr_in urladdress;
	char buffer[1024];
	int bytes_read;
	int hostnum;
	
	wVersionRequested = MAKEWORD( 1, 1 );
	
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 )
	{
		perror("Couldn't find a useable winsock.dll");
	    return(-1);
	}
	
	/* Confirm that the Windows Sockets DLL supports 1.1.*/
	
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
	         HIBYTE( wsaData.wVersion ) != 1 )
	{
	    WSACleanup();
		perror("Couldn't find a useable winsock.dll");
	    return(-1);   
	}


	for (hostnum = 0; hostnum <= config->num_hosts; hostnum++)
	{
		if ( hostnum == config->num_hosts )
			return(-1);          // tried 'em all, fail exit		

		if ( (hostinfo = gethostbyname(config->aprs_host[hostnum].name) ) == NULL )		
		{
			sprintf(buffer,"Host, %s, not known ", config->aprs_host[hostnum].name);
			perror(buffer);
			continue;
		}
				
		sockfd = socket(AF_INET, SOCK_STREAM, 0);
	
		if (sockfd == INVALID_SOCKET)
		{
			sprintf(buffer,"Cannot open socket on %s ", config->aprs_host[hostnum].name);
			perror(buffer);
			continue;
		}
		
		memset(&urladdress, 0, sizeof(urladdress));// clear the structure
		urladdress.sin_family = AF_INET;
		urladdress.sin_port = htons(config->aprs_host[hostnum].port);
		urladdress.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list;

		if ( connect(sockfd, (struct sockaddr*)&urladdress, sizeof(urladdress)) != 0 )
		{
			sprintf(buffer,"Cannot connect to host: %s ", config->aprs_host[hostnum].name);
			perror(buffer);
			continue;
		}
		else
		{
			break;   // success
		}
	}

	if (DEBUG) printf("%d: %s: ",hostnum, config->aprs_host[hostnum].name);

	memset(buffer, 0, sizeof(buffer));

	// read login prompt
	if ( (recv(sockfd, buffer, sizeof(buffer), 0) > 0) && (DEBUG != 0) )
	{
		printf("%s", buffer);	// display prompt - if debug
	}

	// The login/header line
	sprintf(buffer,"user %s pass %s vers open2300 %s\n",
	        config->citizen_weather_id, config->citizen_weather_passcode,
		VERSION);
	send(sockfd, buffer, strlen(buffer), 0);
	if (DEBUG)
		printf("%s\n", buffer);

	// now the data
	sprintf(buffer,"%s\n", aprsline);
	send(sockfd, buffer, strlen(buffer), 0);
	if (DEBUG)
		printf("%s\n", buffer);

	/* While there's data, read and print it - Not sure it is needed */
	do
	{
		memset(buffer, 0, sizeof(buffer));
		bytes_read = recv(sockfd, buffer, sizeof(buffer), 0);
		if ( bytes_read > 0 )
		{
			if (DEBUG)
				printf("Data returned from server\n%s\n", buffer);
			break;
		}
	}
	while ( bytes_read > 0 );

	/* Close socket*/
	close(sockfd);
	
	WSACleanup();

	return(0);
}

#endif
