/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 16-Nov-85 | [1.56] Created
* 23-Nov-85 | [1.91] Wait for mouse cursor up; hide mouse cursor during bit
*           | flip
* 11-Dec-85 | [1.164] ^L refreshes screen
* 11-Dec-85 | [1.167] Hide mouse cursor before displaying keyboard char
* 29-Dec-85 | [1.270] Do not allow Enter key to work unless in Alter mode
* 11-Jan-86 | [1.290] Handle user hitting function keys...support mouseless
*           | system
* 11-Jan-86 | [1.293] Do not update character or store it if console display
*           | not active
* 11-Jan-86 | [1.296] No longer avoid getkey when running
* 23-Jan-86 | [1.305] Introduce read_keyboard_char to handle cursor in
*           | non-mouse mode
* 23-Jan-86 | [1.306] Recognize ^C in scan loop
* 24-Jan-86 | [1.306] clear_off when memory data enter button hit
* 25-Jan-86 | [1.328] New screen format; major rewrite
* 25-Jan-86 | [1.347] Added code for alter-plus-one
* 23-Feb-86 | [1.367] Color support.  printfs go to scdspmsg
* 23-Feb-86 | [1.367] include <> => include ""
* 25-Feb-86 | [1.378] Use row-specific colors for 1447 input mode.  When we
*           | rediscover the colors used on the 1447 we will change the
*           | initialization for this
* 29-Jul-86 | [1.385] Added C-P option to do screen dump
* 31-Jul-86 | [1.405] Removed keyboard support to kbd.c
* 31-Jul-86 | [1.405] Made char variables unsigned
* 18-Aug-86 | [1.414] screen.h -> bscreen.h
* 10-Nov-91 | [1.428] <jmn> converted for Microsoft C 6.0
* 18-Nov-91 | [1.428] <jmn> memory.h => mem1401.h, avoid ANSI name
* 23-Nov-91 | [1.455] <jmn> converted to support mappable screen chars
* 23-Dec-91 | [1.522] <jmn> added stack checking pragmas
*****************************************************************************/


/*****************************************************************************
				1401 Simulation

			      Enter Switch Module

This module handles the bit-pattern select and ENTER switch

*****************************************************************************/

#include "stdio.h"
#include "boolean.h"

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

#include "panel.h"
#include "mem1401.h"
#include "mach.h"
#include "modes.h"
#include "mouse.h"
#include "keys.h"
#include "hercules.h"
#include "disp.h"
#include "diag.h"
#include "enter.h"
#include "addr.h"
#include "display.h"
#include "kb.h"
#include "scan.h"
#include "color.h"
#include "bcd.h"
#include "kbd.h"
#include "1401.h"
#include "button.h"
#include "enter.h"
#include "pause.h"
#include "chars.h"

unsigned char bcd_enter_char = '\0';
extern int current_mode;
extern boolean mousing;
extern int lastkey;
extern boolean tracking;
extern boolean color_printer;

coord select_zones = 0;
coord select_numeric = 0;
boolean alter_plus_one = false;

#define debug_xy true

attrib row_colors[4];


/****************************************************************************
*                              get_enter_address
* Result: int
*       Place to enter data
****************************************************************************/

int get_enter_address()
    {
     if(alter_plus_one)
	return B_addr++;

     return get_address_switches();

    }

/****************************************************************************
*                                   mark_WM
* Inputs:
*       boolean set: true to highlight, false to unhighlight
* Effect: 
*       Marks the WM enter button as appropriate
****************************************************************************/

void mark_WM(boolean set)
    {
     mark_screen(enter_X+1,enter_Y+4,"  ",set);
     mark_screen(enter_X+1,enter_Y+5,"WM",set);
    }


/****************************************************************************
*                                   mark_NWM
* Inputs:
*       boolean set: true to highlight, false to unhighlight
* Effect: 
*       Marks the ~WM enter button as appropriate
****************************************************************************/

void mark_NWM(boolean set)
    {
     mark_screen(enter_X+1,enter_Y+1,"__",set);
     mark_screen(enter_X+1,enter_Y+2,"WM",set);
    }


/****************************************************************************
*                               draw_enter_box
* Effect: 
*       Draws the 'enter' key box on the screen
****************************************************************************/

