/*
 GSport - an Apple //gs Emulator
 Copyright (C) 2010 by GSport contributors
 
 Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey

 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
*/

/* This file contains the Win32 COM1/COM2 calls */

#include "defc.h"
#include "scc.h"

#ifdef __CYGWIN__
#include <Windows.h>
#include <NspAPI.h>
#endif

#ifdef UNDER_CE
#define vsnprintf _vsnprintf
#endif

extern Scc scc_stat[2];
extern word32 g_c025_val;

#ifdef _WIN32
int
scc_serial_win_init(int port)
{
	COMMTIMEOUTS commtimeouts;
	TCHAR	str_buf[8];
	Scc	*scc_ptr;
	HANDLE	host_handle;
	int	state;
	int	ret;

	scc_ptr = &(scc_stat[port]);

	scc_ptr->state = 0;		/* mark as failed */

#ifdef UNICODE
	wsprintf(&str_buf[0], _T("COM%d"), port+1);
#else
	sprintf(&str_buf[0], "COM%d", port+1);
#endif
	host_handle = CreateFile(&str_buf[0], GENERIC_READ | GENERIC_WRITE,
			0, NULL, OPEN_EXISTING, 0, NULL);

	scc_ptr->host_handle = host_handle;
	scc_ptr->host_handle2 = malloc(sizeof(DCB));

	printf("scc_socket_init %d called, host_handle: %p\n", port,
				host_handle);

	if(host_handle == INVALID_HANDLE_VALUE) {
		scc_ptr->host_handle = 0;
		return 0;
	}

	scc_serial_win_change_params(port);


	commtimeouts.ReadIntervalTimeout = MAXDWORD;
	commtimeouts.ReadTotalTimeoutMultiplier = 0;
	commtimeouts.ReadTotalTimeoutConstant = 0;
	commtimeouts.WriteTotalTimeoutMultiplier = 0;
	commtimeouts.WriteTotalTimeoutConstant = 10;
	ret = SetCommTimeouts(host_handle, &commtimeouts);
	if(ret == 0) {
		printf("setcommtimeout ret: %d\n", ret);
	}

	state = 2;	/* raw serial */
	scc_ptr->state = state;

	return state;
}

void
scc_serial_win_change_params(int port)
{
	DCB	*dcbptr;
	HANDLE	host_handle;
	Scc	*scc_ptr;
	int	ret;

	scc_ptr = &(scc_stat[port]);

	host_handle = scc_ptr->host_handle;
	dcbptr = (DCB*)scc_ptr->host_handle2;	// OG Added cast
	if(host_handle == 0) {
		return;
	}

	ret = GetCommState(host_handle, dcbptr);
	if(ret == 0) {
		printf("getcomm port%d ret: %d\n", port, ret);
	}

#if 1
	printf("dcb.baudrate: %d, bytesize:%d, stops:%d, parity:%d\n",
		(int)dcbptr->BaudRate, (int)dcbptr->ByteSize,
		(int)dcbptr->StopBits, (int)dcbptr->Parity);
	printf("dcb.binary: %d, ctsflow: %d, dsrflow: %d, dtr: %d, dsr: %d\n",
		(int)dcbptr->fBinary,
		(int)dcbptr->fOutxCtsFlow,
		(int)dcbptr->fOutxDsrFlow,
		(int)dcbptr->fDtrControl,
		(int)dcbptr->fDsrSensitivity);
	printf("dcb.txonxoff:%d, outx:%d, inx: %d, null: %d, rts: %d\n",
		(int)dcbptr->fTXContinueOnXoff,
		(int)dcbptr->fOutX,
		(int)dcbptr->fInX,
		(int)dcbptr->fNull,
		(int)dcbptr->fRtsControl);
	printf("dcb.fAbortOnErr:%d, fParity:%d\n", (int)dcbptr->fAbortOnError,
		(int)dcbptr->fParity);
#endif

	dcbptr->fAbortOnError = 0;

	dcbptr->BaudRate = scc_ptr->baud_rate;
	dcbptr->ByteSize = scc_ptr->char_size;
	dcbptr->StopBits = ONESTOPBIT;
	switch((scc_ptr->reg[4] >> 2) & 0x3) {
	case 2: // 1.5 stop bits
		dcbptr->StopBits = ONE5STOPBITS;
		break;
	case 3: // 2 stop bits
		dcbptr->StopBits = TWOSTOPBITS;
		break;
	}

	dcbptr->Parity = NOPARITY;
	switch((scc_ptr->reg[4]) & 0x3) {
	case 1:	// Odd parity
		dcbptr->Parity = ODDPARITY;
		break;
	case 3:	// Even parity
		dcbptr->Parity = EVENPARITY;
		break;
	}

	dcbptr->fNull = 0;
	dcbptr->fDtrControl = DTR_CONTROL_ENABLE;
	dcbptr->fDsrSensitivity = 0;
	dcbptr->fOutxCtsFlow = 0;
	dcbptr->fOutxDsrFlow = 0;
	dcbptr->fParity = 0;
	dcbptr->fInX = 0;
	dcbptr->fOutX = 0;
	dcbptr->fRtsControl = RTS_CONTROL_ENABLE;

	ret = SetCommState(host_handle, dcbptr);
	if(ret == 0) {
		printf("SetCommState ret: %d, new baud: %d\n", ret,
			(int)dcbptr->BaudRate);
	}
}

