/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 23-Nov-85 | [1.91] Created change log
* 23-Nov-85 | [1.91] Test for mouse first.
* 23-Nov-85 | [1.93] Handle diagnostics button.
* 25-Nov-85 | [1.136] Include version number in login message
* 12-Dec-85 | [1.173] check_left_mouse_button now waits for button release
* 20-Dec-85 | [1.223] Removed right-button mouse tracking stuff
* 24-Dec-85 | [1.231] Call init_op_buttons
* 30-Dec-85 | [1.283] Draw iocheck box, check it 
* 31-Dec-85 | [1.290] do 'continue' after simple - switch
* 11-Jan-86 | [1.290] Added dokey, handle cursor keys on mouseless system
* 23-Jan-86 | [1.297] Put mouse check after switch scan so -nomouse has
*           | some effect
* 23-Jan-86 | [1.298] Added vmouse support for virtual mouse
* 23-Jan-86 | [1.303] Added 'lpt' variable, -printer switch
* 24-Jan-86 | [1.306] Added 'nlpt' variable, set it to -1, 0, 1, 2
* 24-Jan-86 | [1.312] Removed much of mouse support to scan.c
* 27-Jan-86 | [1.348] Save cursor around console_push
* 27-Jan-86 | [1.350] Markscreen now takes color argument
* 27-Jan-86 | [1.350] Add extra parameter to scan
* 27-Jan-86 | [1.350] Detect color display and set to 80x25
*  8-Feb-86 | [1.355] Added mark_screen_color for color support
*  8-Feb-86 | [1.355] Changed from <> to "" on includes
* 22-Feb-86 | [1.362] Changed markscreen to handle color attributes
* 22-Feb-86 | [1.362] Removed display handlers to display.c
* 22-Feb-86 | [1.362] call set_display and draw_current_screen to get
*           | instantaneous display when in color mode
* 25-Feb-86 | [1.378] Added -1447 switch, enables 1447 enter key display
* 29-Jul-86 | [1.385] Added -color switch, color_printer variable, enables
*           | color screen dumps to Canon PJ1080A color printer
* 29-Jul-86 | [1.385] Draw lamp-test button
* 30-Jul-86 | [1.398] Added check_lamp call
* 31-Jul-86 | [1.405] Made char variables unsigned
*  6-Aug-86 | [1.410] utrdykey cast first arg to (char)
*  6-Aug-86 | [1.410] Undid above becaus of error msg
*           | 1401.c 526 Warning 94: uninitialized auto variable "ch"
* 18-Aug-86 | [1.414] screen.h -> bscreen.h
* 10-Nov-91 | [1.428] <jmn> converted to Microsoft C 6.0 libraries
* 20-Nov-91 | [1.434] <jmn> added -vp switch for virtual printer
* 20-Nov-91 | [1.434] <jmn> console_nop => console_pr, handles virtual printer
*           | interface
* 21-Nov-91 | [1.437] <jmn> for EGA or VGA, map "BROWN", used throughout as a
*           | hack (because "YELLOW" blinks in text mode background) so it is
*           | really yellow (improves appearance)
* 22-Nov-91 | [1.438] <jmn> unmap color palette when done
* 23-Nov-91 | [1.455] <jmn> allow '1403' environment variable for printer
* 23-Nov-91 | [1.455] <jmn> allow redefinition of mouse cursor character
* 23-Nov-91 | [1.455] <jmn> added vars for char redefinitions
* 25-Nov-91 | [1.456] <jmn> moved char defs to chars.c
* 21-Dec-91 | [1.497] <jmn> remap cyan to gray so carriage tape looks nice
* 22-Dec-91 | [1.514] <jmn> added -form specification
* 23-Dec-91 | [1.535] <jmn> implemented -printer virtual as an option
* 23-Dec-91 | [1.544] <jmn> added -132 for 132 column printer display
*****************************************************************************/
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "conio.h"
#include "graph.h"

#include "btypes.h"
#include "scdspmsg.h"