void draw_enter_box()
    {
     attrib fore;
     attrib back;


     if(ismono())
        { /* mono */
	 fore = H_NORMAL;
	 back = 0;
	} /* mono */
     else
        { /* color */
	 fore = LTBLUE;
	 back = BLACK;
	} /* color */

     scdspmsg(enter_Y,enter_X,fore,back,  "ษออป");
     scdspmsg(enter_Y+1,enter_X,fore,back,"บ__บ");
     scdspmsg(enter_Y+2,enter_X,fore,back,"บWMบ");
     scdspmsg(enter_Y+3,enter_X,fore,back,"ฬออน");
     scdspmsg(enter_Y+4,enter_X,fore,back,"บ  บ");
     scdspmsg(enter_Y+5,enter_X,fore,back,"บWMบ");
     scdspmsg(enter_Y+6,enter_X,fore,back,"ศออผ");
     mark_WM(false);
     mark_NWM(false);
    }

/****************************************************************************
*                                draw_bit_box
* Effect: 
*       Draws the bit pattern select box on the screen
****************************************************************************/

void draw_bit_box()
    {
     attrib fore;
     attrib back;
     attrib d_back;
     attrib h_fore;
     attrib h_back;


     if(ismono())
        { /* mono */
	 fore = H_NORMAL;
	 back = 0;
	 h_fore = H_NORMAL;
	 h_back = 0;
	 row_colors[0] = row_colors[1] = row_colors[2] = row_colors[3] = H_NORMAL;
	 d_back = 0;
	} /* mono */
     else
        { /* color */
	 fore = LTBLUE;
	 back = BLACK;
	 row_colors[0] = WHITE;
	 row_colors[1] = WHITE;
	 row_colors[2] = WHITE;
	 row_colors[3] = WHITE;
	 d_back = BLACK;
	 h_fore = legend_fore;
	 h_back = legend_back;
	} /* color */

     scdspmsg(pattern_Y,pattern_X,fore,back,  "ึฤฤฤ                ฤฤฤท");
     scdspmsg(pattern_Y+1,pattern_X,fore,back,"ฬอออหออออออออออออออออออน");
     scdspmsg(pattern_Y+2,pattern_X,fore,back,"บ   บ                  บ");
     scdspmsg(pattern_Y+3,pattern_X,fore,back,"บ   บ                  บ");
     scdspmsg(pattern_Y+4,pattern_X,fore,back,"บ   บ                  บ");
     scdspmsg(pattern_Y+5,pattern_X,fore,back,"บ   บ                  บ");
     scdspmsg(pattern_Y+6,pattern_X,fore,back,"ศอออสออออออออออออออออออผ");
     scdspmsg(pattern_Y,pattern_X+4,h_fore,h_back,"Character Select");
{
 char tmp[20];
 sprintf(tmp," 1234567890%c%c:>%c",char_equal,char_quote,char_sq);
 scdspmsg(pattern_Y+2,pattern_X+6,row_colors[0],d_back,tmp);
 sprintf(tmp,"%cABCDEFGHI?.%c[<%c",char_plus,char_rparen,char_gm);
 scdspmsg(pattern_Y+3,pattern_X+6,row_colors[1],d_back,tmp);
 sprintf(tmp,"-JKLMNOPQR!$*];%c",char_dl);
 scdspmsg(pattern_Y+4,pattern_X+6,row_colors[2],d_back,tmp);
 sprintf(tmp,"b/STUVWXYZ%c,%c%c\\%c",char_lparen,char_rm,char_wm,char_sm);
 scdspmsg(pattern_Y+5,pattern_X+6,row_colors[3],d_back,tmp);
} 
     scdspmsg(pattern_Y+2,pattern_X+2,row_colors[0],d_back,"NO");
     scdspmsg(pattern_Y+3,pattern_X+2,row_colors[1],d_back,"BA");
     scdspmsg(pattern_Y+4,pattern_X+2,row_colors[2],d_back," B");
     scdspmsg(pattern_Y+5,pattern_X+2,row_colors[3],d_back," A");


    }

/****************************************************************************
*                                 mark_column
* Inputs:
*       coord col: Column to mark, 0..15
*	boolean set: Value to set it to
* Effect: 
*       Marks the character selection dial column to the value indicated
****************************************************************************/

void mark_column(coord col,boolean set)
    {
     coord i;
     for(i=0;i<4;i++)
        { /* mark it */
	 markscreen((coord)(pattern_Y+2+i),(coord)(pattern_X+6+col),set,
	 			BLACK,WHITE,row_colors[i],BLACK);
	} /* mark it */
    }

/****************************************************************************
*                                 mark_row
* Inputs:
*       coord row: Column to mark, 0..3
*	boolean set: Value to set it to
* Effect: 
*       Marks the zone selection dial row to the value indicated
****************************************************************************/