void
scc_serial_win_fill_readbuf(int port, int space_left, double dcycs)
{
	byte	tmp_buf[256];
	Scc	*scc_ptr;
	HANDLE	host_handle;
	DWORD	bytes_read;
	DWORD	i;
	int	ret;

	scc_ptr = &(scc_stat[port]);

	host_handle = scc_ptr->host_handle;
	if(host_handle == 0) {
		return;
	}

	/* Try reading some bytes */
	space_left = MIN(256, space_left);
	ret = ReadFile(host_handle, tmp_buf, space_left, &bytes_read, NULL);

	if(ret == 0) {
		printf("ReadFile ret 0\n");
	}

	if(ret && (bytes_read > 0)) {
		for(i = 0; i < bytes_read; i++) {
			scc_add_to_readbuf(port, tmp_buf[i], dcycs);
		}
	}
	
}

void
scc_serial_win_empty_writebuf(int port)
{
	Scc	*scc_ptr;
	HANDLE	host_handle;
	int	rdptr;
	int	wrptr;
	int	done;
	word32	err_code;
	DWORD	bytes_written;
	int	ret;
	int	len;

	scc_ptr = &(scc_stat[port]);

	//printf("win_empty_writebuf, host_handle: %d\n", scc_ptr->host_handle);
	host_handle = scc_ptr->host_handle;
	if(host_handle == 0) {
		return;
	}

	/* Try writing some bytes */
	done = 0;
	while(!done) {
		rdptr = scc_ptr->out_rdptr;
		wrptr = scc_ptr->out_wrptr;
		if(rdptr == wrptr) {
			//printf("...rdptr == wrptr\n");
			done = 1;
			break;
		}
		len = wrptr - rdptr;
		if(len < 0) {
			len = SCC_OUTBUF_SIZE - rdptr;
		}
		if(len > 32) {
			len = 32;
		}
		if(len <= 0) {
			done = 1;
			break;
		}
		bytes_written = 1;
		ret = WriteFile(host_handle, &(scc_ptr->out_buf[rdptr]), len,
				&bytes_written, NULL);
		printf("WriteFile ret: %d, bytes_written:%d, len:%d\n", ret,
			(int)bytes_written, len);

		err_code = (word32)-1;
		if(ret == 0) {
			err_code = (word32)GetLastError();
			printf("WriteFile ret:0, err_code: %08x\n", err_code);
		}

		if(ret == 0 || (bytes_written == 0)) {
			done = 1;
			break;
		} else {
			rdptr = rdptr + bytes_written;
			if(rdptr >= SCC_OUTBUF_SIZE) {
				rdptr = rdptr - SCC_OUTBUF_SIZE;
			}
			scc_ptr->out_rdptr = rdptr;
		}
	}
}

#endif