#include "mouse.h"
#include "graph.h"
#include "hercules.h"
#include "boolean.h"
#include "logic.h"
#include "display.h"
#include "diag.h"
#include "disp.h"
#include "1401.h"
#include "lamp.h"
#include "addr.h"
#include "data.h"
#include "length.h"
#include "periph.h"
#include "iocheck.h"
#include "mode.h"
#include "enter.h"
#include "button.h"
#include "switches.h"
#include "iocheck.h"
#include "kb.h"
#include "kbd.h"
#include "scan.h"
#include "dead.h"
#include "cdr.h"
#include "alert.h"
#include "select.h"
#include "card.h"
#include "pause.h"
#include "pr.h"
#include "printers.h"
#include "print.h"
#include "vprinter.h"
#include "color.h"
#include "chars.h"
#include "form.h"

extern char version[];
boolean zip = false;
boolean c_1447 = false;
boolean done = false;
boolean print_heading = true;
boolean color_printer = false;
boolean no_screen_font = false;
boolean mode132 = false;
short lastkey;
static char * formfilename = NULL;
boolean mousing = true;
extern void pause();
#define debug_x true

struct videoconfig video;

void console_push(mouse_coord mX,mouse_coord mY);
void console_pr(mouse_coord mX,mouse_coord mY);
void console_push2(mouse_coord mX,mouse_coord mY);


static keybindings kb =
	   { 
	    console_push,	/* mouse left */
	    console_pr,		/* mouse middle */
	    console_push2,	/* mouse right */
	    ins_key,		/* INS */
	    del_key,		/* DEL */
	    move_y_up,		/* uparrow */
	    move_y_down,	/* downarrow */
	    move_x_left,	/* leftarrow */
	    move_x_right,	/* rightarrow */
	    pr_go,		/* pgup */
	    pr_go,		/* pgdn */
	    NULL,		/* home */
	    NULL,		/* end */
	    NULL,		/* c-left */
	    NULL,		/* c-right */
	    {
	     NULL,		/* 000 */
	     NULL,		/* ^A */
	     NULL,		/* ^B */
	     ctlc,		/* ^C */
	     NULL,		/* ^D */
	     NULL,		/* ^E */
	     NULL,		/* ^F */
	     NULL,		/* ^G */
	     NULL,		/* ^H */
	     NULL,		/* ^I */
	     NULL,		/* ^J */
	     NULL,		/* ^K */
	     draw_current_screen,/* ^L */
	     ins_key,		/* ^M */
	     NULL,		/* ^N */
	     NULL,		/* ^O */
	     ctlp,		/* ^P */
	     ctlq,		/* ^Q */
	     NULL,		/* ^R */
	     ctls,		/* ^S */
	     NULL,		/* ^T */
	     NULL,		/* ^U */
	     NULL,		/* ^V */
	     NULL,		/* ^W */
	     NULL,		/* ^X */
	     NULL,		/* ^Y */
	     NULL,		/* ^Z */
	     ctlc,		/* ^[ */
	     NULL,		/* ^\ */
	     NULL,		/* ^] */
	     NULL,		/* ^^ */
	     NULL		/* ^_ */
	    }
	   };

/****************************************************************************
*                                draw_lights
* Effect: 
*       Redraws screen without clearing it
****************************************************************************/

void draw_lights()
    {
     draw_a_button();
     draw_b_button();
     draw_i_button();
     draw_off_button();
     draw_load_button();
     draw_start_button();
     draw_stop_button();
     draw_reset_button();
     draw_lamp_test();

     draw_address_box();

     draw_data_boxes();

     draw_logic_box();

     draw_alerts();

     draw_len();

     redraw_messages();	/* this must FOLLOW draw_alerts! */
    }

/****************************************************************************
*                                 drawscreen
* Effect: 
*       Draws the 1401 control panel on the screen
****************************************************************************/

void drawscreen()
    {
     coord row;
     coord col;

     sccurpos(&row, &col);
     sccurset(0,0);
     _clearscreen(_GCLEARSCREEN);

     set_display(CONSOLE);

     draw_sense_switches();

     draw_diagnostic_key();

     draw_peripherals();

     draw_manual_address_box();
     
     draw_iocheck();

     draw_mode_box();

     draw_enter_stuff();

     draw_lights();

     sccurset(row,col);

    }

