// (C) Фролов Г.В., 1995
// Адрес E-mail: frolov@glas.apc.org

#include	<windows.h>
#include	<string.h>
#include	<conio.h>
#include	<stdio.h>

// Определение констант
#define	QUEUE_SIZE	1024
#define	CTRL_Q		17
#define	CTRL_S		19
#define	ESC			27 

// Прототипы функций
int InitCommPort(void);
int CloseCommPort(int idCommPort);
int ProcessExchange(int idCommPort);

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain( HANDLE hInstance,
			HANDLE hPrevInstance,
			LPSTR lpszCmdLine,
			int cmdShow )
{
	int	idCommPort;				// иденитификатор COM-порта


	// Позволяем одновременно запустить только одну копию приложения
	if( hPrevInstance )
		return( FALSE );

	// Инициализация интерфейса EasyWin
	_InitEasyWin();

	// Открываем COM-порт и устанавливаем новый режим работы
	idCommPort = InitCommPort();

	// В случае возникновения ошибки при инициализации COM-порта
	// завершаем работу приложения
	if(idCommPort < 0)
		return FALSE;

	puts( "Для завершения программы нажмите клавишу <ESC>" );

	while( TRUE )
	{
		MSG    msg;

		// Организуем цикл обработки сообщений
		if( PeekMessage( ( LPMSG )&msg, NULL, 0, 0, PM_REMOVE ) )
		{
			// При получении сообщения WM_QUIT завершаем приложение
			if ( msg.message == WM_QUIT )
				return( msg.wParam );

			TranslateMessage( &msg );
			DispatchMessage ( &msg );
		}

		// Если в очереди приложения нет сообщений, начинаем обмен
		// данными через COM-порт
		else
      {
			if( !ProcessExchange( idCommPort ))
			{
				// Помещаем сообщение WM_DESTROY в очередь приложения
				PostMessage( GetFocus(), WM_DESTROY, 0, 0L  );
			}
      }
	}
}

// =====================================
// Функция InitCommPort
// =====================================
int InitCommPort()
{
	DCB	dcbCommPort;	// структура DCB
	int	idCommPort;		// идентификатор COM-порта

	char	szMsg[144];				// временный буфер
	char	szCommSettings[20];	// режим работы COM-порта
	char	szPortName[6];			// имя COM-порта


	// Получаем из раздела [Port] файла PHONE.INI строку Mode,
	// определяющую режим работы COM-порта и записываем ее в буфер
	// szCommSettings

	GetPrivateProfileString("Port", "Mode", "COM1:9600,n,8,1",
			szCommSettings, sizeof(szCommSettings) - 1, "easytty.ini");


	// Выделяем из полученной строки первые четыре символа, задающие
	// номер COM-порта для последующей передачи его функции OpenComm
	lstrcpyn(szPortName, szCommSettings, 5);
	szPortName[4] = '\0';


	// Открываем COM-порт szPortName
	if (( idCommPort = OpenComm( szPortName, QUEUE_SIZE, QUEUE_SIZE )) < 0 )
	{
		// В случае ошибки отображаем сообщение и завершаем работу приложения
		wsprintf( szMsg,
			"Ошибка открытия порта.\nФункция OpenComm возвратила %d",
			idCommPort );

		MessageBox( NULL, szMsg,
						"Ошибка", MB_OK | MB_ICONEXCLAMATION );

		return( idCommPort );
	}

	// Удаляем все символы из выходной очереди
	FlushComm( idCommPort, 0 );

	// Удаляем все символы из входной очереди
	FlushComm( idCommPort, 1 );

	// Заполняем структуру DCB в соответствии с командной строкой
	// Mode из раздела [Port] файла PHONE.INI

	if( BuildCommDCB( szCommSettings, &dcbCommPort ) != 0 )
	{
		// В случае ошибки отображаем сообщение и завершаем работу приложения
		MessageBox( NULL, "Ошибка при заполнении структуры DCB",
						"Ошибка", MB_OK | MB_ICONEXCLAMATION );
		return( -1 );
	}

	// Устанавливаем новый режим COM-порта в соответствии с
	// подготовленной структурой DCB
	if ( SetCommState( &dcbCommPort ) != 0 )
	{
		// В случае ошибки отображаем сообщение и завершаем работу приложения
		MessageBox( NULL, "Ошибка установки режима COM-порта",
						"Ошибка", MB_OK | MB_ICONEXCLAMATION );

		return( -1 );
	}

	// Возвращаем идентификатор открытого COM-порта
	return idCommPort;
}

// =====================================
// Функция ProcessExchange
// =====================================
int ProcessExchange( int idCommPort )
{
	int nCharWaiting, nCharWriting;
	COMSTAT	ComStat;
	char	inBuff[ QUEUE_SIZE ];

	// Определяем текущее состояние открытого COM-порта
	GetCommError( idCommPort, &ComStat );

	// Если во входной очереди уже есть данные считываем их и
	// выводим на экран
	if ((nCharWaiting = ComStat.cbInQue ) > 0 )
	{

		// Считываем данные из входной очереди и помещаем их в буффер inBuff
		nCharWaiting = ReadComm( idCommPort, inBuff,
							( nCharWaiting > QUEUE_SIZE ? QUEUE_SIZE : nCharWaiting ));

		// Отображаем полученные символы на экране
		if ( nCharWaiting > 0 )
			for( int i = 0; i < nCharWaiting; i++ )	putch(inBuff[i]);

		else
			return( FALSE );
	}

	// Узнаем, нажата ли какая-нибудь клавиша на клавиатуре
	else if ( kbhit() )
	{
		// Если клавиша нажата, определяем ее код
		char keyHit  = ( char )getch();

		if ( !keyHit )
			keyHit = ( char )getch();

		// Если нажата клавиша <Esc> закрываем COM-порт и завершаем
		// работу приложения
		if ( keyHit == ESC )
		{
			CloseCommPort( idCommPort );
			return( FALSE );
		}

		// Записывем код нажатой клавиши в выходную очередь COM-порта
		else
		{
			nCharWriting = WriteComm( idCommPort, ( LPSTR )&keyHit, 1 );

			// При возникновении ошибки завершаем приложение
			if( nCharWriting < 0 )
				return( FALSE );
		}
	}
	return( TRUE );
}

// =====================================
// Функция CloseCommPort
// =====================================
int CloseCommPort( int idCommPort )
{
	// Удаляем все символы из входной и выходной очереди COM-порта
	FlushComm( idCommPort, 0 );
	FlushComm( idCommPort, 1 );

	// Закрываем COM-порт
	CloseComm( idCommPort );

	return 0;
}

