/************************************************************************\
 * File Version Information
 * $Header: /Altair32v2/lpr.c 47    11/08/06 9:21p Racini $
 ************************************************************************/
/************************************************************************\
  MITS Altair Emulator
  LPR device emulation

  Copyright (c) 2000-2006 Richard A. Cini

  The 88-LPC was a parallel printer interface designed by MITS for its
  related 88-LPR printer. Since I have no docs other than a test program
  for the interface, I do not know the manufacturer or the design of the
  printer mechanism itself. It does not conform to the modern ASCII
  character set, instead using 0x0-0x3f (6-bits) to represent its
  64-character set.

  The 88-LPR printer is an 80-column printer. After the 80th character
  is received in the buffer, the LPC prints the buffer contents. It's
  important to note that unless the user issues a print command, no
  characters will be printed until the buffer is full (making the LPR a
  "line printer"). If the user sends the print command and the buffer is
  not full, the current buffer contents will be printed and an automatic
  linefeed will occurr. In addition, a CR/LF will be performed when 1) the
  buffer contains the CR/LF codes or 2) 80 characters have printed without
  a CR/LF occurring.


Change Log:
  2001/12/02  RAC -- Began coding.
  2001/12/14  RAC -- RELEASE MARKER -- v2.1
  2002/01/31  RAC -- RELEASE MARKER -- v2.2
  2002/07/07  RAC -- RELEASE MARKER -- v2.3
  2002/08/23  RAC -- RELEASE MARKER -- v2.30.10
  2002/10/16  RAC -- Merged header file into sio.h or altair32.h
  2002/11/15  RAC -- RELEASE MARKER -- v2.40.2100
  2003/03/24  RAC -- Changes to setting of power flag
  2003/04/26  RAC -- RELEASE MARKER -- v2.50.2045
  2003/10/16  RAC -- Added skeleton functions for LPR_ stuff
  2003/10/29  RAC -- Began modifications to convert to 88-LPR
  2003/11/14  RAC -- 88-LPR conversion complete; test progs work.
  2003/12/20  RAC -- Added panel graphics and new Destroy fn
  2004/01/10  RAC -- Added params for ifferent character sets (MITS and
  						ASCII) IDC_RADIO_ASCII7; IDC_RADIO_MITS.
  2004/01/22  RAC -- InitDialog retrieves saved window coordinates from INI.
  2004/03/09  RAC -- Diff'ed more changes from FJS (Fred J. Scipione)
  2004/05/01  RAC -- Merged 88lpc into this module.
  2004/06/13  SML -- Rewrote portions to fix EOL handling.
  2004/07/30  RAC -- RELEASE MARKER -- v3.00.0135
  2006/05/12  RAC -- RELEASE MARKER -- v3.10.0200
  2006/11/15  RAC -- RELEASE MARKER -- v3.20.0400
\************************************************************************/
#include <windows.h>
#include <commdlg.h>
#include <commctrl.h>
#include <mmsystem.h>
#include <stdio.h>		// C-lib - I/O 
#include <string.h>
#include "altair32.h"	// includes "resource.h"
#include "comcthlp.h"	// common control macro file


/**  Defines  *************************************************/
#define LPR_WIDTH    640
#define LPR_HEIGHT   187
#define LF			0x0a
#define	CMD_PRINT	0x01	// force print from buffer
#define CMD_LF		0x02	// line feed command
#define CMD_CLRBUF	0x04	// clear buffer command
#define LPC_BUFSIZE   80	// 80 char buffer/printer line length

#define	ERR_BUFFUL	0x01	// BIT 0=0=FULL, BIT 0=1=EMPTY
#define	ERR_PRTBSY	0x02	// BIT 1=0=PRINTING, BIT 1=1=IDLE
#define	ERR_JAMMED	0x04	// BIT 2=0=JAMMED, BIT 2=1=NORMAL
#define	ERR_LFRDY	0x08	// BIT 3=0=LF IN PROCESS, BIT 3=1=RDY FOR LF
#define STAT_ON		(ERR_BUFFUL | ERR_PRTBSY | ERR_JAMMED | ERR_LFRDY)
#define STAT_OFF	   0
#define	LPR_SHUTDOWN  99