/****************************************************************************
*                                  activate
* Inputs:
*	mouse_coord X: X-mouse-coordinate on screen
*	mouse_coord Y: Y-mouse-coordinate on screen
* Effect: 
*       Activates a screen image if applicable
****************************************************************************/

void activate(mouse_coord mX,mouse_coord mY)
    {
     coord X;
     coord Y;

     X = mouse_to_screen_x(mX);
     Y = mouse_to_screen_y(mY);

     check_buttons(X,Y);

     check_switches(X,Y);

     check_addr_switches(X,Y,1);

     check_mode(X,Y);

     check_pattern_hit(X,Y);

     check_enter_switch(X,Y);

     check_diag(X,Y);

     check_peripherals(X,Y);

     check_IOcheck(X,Y);

     check_lamp(X,Y);

    }

/****************************************************************************
*                                console_push2
* Inputs:
*       mouse_coord X: mouse X-coordinate of mouse hit
*	mouse_coord Y: mouse Y-coordinate of mouse hit
* Effect: 
*       Handles decrementing address switches.
****************************************************************************/

void console_push2(mouse_coord mX,mouse_coord mY)
    {
     coord x;
     coord y;

     sccurpos(&y, &x);
     hide_mouse_cursor();


     if(!check_addr_switches(mouse_to_screen_x(mX),mouse_to_screen_y(mY),-1))
	{
#if 0
	 display_address(mX);
	 set_A(mY & 0177);
	 set_B((mY/4) & 0177);
	 set_O(mouse_to_screen_y(mY4) & 0177);
	 set_compare( mX == mY ? logic_BEQA :
	 		( mY > mX ? logic_BGTA : logic_BLTA));
    	 show_compare();

	 sprintf(msg,"X=%3d, Y=%3d",mX,mY);
	 scdspmsg(0,0,H_NORMAL,0,msg);
#endif
	}
     sccurset(y, x);
     show_mouse_cursor();
    }

/****************************************************************************
*                               console_push
* Inputs:
*       mouse_coord mX: mouse X-coordinate of hit
*	mouse_coord mY: mouse Y-coordinate of hit
* Effect: 
*       Activates the switch or button pushed
****************************************************************************/

void console_push(mouse_coord mX, mouse_coord mY)
    {
     coord x;
     coord y;

     sccurpos(&y, &x);
     hide_mouse_cursor();
     activate(mX,mY);
     sccurset(y,x);
     show_mouse_cursor();
    }

/****************************************************************************
*                                console_pr
* Inputs:
*       mouse_coord mX: mouse X-coordinate
*	mouse_coord mY: mouse Y-coordinate
* Effect: 
*	If virtual printer, switches to printer screen, otherwise does
*	nothing
****************************************************************************/
/*ARGSUSED */

void console_pr(mouse_coord mX,mouse_coord mY)
    {
     if((*current_printer->is_virtual)())
        { /* virtual printer */
	 vprinter();
	} /* virtual printer */
    }

/****************************************************************************
*                              use_mouse_cursor
* Inputs:
*       unsigned char ch: Character to use for software cursor
* Result: void
*       
* Effect: 
*       Sets the current mouse software cursor character to ch
****************************************************************************/

void use_mouse_cursor(unsigned char ch)
    {
     short M1;
     short M2;
     short M3;
     short M4;

     M1 = mouse_set_text_cursor;
     M2 = mouse_software_cursor;
     M3 = 0x0000;
     M4 = (0x0F << 8) | ch;
     
     vmouse(&M1,&M2,&M3,&M4);

    }

/****************************************************************************
*                                    card1
* Result: void
*       
* Effect: 
*       Prints the first card
****************************************************************************/

void card1(void)
    {
     char msg[80];

     sprintf(msg,"IBM 1401 SIMULATOR     VERSION %s",version);
     card(msg);
    }

/****************************************************************************
*                                    card2
* Result: void
*       
* Effect: 
*       Prints the second card
****************************************************************************/

void card2(void)
    {
     card("COPYRIGHT 1985, 1991, JOSEPH M. NEWCOMER;  ALL RIGHTS RESERVED");
    }