void mark_row(coord row,boolean set)
    {
     coord i;
     for(i=0;i<3;i++)
        { /* mark it */
	 markscreen((coord)(pattern_Y+2+row),(coord)(pattern_X+1+i),set,
	 			BLACK,WHITE,row_colors[i],BLACK);
	} /* mark it */
    }

/****************************************************************************
*                               show_selection
* Effect: 
*       Marks the selection
****************************************************************************/

void show_selection()
    {
     mark_column(select_numeric,true);
     mark_row(select_zones,true);
    }

/****************************************************************************
*                              draw_enter_stuff
* Effect: 
*       Draws all the boxes for the enter mechanism
****************************************************************************/

void draw_enter_stuff()
    {
     draw_enter_box();
     draw_bit_box();
     show_selection();
    }

/****************************************************************************
*                                 select_zone
* Inputs:
*       coord X: Screen X-coordinate of mouse hit
*	coord Y: Screen Y-coordinate of mouse hit
* Effect: 
*       Snaps the mouse cursor to the zone selection dial, then
*	tracks the mouse up or down until the button is released
****************************************************************************/

void select_zone(coord X,coord Y)
    {
     short M1, M2, M3, M4;

     coord wY;		/* Snap row */
     mouse_coord minX;		/* minimum X position */
     mouse_coord minY;		/* minimum Y position */
     mouse_coord maxX;		/* maximum X position */
     mouse_coord maxY;		/* maximum Y position */
     mouse_coord cX;		/* current X position */
     mouse_coord cY;		/* current Y position */
     short VM2;		/* virtual mouse (cursor arrow simulation */
     boolean changed;	/* change flag */

     /* Snap mouse cursor to wheel position */

     wY = pattern_Y + 2 + select_zones;

     M1 = mouse_set_cursor;
     M2 = 0;
     M3 = screen_to_mouse_x(X);
     M4 = screen_to_mouse_y(wY);
     vmouse(&M1,&M2,&M3,&M4);

     /* Now drag the lit row up and down with the mouse */

     cX = M3;
     cY = M4;
     minY = screen_to_mouse_y(pattern_Y + 2);
     maxY = screen_to_mouse_y(pattern_Y + 5);
     minX = screen_to_mouse_x(pattern_X + 1);
     maxX = screen_to_mouse_x(pattern_X + 3);

     if(lastkey != KEYACTIVATE)
	VM2 = 0;		/* no virtual mouse */
     else
	VM2 = MOUSEACTIVATE;

     while(true)
        { /* track it */
	 lastkey = 0;
	 
	 /* read the cursor position */

	 get_keyboard_char(current_kb);
	 /* lastkey may be zero, but we need to see motion */

	 M1 = mouse_get_cursor;
	 vmouse(&M1,&M2,&M3,&M4);
	 if(lastkey != 0)
	    { /* keys */
	     if(lastkey == KEYACTIVATE2) 
		M2 = VM2 = 0;	/* simulate release of mouse key */
	    } /* keys */
	 else
	    if(VM2!=0) 	 
	       M2 = VM2;

	 if(tracking)
	    { /* report motion */
	     trackit("zon",M2,M3,M4);
	     pause(100);
	    } /* report motion */

	 if((M2 & 7) == 0) 
	    break;	/* button released */
	 
	 /* Don't let it drag outside the valid columns */

	 changed = false;

	 if(M3 < minX) { M3 = minX; changed = true;}
	 else
	 if(M3 > maxX) { M3 = maxX; changed = true;}

	 if(M4 < minY) { M4 = minY; changed = true;}
	 else
	 if(M4 > maxY) { M4 = maxY; changed = true;}

	 /* Set the cursor in case the above code did a limit operation */

	 if(changed) 
	    { /* fix change */

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

	    } /* fix change */

	 /* Did we move at all? */

	 if(M4 == cY) 
	    continue;		/* no X-motion, continue */

	 /* We have changed rows */

	 mark_row(mouse_to_screen_y(cY-minY),false);
	 mark_row(mouse_to_screen_y(M4-minY),true);
	 cY = M4;
	 
	 select_zones = mouse_to_screen_y(M4 - minY);
	 
	} /* track it */

     /* Now put the cursor back where we found it */

     M1 = mouse_set_cursor;
     M3 = screen_to_mouse_x(X);
     M4 = screen_to_mouse_y(Y);
     vmouse(&M1,&M2,&M3,&M4);
    }

