/*****************************************************************************
*           Change Log
*  Date     | Change
*-----------+-----------------------------------------------------------------
* 24-Jan-86 | [1.312] Created
* 24-Jan-86 | [1.314] Check_left_mouse_button => check_mouse_halt
* 24-Jan-86 | [1.322] Properly simulate hide/show cursor with depth count
* 25-Jan-86 | [1.332] Added 'tracking' feature
* 27-Jan-86 | [1.350] check_for_mouse_up now aborts on mouse_halt code and
*           | also if activation (lastkey) was keypad
* 23-Feb-86 | [1.369] Added mhidden, print out as part of tracking
* 25-Feb-86 | [1.379] include <> => include ""
* 30-Jul-86 | [1.401] wait_for_mouse_up polls keyboard
* 31-Jul-86 | [1.405] Made chars unsigned
* 10-Nov-91 | [1.428] <jmn> converted to Microsoft C 6.0
*  7-Dec-91 | [1.475] <jmn> use new keybindings structure
* 21-Dec-91 | [1.499] <jmn> added CTRL-LEFT, CTRL-RIGHT dispatches
* 23-Dec-91 | [1.521] <jmn> added pragmas for check_stack
* 15-Mar-92	<jrj>	Made changes to make screen stuff independent of MS C
*****************************************************************************/
#include "stdio.h"
#include "boolean.h"
/* #include "graph.h" */

#include "btypes.h"
#include "scdspmsg.h"

#include "mouse.h"
#include "keys.h"
#include "kb.h"
#include "kbd.h"
#include "scan.h"
#include "kbd.h"

extern boolean mousing;
extern short lastkey;
extern boolean tracking;

short mhidden = -1;
extern struct videoconfig video;
keybindings * current_kb = NULL;

/****************************************************************************
*                                   del_key
* Result: void
*       
* Effect: 
*       Handles DEL key
****************************************************************************/

void del_key()
    {
     lastkey = KEYACTIVATE2;
    }

/****************************************************************************
*                                   ins_key
* Result: void
*       
* Effect: 
*       Handles INS key
****************************************************************************/

void ins_key()
    {
     lastkey = KEYACTIVATE;
    }

/****************************************************************************
*                                 move_y_down
* Result: void
*       
* Effect: 
*       Moves y down
****************************************************************************/

void move_y_down()
    {
     short M1;
     short M2;
     short M3;
     short M4;

     M1 = mouse_get_cursor;

     vmouse(&M1,&M2,&M3,&M4);

     M4 += MOUSE_Y;
     if(M4 > video.numypixels) 
	M4 = video.numypixels;
     M1 = mouse_set_cursor;
     vmouse(&M1,&M2,&M3,&M4);
    }

/****************************************************************************
*                                   move_y_up
* Result: void
*       
* Effect: 
*       Moves cursor up
****************************************************************************/

void move_y_up()
    {
     short M1;
     short M2;
     short M3;
     short M4;

     M1 = mouse_get_cursor;

     vmouse(&M1,&M2,&M3,&M4);

     M4 -= MOUSE_Y;
     if(M4 < 0) 
	M4 = 0;
     M1 = mouse_set_cursor;
     vmouse(&M1,&M2,&M3,&M4);
    }

/****************************************************************************
*                                 move_x_left
* Result: void
*       
* Effect: 
*       Moves cursor left
****************************************************************************/

void move_x_left()
    {
     short M1;
     short M2;
     short M3;
     short M4;

     M1 = mouse_get_cursor;

     vmouse(&M1,&M2,&M3,&M4);

     M3 -= MOUSE_X;
     if(M3 < 0) 
	M3 = 0;
     M1 = mouse_set_cursor;
     vmouse(&M1,&M2,&M3,&M4);
    }

/****************************************************************************
*                                move_x_right
* Result: void
*       
* Effect: 
*       Moves cursor right
****************************************************************************/

void move_x_right()
    {
     short M1;
     short M2;
     short M3;
     short M4;

     M1 = mouse_get_cursor;

     vmouse(&M1,&M2,&M3,&M4);

     M3 += MOUSE_X;
     if(M3 > video.numxpixels) 
	M3 = video.numxpixels;
     M1 = mouse_set_cursor;
     vmouse(&M1,&M2,&M3,&M4);
    }