/****************************************************************************
*                               center_message
* Inputs:
*       char * msg: Message to write
* Result: void
*       
* Effect: 
*       Centers the message on the next-to-bottom line
****************************************************************************/

void center_message(char * msg)
    {
     _settextposition(video.numtextrows-1,
     				(video.numtextcols - strlen(msg))/2);
     _outtext(msg);
    }

/****************************************************************************
*                                    about
* Result: void
*       
* Effect: 
*       Displays the startup sequence
****************************************************************************/

void about()
    {
     card1();
     center_message("Hit any key for next screen");
     getch();
     card2();
     center_message("Hit any key to continue");
     getch();
     draw_current_screen();
    }

/****************************************************************************
*                                set_color_map
* Result: void
*       
* Effect: 
*       Sets the EGA or VGA color map
****************************************************************************/

void set_color_map()
    {
     switch(video.adapter)
        { /* special maps */
	 case _EGA:
	 case _VGA:
		 _remappalette(BROWN,_YELLOW);
		 _remappalette(CYAN, _GRAY);
		 break;
	} /* special maps */
    }

/****************************************************************************
*                             standard_video_mode
* Result: boolean
*       true if success
*	false if error
* Effect: 
*       Initializes the video mode for all standard modes
****************************************************************************/

boolean standard_video_mode()
    {
     if(_setvideomode(_TEXTC80) == 0)
        { /* not color */
	 if(_setvideomode(_TEXTBW80) == 0)
	    { /* failure */
	     printf("Unable to initialize display\n");
	     return false;
	    } /* failure */
	} /* not color */

     _getvideoconfig(&video);
     if(video.numxpixels == 0)
	video.numxpixels = screen_to_mouse_x(video.numtextcols);
     if(video.numypixels == 0)
	video.numypixels = screen_to_mouse_y(video.numtextrows);

     set_color_map();

     return true;
    }

/****************************************************************************
*                                    main
* Inputs:
*	int argc: count of args
*       char * argv[]: argument words
* Result: int
*	0 if no error
*	1 if error
* Effect: 
*       Runs everything
****************************************************************************/