/**  Forward Prototypes  **************************************/
LRESULT CALLBACK WndProcLPR( HWND, UINT, WPARAM, LPARAM );
static  BOOL AttachLPRFile( HWND );
static  void flush_lp_buf( void );
static  void do_linefeed( void );
static  void DrawLPBitmap( HDC, HBITMAP, int, int );
static  void LPR_Write( byte );
static	void LPR_Cmd( byte );
static	int  LPR_Stat( void );


/**  Module Globals - Public **********************************/
HWND hwndLPR = NULL;


/**  Module Globals - Private *********************************/
BOOL Send_Stat = FALSE;	// insures status conditions are sent once
int bufcnt = 0;	// current characters in printer buffer
static char szBuffer[64 + _MAX_FNAME + _MAX_EXT];	// general message bufr
static char	szOpenFileTitle[_MAX_FNAME+_MAX_EXT];
static char szFileDir[_MAX_PATH + _MAX_FNAME + _MAX_EXT];
static char lprbuf[LPC_BUFSIZE];	// line buffer
static FILE	*pFileLPR = NULL;		// ptr for file opening
static HBITMAP  g_hPnlBmp = NULL;	// handle to dialog background (loaded in _Init)
static HDC		g_hWndDC = NULL;	// handle to dialog DC
static int  iLPRStat = STAT_OFF;	// 0=all errors
static int  x_size, y_size;


/**************************************************************\
*   Private Routines  *****************************************|
\**************************************************************/
/*--  AttachLPRFile  ------------------------------------------
	Assigns a file to the line printer device.
	
-------------------------------------------------------------*/
static BOOL AttachLPRFile(HWND hwnd)
{
	char	szFileName[_MAX_PATH];	// fully-qualified filename buffer
	char	szFileTitle[_MAX_FNAME + _MAX_EXT];	// filename only
	static  char szFilter[] = "Printer Capture Files (.TXT;.LPR)\0*.txt;*.lpr\0" \
                              "All Files (*.*)\0*.*\0\0";
	DWORD   fstat = 0xFFFFFFFF;
	OPENFILENAME ofn;


	bufcnt = 0;
	szFileName[0] = 0;
	szFileTitle[0] = 0;
	strcpy(szFileDir, winstate.szFilesDir);

	ofn.lStructSize       = sizeof(OPENFILENAME);
	ofn.hwndOwner         = hwnd;
	ofn.hInstance         = (HINSTANCE)NULL;
	ofn.lpstrFilter       = szFilter;		// filter string
	ofn.lpstrCustomFilter = NULL;			// used to preserve user-defined filters
	ofn.nMaxCustFilter    = 0;				// size of custom filter buffer
	ofn.nFilterIndex      = 0;				// current filter index
	ofn.lpstrFile         = szFileName;		// contains full path and filename on return
	ofn.nMaxFile          = _MAX_PATH;		// sizeof lpstrFile
	ofn.lpstrFileTitle    = szFileTitle;	// filename and extension only
	ofn.nMaxFileTitle     = _MAX_FNAME + _MAX_EXT;	// sizeof lpstrFileTitle
 	ofn.lpstrInitialDir   = szFileDir;		// initial directory
	ofn.lpstrTitle        = "Open Printer Capture File";	// title bar string or NULL
	ofn.Flags             = OFN_HIDEREADONLY | OFN_CREATEPROMPT;
	ofn.nFileOffset       = 0;				// offset to filename in lpstrFile
	ofn.nFileExtension    = 0;				// offset in TCHARs to ext. in lpstrFile
	ofn.lpstrDefExt       = "txt";			// default extension
	ofn.lCustData         = 0L;				// data passed to hook procedure
	ofn.lpfnHook          = NULL;			// pointer to hook procedure
	ofn.lpTemplateName    = NULL;			// dialog box template

	if (!GetOpenFileName(&ofn))
		return FALSE;

	fstat = GetFileAttributes(szFileName);
	if (fstat == 0xFFFFFFFF){		// FNF
		// create the file
		if((pFileLPR = fopen(szFileName, "wt")) == NULL) {
			HOST_ThrowErrMsg("AttachLPRFile: Unable to create capture file!");
			return FALSE;
		}
		fclose(pFileLPR);
		pFileLPR = NULL;
	}

	// reopen file for w
	if((pFileLPR = fopen(szFileName, "wt+")) == NULL) {
		HOST_ThrowErrMsg("AttachLPRFile: Unable to open capture file!");
		return FALSE;
	}

	// Confirm attachment and finish up
	strcpy(szOpenFileTitle, szFileTitle);
	wsprintf(szBuffer, "AttachLPRFile: Capture file %s attached successfully!", szFileTitle);
	Status_SetText(hwndStatusBar, STATBAR_READY, 0, szBuffer);
	Status_SetText(hwndStatusBar, STATBAR_LPR, 0, "LPR On");
	DoCaption(hwnd, "Line Printer", szFileTitle);
	iLPRStat = STAT_ON;	// initial status is ALL CLEAR
	return TRUE;
}