/****************************************************************************
*                                   trackit
* Inputs:
*	char * msg: Identifying position
*       short M2: M2 value
*	short M3:
*	short M4;
* Effect: 
*       Tracks the value
****************************************************************************/

void trackit(char * msg, short M2, short M3, short  M4)
    {
     coord x;
     coord y;
     short mode;
     short columns;
     short page = 0;

     if(!tracking) 
	return;


#if 0
     scmode(&mode,&columns,&page);
#endif
     sccurpos(&y, &x);
     sc_settextposition(24,0);
     printf("%.3s: M3 = %3d, M4 = %3d, M2 = %d, hid = %3d, key = \\%03o, pg = %2d",
     				msg,M3,M4,M2,mhidden,lastkey,page);
     sccurset(y, x);
    }

/****************************************************************************
*                              hide_mouse_cursor
* Effect: 
*       Hides the mouse cursor
****************************************************************************/

void hide_mouse_cursor()
    {
     short M1, M2, M3, M4;

     mhidden--;

     M1 = mouse_hide_cursor;
     vmouse(&M1,&M2,&M3,&M4);
    }

/****************************************************************************
*                              show_mouse_cursor
* Effect: 
*       Shows the mouse cursor
****************************************************************************/

void show_mouse_cursor()
    {
     short M1, M2, M3, M4;

     mhidden++;
     if(mhidden > 0) 
	mhidden = 0;

     M1 = mouse_show_cursor;

     vmouse(&M1,&M2,&M3,&M4);

    }

/****************************************************************************
*                              wait_for_mouse_up
* Effect: 
*       Waits until all mouse buttons have been released
****************************************************************************/

void wait_for_mouse_up()
    {
     short M1, M2, M3, M4;

     if(!mousing) 
	return;
     if(lastkey == KEYACTIVATE || 
        lastkey == KEYACTIVATE2 ||
	lastkey == KEYHALT) 
	    return;

     while(true) 
        {
	 M1 = mouse_get_cursor;
	 vmouse(&M1,&M2,&M3,&M4);
	 if((M2 & 7) == 0) 
	    return;
	 read_keyboard_char(current_kb);
	}
    }

/****************************************************************************
*				    vmouse
* Inputs:
*       short * M1:
*	short * M2:
*	short * M3:
*	short * M4:
* Effect: 
*       Simulates mouse if not mousing.  Virtual mouse if is mousing
****************************************************************************/

#pragma check_stack(off)

static hidden = 0;