int main(int argc,char * argv[])
    {
     short M1, M2, M3, M4;
     short i;
     char * env;

     /*
	Process all environment variables here; that way they can be
	later overridden by command line options:
     */
     env = getenv("1403");
     if(env != NULL)
        { /* default 1403 */
	 if(stricmp(env,"virtual") == 0)
	    { /* virtual */
	     current_printer = &virtual_printer;
	    } /* virtual */
	 else
	    { /* physical */
	     if(stricmp(env,"physical") == 0)
		{ /* physical */
		 current_printer->lpt = "prn";
		 current_printer->nlpt = 0;
		 current_printer = &physical_printer;
		} /* physical */
	     else
	     if(stricmp(env,"prn") == 0)
	        { /* prn */
		 current_printer->lpt = "prn";
		 current_printer->nlpt = 0;
		 current_printer = &physical_printer;
		} /* prn */
	     else
	     if(stricmp(env,"lpt1") == 0)
	        { /* lpt1 */
		 current_printer->lpt = "lpt1";
		 current_printer->nlpt = 0;
		 current_printer = &physical_printer;
		} /* lpt1 */
	     else
	     if(stricmp(env,"lpt2") == 0)
	        { /* lpt1 */
		 current_printer->lpt = "lpt2";
		 current_printer->nlpt = 0;
		 current_printer = &physical_printer;
		} /* lpt2 */
	     else
	     if(stricmp(env,"lpt3") == 0)
	        { /* lpt3 */
		 current_printer->lpt = "lpt3";
		 current_printer->nlpt = 0;
		 current_printer = &physical_printer;
		} /* lpt3 */
	     else
	     if(stricmp(env,"nul") == 0)
	        { /* nul */
		 current_printer->lpt = "nul";
		 current_printer->nlpt = 0;
		 current_printer = &physical_printer;
		} /* nul */
	    } /* physical */
	} /* default 1403 */

     current_printer->lpt = "prn";
     current_printer->nlpt = 0;

     if(!standard_video_mode())
	return 1;

     /* process the keywords */

     i = 0;

     while ((++i)<argc)
        { /* process args */

/* -132 */
	if(stricmp(argv[i],"-132") == 0)
	   { /* set 132 column printer mode */
	    mode132 = true;
	    continue;
	   } /* set 132 column printer mode */

/* -1447 */
	if(stricmp(argv[i],"-1447") == 0)
	   { /* set 1447 */
	    c_1447 = true;
	    continue;
	   } /* set 1447 */

/* -color */
	if(stricmp(argv[i],"-color") == 0)
	   { /* -color */
	    color_printer = true;
	    continue;
	   } /* -color */
	    
/* -form */
	if(stricmp(argv[i],"-form") == 0)
	   { /* form */
	    if(++i < argc && argv[i][0] != '-')
	       { /* has form */
	        formfilename = argv[i];
		continue;
	       } /* has form */
	    fprintf(stderr,"-form requires argument\n");
	    return 1;
	   } /* form */
/* -debug */

	 if(stricmp(argv[i],"-d") == 0 || stricmp(argv[i],"-debug") == 0)
	    { /* -debug */
	     diagnostics_on = true;
	     tell("Diagnostics initialized");
	     continue;
	    } /* -debug */
     
/* -slow */
	 if(stricmp(argv[i],"-slow") == 0)
	    { /* -slow */
	     setslow(true);
	     continue;
	    } /* -slow */

/* -zip */
	if(stricmp(argv[i],"-zip") == 0)
	   { /* -zip */
	    zip = true;
	    continue;
	   } /* -zip */

/* -nomouse */
	 if(stricmp(argv[i],"-nomouse") == 0)
	    { /* -nomouse */
	     mousing = false;
	     continue;
	    } /* -nomouse */

/* -printer <dev> */
	if(stricmp(argv[i],"-printer") == 0)
	   { /* -printer */
	    i++;
	    if(i>=argc || argv[i][0] == '-')
	       { /* missing */
	        fprintf(stderr,"-printer requires argument\n");
		return 1;
	       } /* missing */
	    if(stricmp(argv[i],"lpt2") == 0) 
	       { /* lpt2 */
		current_printer->lpt = argv[i];
		current_printer = &physical_printer;
	        current_printer->nlpt = 1;
	       } /* lpt2 */
	    else
	    if(stricmp(argv[i],"lpt3") == 0) 
	       { /* lpt2 */
		current_printer->lpt = argv[i];
		current_printer = &physical_printer;
	        current_printer->nlpt = 2;
	       } /* lpt2 */
	    else
	    if(stricmp(argv[i],"prn") == 0 || stricmp(argv[i],"lpt1") == 0) 
	       { /* lpt1 */
		current_printer->lpt = argv[i];
		current_printer = &physical_printer;
	        current_printer->nlpt = 0;
	       } /* lpt1 */
	    else
	    if(stricmp(argv[i],"virtual") == 0) 
	       { /* virtual */
		current_printer = &virtual_printer;
	       } /* virtual */
	    else
	       current_printer->nlpt = -1;
	    continue;
	   } /* -printer */

/* -help */
	if ((stricmp(argv[i],"-help") == 0) || (stricmp(argv[i],"?") == 0))
	   { /* -help */
	    /* cmdline_help(); */
	    return 0;
	   } /* -help */

/* -nsf */
	if(stricmp(argv[i],"-nsf") == 0)
	   { /* no screen font */
	    no_screen_font = true;
	    continue;
	   } /* no screen font */

/* -vp */
	if(stricmp(argv[i],"-vp") == 0)
	   { /* enable virtual printer */
	    current_printer = &virtual_printer;
	    continue;
	   } /* enable virtual printer */

/* - */
	if( stricmp(argv[i],"-") == 0) 
	   { /* suppress heading */
	    print_heading = false;
	    continue;
	   } /* suppress heading */

/* error */

	} /* process args */

     
     M1 = mouse_reset;
     mouse(&M1,&M2,&M3,&M4);		/* NOT vmouse! */

     if(M1==0)
       if(mousing)
        { /* Eek! No mouse! */
	 dead_mouse();
	 mousing = false;
	 _setnormalcursor();
	 sccurset(12,40);
	} /* Eek! No mouse! */
     else
        { /* fake it */
	 _setnormalcursor();
	 sccurset(12,40);
	} /* fake it */
	 

     diag_init();

     init_op_buttons();

     /* Initialize the printer */

     /*
	We must first load the form specification
     */

     {
      channel_tape * t = NULL;
      if(formfilename != NULL)
	 t = load_form(formfilename);
      
      open_printer(current_printer,t);
     }

     /* Initialize the card reader */

     init_cdr();

     /* Remap the palette so colors come out right */
     
     switch(video.adapter)
        { /* turn off -132 */
	 case _VGA:
		 break;
	 default:
		 mode132 = false;
		 break;
	} /* turn off -132 */

     _setnocursor();

     if(print_heading)
        { /* print heading */
	 card1();
	 pause(3000);
	 card2();
	 pause(3000);
	} /* print heading */

     set_display(CONSOLE);

     draw_current_screen();

     if(!mousing) 
        { /* init cursor */
	 _setnormalcursor();
	 sccurset(12,40);
	} /* init cursor */

     /* set sensitivity */

     M1 = mouse_set_sensitivity;
     M3 = 4;		/* 4 mickeys/8 pixels horizontally */
     M4 = 8;		/* 8 mickeys/8 pixels vertically */

     vmouse(&M1,&M2,&M3,&M4);

     show_mouse_cursor();

     /* At this point, char_mi may have been redefined by printer setup */
     use_mouse_cursor(char_mi);

/*     select_initial_position(); */   /* Put cursor on most likely button */

     scan(&done,&kb,false);

     sccurset(0,0);
     _clearscreen(_GCLEARSCREEN);

     M1 = mouse_set_text_cursor;
     M2 = mouse_hardware_cursor;
     M3 = 12;
     M4 = 13;

     vmouse(&M1,&M2,&M3,&M4);
     _setnormalcursor();

     close_printer(current_printer);

     hide_mouse_cursor();

     /*
	Reset the mouse so it is all cleared and doesn't interfere
	with whatever runs after us!
     */
     M1 = mouse_reset;
     mouse(&M1,&M2,&M3,&M4);		/* NOT vmouse! */

     { /* reset palette */
      static long colors[16] = { _BLACK,      _BLUE,      
      			         _GREEN,      _CYAN, 
      				 _RED,        _MAGENTA,   
				 _BROWN,      _WHITE,
				 _GRAY,       _LIGHTBLUE, 
				 _LIGHTGREEN, _LIGHTCYAN,
				 _LIGHTRED,   _LIGHTMAGENTA, 
				 _YELLOW,     _BRIGHTWHITE};
      short i;
      for(i=0; i < 16; i++)
	 _remappalette(i,colors[i]);
     } /* reset palette */

     _setnormalcursor();

     return 0;
    }