/*--  DrawLPBitmap  -------------------------------------------
	Draws a bitmap in a window. hdc is the hDC to the LP
	window. hBitmap is the handle to the panel bitmap.
	
-------------------------------------------------------------*/
static void DrawLPBitmap(HDC hdc, HBITMAP hBitmap, int xStart, int yStart)
{

	BITMAP bm;
	HDC    hdcMem;
	POINT  ptSize, ptOrg;

	hdcMem = CreateCompatibleDC(hdc);
	SelectObject(hdcMem, hBitmap);
	SetMapMode(hdcMem, GetMapMode(hdc));
	GetObject(hBitmap, sizeof(BITMAP), (LPVOID) &bm);
	ptSize.x = bm.bmWidth;
	ptSize.y = bm.bmHeight;
	DPtoLP(hdc, &ptSize, 1);
	ptOrg.x = 0;
	ptOrg.y = 0;
	DPtoLP(hdcMem, &ptOrg, 1);
	BitBlt(hdc, xStart, yStart, ptSize.x, ptSize.y, hdcMem, ptOrg.x, ptOrg.y, SRCCOPY );
	DeleteDC(hdcMem);
}


/*--  LPR_Stat  -----------------------------------------------
	Returns status variable to the I/O handler.

-------------------------------------------------------------*/
static int LPR_Stat(void)
{

	if (Send_Stat)				// send real status
		Send_Stat = FALSE;
	else {
		iLPRStat |= ERR_BUFFUL;	// clear buffer full
		iLPRStat |= ERR_PRTBSY;	// clear printer busy
		iLPRStat |= ERR_LFRDY;		// clear linfeed flag
	}

	return iLPRStat;

}


/*--  LPR_Cmd  ------------------------------------------------
	Accepts and parses printer commands. Called directly from 
	lpc02h upon writing to command register. Variable cmd_word
	is one of the three commands the printer understands.

-------------------------------------------------------------*/
static void LPR_Cmd(byte cmd_word) 
{

	switch (cmd_word){

		case CMD_PRINT:		// force print from buffer
			iLPRStat &= ~ERR_PRTBSY;	// printer busy
			iLPRStat &= ~ERR_LFRDY;	// LF busy
			flush_lp_buf();		// flush it; resets bufcnt to 0
			do_linefeed();
			Send_Stat = TRUE;		// insure status is sent
			break;
			
		case CMD_LF:		// line feed command
			iLPRStat &= ~ERR_LFRDY;	// LF busy
			do_linefeed();
			Send_Stat = TRUE;		// insure status is sent
			break;
			
		case CMD_CLRBUF:	// clear buffer command
			if (lprbuf != NULL){
				memset(lprbuf, 0, LPC_BUFSIZE);
				bufcnt = 0;
			}

			break;

	} // switch (cmd_word)

	return;
}


/*--  LPR_Write  ----------------------------------------------
	Output file opened in "text" mode so an LF in the buffer
	is translated to CRLF on write.
-------------------------------------------------------------*/
static void LPR_Write(byte data)
{
					
	if (pFileLPR == NULL) {
		iLPRStat &= ~ERR_JAMMED;
		return;
	}

	if (data < 0x20)	// 6-bit ascii used ...keep within lpr character set
		data += 0x40;
	
	if (data > 0x5f)
		data -= 0x40;

	lprbuf[bufcnt] = data;		// save data
	bufcnt++;

	if (bufcnt == LPC_BUFSIZE) {
		iLPRStat &= ~ERR_BUFFUL;	// buffer full
		iLPRStat &= ~ERR_PRTBSY;	// printer busy
		iLPRStat &= ~ERR_LFRDY;	// LF busy
		flush_lp_buf();		// flush it; resets bufcnt to 0
		do_linefeed();
		Send_Stat = TRUE;		// insure status is sent
	}

	return;

}