/****************************************************************************
*                                 select_char
* Inputs:
*       coord X: Screen X-coordinate of mouse hit
*	coord Y: Screen Y-coordinate of mouse hit
* Effect: 
*       Snaps the mouse cursor to the character selection dial, then
*	tracks the mouse left or right until the button is released
****************************************************************************/

void select_char(coord X,coord Y)
    {
     short M1, M2, M3, M4;

     coord wX;		/* Snap column */
     mouse_coord minX;	/* minimum X position */
     mouse_coord minY;	/* minimum Y position */
     mouse_coord maxX;	/* maximum X position */
     mouse_coord maxY;	/* maximum Y position */
     mouse_coord cX;		/* current X position */
     mouse_coord cY;		/* current Y position */
     short VM2;		/* virtual mouse (cursor arrow simulation */
     boolean changed;	/* change flag */

     /* Snap mouse cursor to wheel position */

     wX = pattern_X + 6 + select_numeric;

     M1 = mouse_set_cursor;
     M2 = 0;
     M3 = screen_to_mouse_x(wX);
     M4 = screen_to_mouse_y(Y);
     vmouse(&M1,&M2,&M3,&M4);

     /* Now drag the lit column back and forth with the mouse */

     cX = M3;
     cY = M4;
     minY = screen_to_mouse_y(pattern_Y + 1);
     maxY = screen_to_mouse_y(pattern_Y + 6);
     minX = screen_to_mouse_x(pattern_X + 6);
     maxX = screen_to_mouse_x(pattern_X + 21);

     if(lastkey != KEYACTIVATE)
	VM2 = 0;		/* no virtual mouse */
     else
	VM2 = MOUSEACTIVATE;

     while(true)
        { /* track it */
	 lastkey = 0;
	 
	 /* read the cursor position */

	 get_keyboard_char(current_kb);
	 /* lastkey may be zero, but we need to see motion */

	 M1 = mouse_get_cursor;
	 vmouse(&M1,&M2,&M3,&M4);
	 if(lastkey != 0)
	    { /* keys */
	     if(lastkey == KEYACTIVATE2) 
		M2 = VM2 = 0;	/* release key */
	    } /* keys */
	 else
	    if(VM2!=0) 	 
	       M2 = VM2;

	 if(tracking)
	    { /* report motion */
	     trackit("num",M2,M3,M4);
	     pause(100);
	    } /* report motion */

	 if((M2 & 7) == 0) 
	    break;	/* button released */
	 
	 /* Don't let it drag outside the valid columns */

	 changed = false;

	 if(M3 < minX) 
	    { 
	     M3 = minX; 
	     changed = true;}
	 else
	 if(M3 > maxX) 
	    { 
	     M3 = maxX; 
	     changed = true;
	    }

	 if(M4 < minY) 
	    { 
	     M4 = minY; 
	     changed = true;
	    }
	 else
	 if(M4 > maxY) 
	    { 
	     M4 = maxY; 
	     changed = true;
	    }

	 /* Set the cursor in case the above code did a limit operation */

	 if(changed) 
	    { /* fix change */

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

	    } /* fix change */

	 /* Did we move at all? */

	 if(M3 == cX) 

	    continue;		/* no X-motion, continue */

	 /* We have changed columns */

	 mark_column(mouse_to_screen_x(cX-minX),false);
	 mark_column(mouse_to_screen_x(M3-minX),true);
	 cX = M3;
	 
	 select_numeric = mouse_to_screen_x(M3 - minX);
	 
	} /* track it */

     /* Now put the cursor back where we found it */

     M1 = mouse_set_cursor;
     M3 = screen_to_mouse_x(X);
     M4 = screen_to_mouse_y(Y);
     vmouse(&M1,&M2,&M3,&M4);
    }

/****************************************************************************
*                              check_pattern_hit
* Inputs:
*       coord X: X-screen coordinate of mouse hit
*	coord Y: Y-screen coordinate of mouse hit
* Effect: 
*       Flips the pattern bit if there is a hit
****************************************************************************/