/****************************************************************************
*                                   bounded
* Inputs:
*       coord X: current X position
*	coord Y: current Y position
*	coord X0: top left X value
*	coord Y0: top left Y value
*	coord X1: bottom right X value
*	coord Y1: bottom right Y value
* Result: boolean
*       true if X,Y is in box described X0,Y0,X1,Y1
*	false otherwise
****************************************************************************/
 
boolean bounded(coord X,coord Y,coord X0,coord Y0,coord X1,coord Y1)
    {
#if 0
     sccurset(0,15);
     printf("X=%d, Y=%d, X0=%d, Y0=%d, X1=%d, Y1=%d",
     			X,Y,X0,Y0,X1,Y1);
#endif
     return (X >= X0) && (X <= X1) && (Y >= Y0) && (Y <= Y1);
    }

/****************************************************************************
*                                   ismono
* Result: boolean
*       true if mono adapter
*	false if color adapter
****************************************************************************/

boolean ismono()
    {
     switch(video.monitor)
        { /* decode monitor type */
	 case _ANALOGMONO:
	 case _MONO:
		 return true;
	} /* decode monitor type */
     return false;
    }

/****************************************************************************
*                                    pr_go
* Result: void
*       
* Effect: 
*       Activates the printer from a key
****************************************************************************/

void pr_go()
    {
     if((*current_printer->is_virtual)())
        { /* virtual printer */
	 vprinter();
	} /* virtual printer */
    }