/**************************************************************\
*   Public Routines  ******************************************|
\**************************************************************/
/**************************************************************\
*   I/O Instruction Handlers  *********************************|
***************************************************************|
*
*	I/O instruction handlers are called from the CPU module
*  	when an IN or OUT instruction is issued.
*
*  	Each function is passed an 'io' flag, where 0 means a read
*  	from the port, and 1 means a write to the port.  On input,
*	the actual input is passed as the return value. On output,
*	'data' is written to the device.
\**************************************************************/
/*--  lpc02h  -------------------------------------------------
	This is the status register handler for the 88LPC interface
	card. io=0=read, 1=write.

-------------------------------------------------------------*/
int lpc02h(int io, int data)
{
	
	switch (io) {
		default:					// DEFAULT
		case 0:						// READ
			return LPR_Stat();		// No input data ready, and can't output
			
		case 1:						// WRITE
			LPR_Cmd((byte) data);	// Printer accepts commands
			return 0;
	}
}


/*--  lpc03h  -------------------------------------------------
	This is the data register handler for the 88LPC interface
	card. io=0=read, 1=write.
	
-------------------------------------------------------------*/
int lpc03h(int io, int data)
{

	switch (io) {
		default:					// DEFAULT
		case 0:						// READ
			return 0xff;			// no reading from a printer
			
		case 1:						// WRITE
			LPR_Write((byte) data);
			return 0;
	}
}


/*--  LPR_OpenWin  --------------------------------------------
	Called by main UI code to open up a	modeless dialog for
	the LPR device.
	
-------------------------------------------------------------*/
void LPR_OpenWin( void )
{

	// CreateDialog returns NULL on failure
	if (!IsWindow(hwndLPR)) {
		hwndLPR = CreateDialog(winstate.hInst,
								"LprDev",
								winstate.hWnd,
								(DLGPROC) WndProcLPR);
		ShowWindow(hwndLPR, SW_SHOW);
	}
}


/*--  flush_lp_buf  -------------------------------------------
	Flushes the printer buffer to the stream.
	
-------------------------------------------------------------*/
static void flush_lp_buf( void )
{
	
	if (pFileLPR == NULL)
		return;				// return if power is off

	fwrite(lprbuf, sizeof(byte), bufcnt, pFileLPR);
	if (ferror(pFileLPR))  	// 0=OK; !=0 is error
		iLPRStat &= ~ERR_JAMMED;		// problem with the file

	memset(lprbuf, 0, LPC_BUFSIZE);
	bufcnt = 0;

	return;
}


/*--  do_linefeed  --------------------------------------------
	Perform linefeed.
	
-------------------------------------------------------------*/
static void do_linefeed( void )
{
	
	if (pFileLPR == NULL)
		return;					// return if power is off

	fputc(LF, pFileLPR);			// translated to CRLF
	if (ferror(pFileLPR))  			// 0=OK; !=0 is error
		iLPRStat &= ~ERR_JAMMED;	// problem with the file

	return;
}


/*--  LPR_Destroy  --------------------------------------------
	Closes any open image files and destroys persistent objects.
	If calling _Destroy from the DlgProc, call with flag=0 so
	that any open file is available while the window is closed.
	If calling from MainWndProc during shutdown, call with
	flag=1 to ensure that any open file is closed. 
-------------------------------------------------------------*/
void LPR_Destroy(int flag)
{
	// Probably should make arrays of DCs and hBitmaps
	
	if (flag)
		LPR_Shutdown();			// shutdown file on emulator exit
	
	// delete hDCs
	if (g_hWndDC != NULL) {			// global handle to dialog DC
		DeleteDC(g_hWndDC);
		g_hWndDC = (HDC)NULL;
	}

	// delete hBitmaps
	if (g_hPnlBmp != NULL) {		// global handle to panel bitmap
		DeleteObject(g_hPnlBmp);
		g_hPnlBmp = (HBITMAP)NULL;
	}
}


/*--  LPR_Shutdown  -------------------------------------------
	Closes any open LPR log file. Called by emulator exit
	routine, DlgProc or power switch toggler.
-------------------------------------------------------------*/
void LPR_Shutdown(void)
{

	if (hwndLPR != NULL ){	// do only if window is open
		DoCaption(hwndLPR, "Line Printer", "");
		EnableWindow(GetDlgItem(hwndLPR, IDC_BTN_LPRATT), TRUE);
   		EnableWindow(GetDlgItem(hwndLPR, IDC_BTN_LPRDET), FALSE);
	}

	if (pFileLPR == NULL)
		return;
		
	fclose(pFileLPR);
	pFileLPR = NULL;
	//if (lprbuf != NULL)
	//	free(lprbuf);
	wsprintf(szBuffer, "LPRShutdown: Image file detached successfully!");
	Status_SetText(hwndStatusBar, STATBAR_READY, 0, szBuffer);
	Status_SetText(hwndStatusBar, STATBAR_LPR, 0, "LPR Off");
}


