/***************************************************************************
 *            wince_gps.c
 *
 *  December 2010, Steffen Pohle steffen@gulpe.de
 ****************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */


#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <stdlib.h>
#include <windows.h>
#include <commctrl.h>
#include <winbase.h>
#include <tchar.h>
#if defined(__MINGW32CE__) || defined(_WIN32_WCE)
	#include <aygshell.h>
#endif

#include <mmsystem.h>

#include <shlobj.h>
#include "osmroute.h"
#include "gps.h"
#include "memoryleak.h"
#include "system.h"


int gps_serial_comport = 0;
int gps_serial_baudrate = 6;
char gps_wince_device[GPS_DEVICELEN] = "\0";
int gps_serial_baudratearray[] = { CBR_1200, CBR_2400, CBR_4800, CBR_9600, CBR_19200, CBR_38400, CBR_57600, CBR_115200 };
char gps_serial_device[LEN_FILENAME] = "";
HANDLE gps_serial_hGPScomm;

void gps_serial_settings_read_device ();


/*
 * read the settings from the comport and fill out the data block
 * comport, baudrate
 */
void gps_serial_settings_read_device () {
	char *cfg = gps_get_device ();
	char port[16], baud[16];
	int i;
	
	if (strncmp (cfg, "serial:", 7) == 0) {
		/* serial setting found */
		cfg = strchr (cfg, ':') + 1;
		if (strncmp (cfg, "com", 3) == 0) {
			/* com port */
			cfg = cfg + 3;
			for (i = 0; (i < 15 && cfg[i] != ',' && cfg[i] != 0); i++)
				port[i] = cfg[i];
			port[i] = 0;
			gps_serial_comport = atoi(port) - 1;
			 
			/* baudrate */
			cfg = cfg + i + 1;
			for (i = 0; (i < 15 && cfg[i] != ',' && cfg[i] != 0); i++)
				baud[i] = cfg[i];
			baud[i] = 0;
			i = atoi(baud);
			gps_serial_baudrate = 7;
			switch (i) {
				case (CBR_1200):
					gps_serial_baudrate--;
				case (CBR_2400):
					gps_serial_baudrate--;
				case (CBR_4800):
					gps_serial_baudrate--;
				case (CBR_9600):
					gps_serial_baudrate--;
				case (CBR_19200):
					gps_serial_baudrate--;
				case (CBR_38400):
					gps_serial_baudrate--;
				case (CBR_57600):
					gps_serial_baudrate--;
				default:
					break;
			}
		}
	}
#if defined(__MINGW32CE__) || defined(_WIN32_WCE)
	snprintf (gps_serial_device, LEN_FILENAME, "com%d:", gps_serial_comport+1);
#else
	snprintf (gps_serial_device, LEN_FILENAME, "com%d", gps_serial_comport+1);
#endif
};


/*
 * initialize gps device (for now on windows only serial port supported
 */
int gps_serial_device_open () {
	DCB dcb;
	COMMTIMEOUTS cto;
#if defined(__MINGW32CE__) || defined(_WIN32_WCE)
	WCHAR device[GPS_DEVICELEN];
#endif
	
	/* get config data */
	gps_serial_settings_read_device ();

	/* open serial port */
#if defined(__MINGW32CE__) || defined(_WIN32_WCE)
	char2wchar (device, GPS_DEVICELEN, gps_serial_device);
	gps_serial_hGPScomm = CreateFile(device, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
#else
	gps_serial_hGPScomm = CreateFile(gps_serial_device, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
#endif
	if (gps_serial_hGPScomm == INVALID_HANDLE_VALUE) {
		d_printf ("%s:%d gps_device_open: error during CreateFile (%s)", __FILE__, __LINE__, gps_serial_device);
		return 0;
	}

	/*
	 * setup the serial device with our data
	 * read current settings, change settings and setup the new settings of the port 
	 */
	memset (&dcb, sizeof(dcb), 0);
	if (!GetCommState(gps_serial_hGPScomm, &dcb)) {
		d_printf ("%s:%d gps_device_open: error during GetCommState", __FILE__, __LINE__);
		return 0;
	}

	dcb.BaudRate = gps_serial_baudratearray[gps_serial_baudrate];
	d_printf ("set baud rate to: %d", dcb.BaudRate);
	if (!SetCommState(gps_serial_hGPScomm, &dcb)) {
		/* Error in SetCommState. Possibly a problem with the communications 
		 * port handle or a problem with the DCB structure itself.
		 */
		d_printf ("%s:%d gps_device_open: error during SetCommState", __FILE__, __LINE__);
		return 0;
	}

	/*
	 * setup the timeouts for the port
	 */
	if (!GetCommTimeouts(gps_serial_hGPScomm, &cto)) {
		d_printf ("%s:%d gps_device_open: error during GetCommTimeouts", __FILE__, __LINE__);
	}
	d_printf ("%s:%d commtimeout default: ReadIntervalTimeout %d, ReadTotalTimeoutMultiplier %d, ReadTotalTimeoutConstant %d, WriteTotalTimeoutMultiplier %d, WriteTotalTimeoutConstant %d",
	    __FILE__, __LINE__, cto.ReadIntervalTimeout, cto.ReadTotalTimeoutMultiplier, 
	    cto.ReadTotalTimeoutConstant, cto.WriteTotalTimeoutMultiplier, cto.WriteTotalTimeoutConstant);

	cto.ReadIntervalTimeout = 100;
	cto.ReadTotalTimeoutMultiplier = 1;
	cto.ReadTotalTimeoutConstant = 1;
	cto.WriteTotalTimeoutMultiplier = 0;
	cto.WriteTotalTimeoutConstant = 0;
	
	if (!SetCommTimeouts(gps_serial_hGPScomm, &cto)) {
		d_printf ("%s:%d gps_device_open: error during SetCommTimeouts", __FILE__, __LINE__);
	}
	
	return 1;
};


void gps_serial_device_close () {
	if (gps_serial_hGPScomm > 0)
		CloseHandle (gps_serial_hGPScomm);
	gps_serial_hGPScomm = 0;
};


/*
 * code for reading data
 */
extern int gdb_serial;
int gps_serial_device_read (char *ptr, int ptrsize) {
	int error, finished = 0, i;
	static char inbuf[GPS_LINELEN];
	DWORD dwread = 0;

	/* to terminate pending read: close the event */
	if (ptr == NULL) {
		return -1;
	}
	
	/* prepare for new read */
	if (GPS_LINELEN > ptrsize) i = ptrsize;
	else i = GPS_LINELEN;

	if (!ReadFile (gps_serial_hGPScomm, inbuf, i, &dwread, NULL)) {
		if ((error = GetLastError()) != ERROR_IO_PENDING) {
			/* Error in communications; report it. */
			d_printf ("ReadFile Error : %d", error);
			return -1;
		}
		dwread = 0;
	}

	if ((dwread > ((uint32_t) GPS_LINELEN)) || (dwread > (uint32_t) ptrsize)) {
		d_printf ("%s:%d error dwread:%ud > %ud || %d", __FILE__, __LINE__, dwread, GPS_LINELEN, ptrsize);
	}
	else if (dwread > 0) {
		memcpy (ptr, inbuf, dwread);
		finished = dwread;
	}

	return finished;
};