void check_pattern_hit(coord X,coord Y)
    {
     /*
	See if we are anywhere in the character box.  If so, 
	we will do a move of the character wheel
     */
     if(bounded(X,Y,pattern_X+5,pattern_Y+1,pattern_X+23,pattern_Y+6))
        { /* hit in it somewhere */
	 
	 clear_off();

	 hide_mouse_cursor();

	 select_char(X,Y);

	 show_mouse_cursor();

	} /* hit in it somewhere */
     else
	/*
	   See if we are anywhere in the zone box.  If so, 
	   we will do a move of the zone wheel
	*/
     if(bounded(X,Y,pattern_X,pattern_Y+1,pattern_X+3,pattern_Y+6))
        { /* hit in it somewhere */
	 
	 clear_off();

	 hide_mouse_cursor();

	 select_zone(X,Y);

	 show_mouse_cursor();

	} /* hit in it somewhere */
     
     bcd_enter_char = select_numeric;

     switch(select_zones)
        { /* cases */
	 case 0: /* none */
	 	break;
	 case 1: /* AB */
	 	bcd_enter_char |= BA_bits;
		break;
	 case 2: /* B */
	        bcd_enter_char |= B_bits;
		break;
	 case 3: /* A */
	        bcd_enter_char |= A_bits;
		break;
	} /* cases */
    }

/****************************************************************************
*                             check_enter_switch
* Inputs:
*       coord X: screen X position of mouse hit
*	coord Y: screen Y position of mouse hit
* Result: boolean
*	true if a deposit was done
*	false if not
* Effect: 
*       If either the WM or ~WM switch has been hit, store the selected
*	character at the address indicated by the manual address switches
*
*	Update the address display (but change no registers) to show the
*	address
*
*	If the mouse right button is clicked, go into enter-plus-one mode
****************************************************************************/

boolean enter_plus_one = false;

boolean check_enter_switch(coord X,coord Y)
    {
     short addr;

     /*
	The 'enter' switch is operable only in 'alter' mode
     */

     if(current_mode != mode_alter)
        { /* can't alter unless in right mode */
	 return false;
	} /* can't alter unless in right mode */

     if(bounded(X,Y,enter_X,enter_Y+4,enter_X+3,enter_Y+6))
        { /* store WM */
	 clear_off();
	 mark_WM(true);
	 addr = get_enter_address();
	 display_address(addr);
	 if(diagnostics_on)
	    { /* log it */
	     char msg[80];
	     sprintf(msg,"Store WM: location %d, char '%c'",
	     		addr,bcd_to_ascii(bcd_enter_char));
	     log_console_event(msg);
	    } /* log it */
	 memory[addr] = bcd_enter_char | word_mark;
	 wait_for_mouse_up();
	 mark_WM(false);
	 return true;
	} /* store WM */
     else
     if(bounded(X,Y,enter_X,enter_Y,enter_X+3,enter_Y+2))
        { /* store ~WM */
	 clear_off();
	 mark_NWM(true);
	 addr = get_enter_address();
	 display_address(addr);
	 if(diagnostics_on)
	    { /* log it */
	     char msg[80];
	     sprintf(msg,"Store ~WM: location %d, char '%c'",
	     		addr,bcd_to_ascii(bcd_enter_char));
	     log_console_event(msg);
	    } /* log it */
	 memory[addr] = bcd_enter_char;
	 wait_for_mouse_up();
	 mark_NWM(false);
	 return true;
	} /* store ~WM */
     return false;
	 
    }

/****************************************************************************
*                            cancel_alter_plus_one
* Effect: 
*       alter_plus_one mode is cancelled
****************************************************************************/

#pragma check_stack(off)
void cancel_alter_plus_one()
    {
     alter_plus_one = false;
    }
#pragma check_stack(on)

/****************************************************************************
*                            check_alter_plus_one
* Effect: 
*       If the ACTIVATE2 button is pressed while dragging the cursor,
*	enter alter_plus_one mode
*	Note that this is the simulation of the the 1401 mode of
*	hit-the-start-button-while-holding-the-enter-key
****************************************************************************/

void check_alter_plus_one()
    {
     short M1, M2, M3, M4;

     if(!mousing) 
	return;
     while(true)
        { /* scan mouse */
	 M1 = mouse_get_cursor;
	 vmouse(&M1,&M2,&M3,&M4);
	 if((M2 & 7) == 0) 
	    return;	/* button up with no change */
#define PLUSONE (MOUSEACTIVATE | MOUSEACTIVATE2)
	 if((M2 & PLUSONE ) == PLUSONE)
	    { /* see if alter + 1 */
	     if(check_enter_switch(mouse_to_screen_x(M3),mouse_to_screen_y(M4)))
	        { /* hit enter */
		 alter_plus_one = true;
		 B_addr++;
		} /* hit enter */
	    } /* see if alter + 1 */
	} /* scan mouse */
    }