/**************************************************************\
*   Windows Callback Routines  ********************************|
\**************************************************************/
LRESULT CALLBACK WndProcLPR(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{

	char *ProfBuf;
	char buf2[80];
	static int x_size, y_size;
	RECT rc;
	register int x,y;
	
    switch (iMsg) {
		case WM_LBUTTONDOWN:
			x=LOWORD( lParam );
			y=HIWORD( lParam );

			//Power Off HIT_TEST
			/*
			if (IN_RECT(x,y,46,46+32,65,65+32)) {
				PlaySound("Click", winstate.hInst, SND_RESOURCE | SND_ASYNC | SND_NOWAIT);
				SendMessage(hDlg,WM_COMMAND,IDCANCEL,0);
			}
			*/
			break;

		case WM_INITDIALOG:
			if (pFileLPR == NULL){	// pickup LPR status
				DoCaption(hDlg, "Line Printer", "");	//start at untitled
				EnableWindow(GetDlgItem(hDlg, IDC_BTN_LPRATT), TRUE);
	    		EnableWindow(GetDlgItem(hDlg, IDC_BTN_LPRDET), FALSE);
			}
			else {
				DoCaption(hDlg, "Line Printer", szOpenFileTitle);
				EnableWindow(GetDlgItem(hDlg, IDC_BTN_LPRATT), FALSE);
	    		EnableWindow(GetDlgItem(hDlg, IDC_BTN_LPRDET), TRUE);
			}

			g_hPnlBmp=LoadBitmap(winstate.hInst,"LPPLATEN");
			g_hWndDC=GetDC(hDlg);

			// Move window into position
			GetWindowRect(hDlg, &rc);	// device window
			x_size = (int)(rc.right - rc.left);	// base size x
			y_size = (int)(rc.bottom - rc.top);	// base size y
			ProfBuf = SYSCFG_ReadConfigInfo("windows", "printer");
		    if (sscanf(ProfBuf, "%d,%d", &rc.left, &rc.top) == 2) {
				MoveWindow(hDlg, rc.left, rc.top, x_size, y_size , TRUE);
		    }

			SetFocus(GetDlgItem (hDlg, IDB_LOAD));
			return FALSE;				// don't let Windows set default focus

		case WM_CTLCOLOREDIT:
		case WM_PAINT:
			DrawLPBitmap(g_hWndDC, g_hPnlBmp, 0, 45);
			break;

		case WM_COMMAND:
	    	switch (LOWORD(wParam)) {
	    		case IDC_BTN_LPRATT:	// attach an image file
					if (AttachLPRFile(hDlg)){	// if success, enable DET and disable ATT
						EnableWindow(GetDlgItem(hDlg, IDC_BTN_LPRATT), FALSE);
	    				EnableWindow(GetDlgItem(hDlg, IDC_BTN_LPRDET), TRUE);
					}
					break;

	    		case IDC_BTN_LPRDET:	// detach an image file
					LPR_Shutdown();
					EnableWindow(GetDlgItem(hDlg, IDC_BTN_LPRDET), FALSE);
	    			EnableWindow(GetDlgItem(hDlg, IDC_BTN_LPRATT), TRUE);
					break;

				case IDCANCEL:
					GetWindowRect(hDlg, &rc);
//				    sprintf(buf2, "org=%d,%d, size=%d,%d", rc.left, rc.top, rc.bottom, rc.right);
				    sprintf(buf2, "%d,%d", rc.left, rc.top);
				    SYSCFG_WriteConfigInfo("windows", "printer", buf2);
					LPR_Destroy(0);		// destroys DCs and handles but keeps file open
					DestroyWindow(hDlg);
					hwndLPR = (HWND)NULL;
					SetForegroundWindow(winstate.hWnd);	// main window
					break;
			}	// end of switch (wParam)
 			break;
	}	// message case
	return (LRESULT)FALSE;
}

/*  end of file: lpr.c  */