void vmouse(short * M1,short * M2,short * M3,short *M4)
    {
     coord tx;
     coord ty;
#if debug_x
     coord rX;
     coord rY;
     coord x;
     coord y;
#endif

     if(mousing)
        { /* real */
	 real_mouse(M1,M2,M3,M4);

	 if(lastkey == KEYACTIVATE) 
	    *M2 |= MOUSEACTIVATE;
	 else
	 if(lastkey == KEYHALT) 
	    *M2 |= MOUSEHALT;
	 else
	 if(lastkey == KEYACTIVATE2) 
	    *M2 |= MOUSEACTIVATE2;

	 if(tracking && *M1 == mouse_get_cursor)
	    { /* track it */
	     trackit("vmr",*M2,*M3,*M4);
	    } /* track it */
	     
	 return;
	} /* real */

     switch(*M1)
        { /* simulate */
	 case mouse_hide_cursor:
	 			hidden--;
				sc_settextcursor(SC_CURSOR_NONE);

	 			break;
	 case mouse_show_cursor:
	 			hidden++;
				if(hidden < 0) 
				   break;
				if(hidden > 0) 
				   hidden = 0;
				sc_settextcursor(SC_CURSOR_BLOCK);
	 			break;
         case mouse_set_sensitivity:
		 break;
         case mouse_set_text_cursor:
		 break;
	 case mouse_get_cursor:
		 *M2 = 0;
		 sccurpos(&ty,&tx);
		 *M4 = ty;
		 *M3 = tx;
#if debug_x
		 rX = *M3;
		 rY = *M4;
		 sccurset(0,0);
		 printf("get: *M4 = %2d, *M3 = %2d",*M4,*M3);
#endif
		 *M4 = screen_to_mouse_y(*M4);
		 *M3 = screen_to_mouse_x(*M3);
#if debug_x
		 printf("; s_t_m(*M4) = %2d, s_t_m(*M3) = %2d",*M4, *M3);
		 sccurset(rY,rX);
#endif
		 if(lastkey == KEYACTIVATE) 
		    *M2 |= MOUSEACTIVATE;
		 else
		 if(lastkey == KEYHALT) 
		    *M2 |= MOUSEHALT;
		 else
		 if(lastkey == KEYACTIVATE2) 
		    *M2 |= MOUSEACTIVATE2;
		 break;
	case mouse_set_cursor:
#if debug_x
		sccurset(1,0);
		printf("set: *M4 = %2d, *M3 = %2d",*M4,*M3);
		printf("; m_t_s(*M4) = %2d, m_t_s(*M3) = %2d",
				mouse_to_screen_y(*M4), 
				mouse_to_screen_x(*M3));
#endif
		sccurset(mouse_to_screen_y(*M4), mouse_to_screen_x(*M3));
#if debug_x
		sccurpos(&y, &x);
		rX = x;
		rY = y;
		if(*M4/8 != rY || *M3/8 != rX)
		   printf("\7\7");
#endif
				
		break;
	} /* simulate */
     if(tracking && (*M1 == mouse_get_cursor || *M1 == mouse_set_cursor))
	{ /* track it */
	 trackit("vms",*M2,*M3,*M4);
	} /* track it */

    }
#pragma check_stack(on)


/****************************************************************************
*			       check_mouse_halt
* Result: boolean
*       true if mouse button "halt" is pushed.  Does not return until
*	button is released.
****************************************************************************/

#pragma check_stack(off)
boolean check_mouse_halt()
    {
     short M1, M2, M3, M4;

     M1 = mouse_get_cursor;
     vmouse(&M1,&M2,&M3,&M4);

     if((M2 & 7) == MOUSEHALT) 
        { /* wait for release */
	 printf("\7");	/* tell user we saw it */
	 wait_for_mouse_up();
	 return true;
	} /* wait for release */
     return false;
    }
#pragma check_stack(on)


/****************************************************************************
*                                    dokey
* Inputs:
*       unsigned char key: Function key scan code
* Effect: 
*       Does the operation required by the key
****************************************************************************/

#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define HOME 71
#define PGUP 73
#define PGDN 81
#define INS 82
#define DEL 83
#define HOME 71
#define END 79
#define CTRL_LEFT 115
#define CTRL_RIGHT 116

void dokey(unsigned char key,keybindings * kb)
    {
     hide_mouse_cursor();

     if(kb != NULL)
        { /* decode kb */
	 switch(key)
	    { /* operations */
	     case UP: 
		    if(kb->uparrow)
		       (*kb->uparrow)();
		    break;
	     case DOWN: 
		     if(kb->downarrow)
			(*kb->downarrow)();
		    break;
	     case LEFT: 
		     if(kb->leftarrow)
			(*kb->leftarrow)();
		    break;
	     case RIGHT: 
		     if(kb->rightarrow)
			(*kb->rightarrow)();
		    break;
	     case INS:
		     if(kb->ins)
			(*kb->ins)();
		    break;
	     case DEL:
		     if(kb->del)
			(*kb->del)();
		     break;
	     case PGUP:
		     if(kb->pgup)
			(*kb->pgup)();
		     break;
	     case PGDN:
		     if(kb->pgdown)
			(*kb->pgdown)();
		     break;
	     case HOME:
		     if(kb->home)
			(*kb->home)();
	     case END:
		     if(kb->end)
			(*kb->end)();
	     case CTRL_LEFT:
		     if(kb->ctrl_leftarrow)
			(*kb->ctrl_leftarrow)();
		     break;
	     case CTRL_RIGHT:
		     if(kb->ctrl_rightarrow)
			(*kb->ctrl_rightarrow)();
		     break;
	    } /* operations */
	} /* decode kb */

     show_mouse_cursor();

    }

/****************************************************************************
*                                    scan
* Inputs:
*	boolean * done: Boolean to tell if done
*	keybindings * kb: Key bindings
*	boolean forcehalt: Forces halt after single scan if true
* Effect: 
*       Scans input.  Handles keyboard or mouse interface.  Calls procedures
*	for mouse button hits
*	Sets current_kb to input parameter so that subcomponents will use
*	the current keybinding
****************************************************************************/

void scan(boolean * done, keybindings * kb, boolean forcehalt)
    {
     short M1, M2, M3, M4;
     boolean activate_down;
     boolean activate2_down;
     boolean extra_down;
     boolean restore;
     short rX;
     short rY;
     keybindings * old_kb = current_kb;

     activate_down = false;
     activate2_down = false;
     extra_down = false;
     restore = false;
     current_kb = kb;

     while(!*done)
        { /* scan it */
	 *done = forcehalt;
	 lastkey = 0;
	 get_keyboard_char(kb);
	 if(!mousing && lastkey == 0) 
	    continue;
	 M1 = mouse_get_cursor;
	 vmouse(&M1,&M2,&M3,&M4);
	 rX = M3;
	 rY = M4;
	 if(check_mouse_halt())
	    { /* halt */
	     *done = true;
	     continue;
	    } /* halt */

	 if(M2 & MOUSEEXTRA)
	    { /* extra button */
	     if(!extra_down)
	        { /* extra button */
		 if(kb->mouse_middle)
		    (*kb->mouse_middle)(M3,M4);
		 if(!mousing) 
		    extra_down = true;
		 restore = true;
		} /* extra button */
	    } /* extra button */

	 if(M2 & MOUSEACTIVATE2)
	    { /* activate2 button */
	     if(!activate2_down)
	        { /* 2 down */
		 if(kb->mouse_right)
		    (*kb->mouse_right)(M3,M4);
		 if(mousing) 
		    activate2_down = true;
		 restore = true;
		} /* 2 down */
	    } /* activate2 button */

	 if(M2 & MOUSEACTIVATE)
	    { /* activate button */
	     if(!activate_down)
	        {
		 if(kb->mouse_left)
		    (*kb->mouse_left)(M3,M4);
		 if(mousing) 
		    activate_down = true;
		 restore = true;
		}
	    } /* activate button */	     

	 /* --- WATCH FOR BUTTON RELEASE --- */

         if(!(M2 & MOUSEACTIVATE))
	    { /* activate button release */
	     activate_down = false;
	    } /* activate button release */

         if(!(M2 & MOUSEACTIVATE2))
	    { /* activate2 button release */
	     activate2_down = false;
	    } /* activate2 button release */

         if(!(M2 & MOUSEEXTRA))
	    { /* extra button release */
	     extra_down = false;
	    } /* extra button release */

#if 0
	 if(restore)
	    { /* restore cursor */
	     
	     /* The following odd sequence is required because if
		the mouse hardware cursor is used various screen
		updates will mess its position up; we get the
		mouse's image of where the cursor is and the set it
		back, thus forcing the cursor into correspondence with
		where it is thought to be
	     */

	     hide_mouse_cursor();
	     M1 = mouse_set_cursor;
	     vmouse(&M1,&M2,&rX,&rY); /* return cursor to original pos */
	     show_mouse_cursor();
	     restore = false;
	    } /* restore cursor */
#endif
	} /* scan it */
     current_kb = old_kb;
    }

/****************************************************************************
*                                 liven_mouse
* Effect: 
*       Attempts to reactivate mouse in a new color display page
****************************************************************************/

void liven_mouse()
    {
     short M1,M2,M3,M4;

     M1 = mouse_get_cursor;
     vmouse(&M1,&M2,&M3,&M4);
     M1 = mouse_set_cursor;
     vmouse(&M1,&M2,&M3,&M4);

     M1 = mouse_set_text_cursor;
     M2 = mouse_software_cursor;
     M3 = 0x0000;
     M4 = 0x0FB0;

     vmouse(&M1,&M2,&M3,&M4);

    }
