/*
 Copyright (c) Andrew Wuensche
 
 This file is part of DDLab (Discrete Dynamics Lab) <www.ddlab.org>
 
 DDLab is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with DDLab.  If not, see <http://www.gnu.org/licenses/>.
 */

/************** input from keyboard or mouse Sept 1995 ********************/
#include <unistd.h>
#include "edecfunc.h"
#include <stdio.h>

/*note: 3 alternative graphics files, UNIX and  WATCOM/Borlandc*/

/*-------------------- UNIX graphics -----------------------------*/
# if __UNIX__

/***************  list of functions in this file ************/
/*char event_ready(void)*/
/*char keycheck(signed int incount)*/
/*void flashing_cursor(signed int startx,signed int endx,
      signed int starty,signed int endy,
      signed int xl,signed int yt,signed int xr,signed int yb, 
                             unsigned char cursecolor)*/
/*signed int input_string(char stringlength, char stringtype)*/


/******************** check event *******************/
char event_ready(void){

      /*if (XEventsQueued(D,QueuedAfterFlush)>0){*/ 
      if (XCheckMaskEvent(D,KeyPressMask|ButtonPressMask,&Event)){
         if (Event.type == ButtonPress) {
            /*left button*/
	     if (Event.xbutton.button==1){
                button='l'; /*left mouse button pressed*/
             }
             /*right button*/
	     else if (Event.xbutton.button==3){
                 button='r'; /*right mouse button pressed*/
             }
         }          
         else if (Event.type == KeyPress) button='k';
         is[0]=keycheck(0);   
         return(1);
      }
      else return(0);

}

/*********** check key press April 93, mouse Aug 94 ****************/
char keycheck(signed int incount){
  char test[16];    
  KeySym key;
    
      memset(test,0,16);
      /*XNextEvent(D, &Event);*/
      /*XCheckWindowEvent(D,W,KeyPressMask|ButtonPressMask, &Event);*/
      
     if (button=='l'){
        test[0]=13; /*left mouse button = return*/
	button=0;
     }
     else if (button=='r'){
	if (incount==0) test[0]='q'; /*right mouse button stepback*/
	else test[0]=8;              /*               or backspace*/
	button=0;
     }      
     else if (button=='k'){
       /*keypress is put in test buf here*/
        XLookupString(&Event.xkey, test, 16, &key, NULL);
 
        if (key>=0xff51 && key<=0xff54){  /*arrow keys*/
	   if (key==0xff51) test[0]=     LeftArrow;
	   else if (key==0xff52) test[0]=UpArrow;
	   else if (key==0xff53) test[0]=RightArrow;
	   else if (key==0xff54) test[0]=DownArrow;
	}
        else if (key==0xffff) test[0]=8;   /*delete key*/
                    
	else if ((key==0x071) && ((Event.xkey.state &0xFFE3) ||
				 (Event.xkey.state & 0xFFE4))){
          /*to exit DDLab at any point*/
          /*variable XK_q in X11/keysymdef.h not working*/
          unsigned char tempcolor1;
          unsigned int temp,tempx,tempy;
   
	   tempx=getx;
	   tempy=gety; 
           tempcolor1=getcolor();      
           memory_box(1,NULL);
	   setcolor(lightred);
	   sprintf(textb[0],"Exit DDLab-q, Restart-ret:");
           moveto(0,-30);
           outtext(textb[0]);
           temp=getx; 
	   draw_box(29,Maxx/2-temp-vgax(15),Maxy/2-vgay(15),
			Maxx/2+temp+vgax(15),Maxy/2+vgay(15),1,blue);
	   draw_box(-1,Maxx/2-temp-vgax(8),Maxy/2-vgay(8),
			     Maxx/2+temp+vgax(8),Maxy/2+vgay(8),1,black);
	   moverel(CWidth,0);
	       
	   outtext(textb[0]);
	   input_string(1,0);
	   if (is[0]=='q'){
              close_graphics();
              fprintf(stderr,"\nDDLab clean exit\n");
	      exit(0);
	   }
           sprintf(textb[9],"%s","boximage[29]");
           putimage(Maxx/2-temp-vgax(15),Maxy/2-vgay(15),
                                         boximage[29],COPY_PUT,textb[9]);
           freeimage(boximage[29]);
           setcolor(tempcolor1);
           moveto(tempx,tempy);	   
        }                  
        button=0;
      }               
      return(test[0]);
}

/****** create flashing cursor - and check for mouse button press *******/ 
void flashing_cursor(signed int startx,signed int endx,
      signed int starty,signed int endy,
      signed int xl,signed int yt,signed int xr,signed int yb, 
                             unsigned char cursecolor){    
     
 int got_event=0;
 int bartop, barbottom, barleft, barright;
 unsigned long last_flash=0, thisflash;
 int cursor_visible=0;

 unsigned long starttime,starttime_m,
                endtime,endtime_m;
 long  elapsedtime_m;
 struct timeval  tp;
 struct timezone tzp;
 unsigned long  elapsedtime;

 float timesec=0;
 signed int timemin=0;
 
   /*fprintf(stderr,"\nkCursorFlash=%ld",kCursorFlash);*/

   moverel(0,vgay(4));  
   button=button_not_rel=0;
   inside_rect=0;
   if (cursecolor!=255){ /*to suppress flashing cursor*/
      /*makes cursor alternate white/green or black green*/  
      XSetFunction(D,Gc,GXxor);  /*sets drawing to negative*/

      /*Get the cursor bounds*/
      bartop = gety + yt;
      barleft = getx + xl;
      barbottom = gety + yb - 1;
      barright = getx + xr - 1;
   
      cursor_visible = 1;
      setfillstyle(SOLID_FILL,cursecolor);
      bar(barleft, bartop, barright, barbottom);
   }
 
   while (!got_event){
		
      if (endx-startx>0 && endy-starty>0){
         if (mouse_x>=startx && mouse_y>=starty
	         && mouse_x<=endx && mouse_y<=endy){
	     /*draw_prompt_box(1);
	    outtext("cursor in rectangle");*/
	    if (inside_rect==0){
	       XDefineCursor(D,W,leftCursor);
	       inside_rect=1;
	    }
	 }
	 else if (inside_rect==1){
	    XDefineCursor(D,W,rightCursor);
	    inside_rect=0;
	 }
      }

      if (XCheckWindowEvent(D,W,
	      PointerMotionMask|KeyPressMask|ButtonPressMask|
		     StructureNotifyMask | ExposureMask, &Event)){
		
         switch (Event.type){	
	    case MotionNotify:
	       mouse_x=Event.xmotion.x;
	       mouse_y=Event.xmotion.y;
	       /* show co-ordinates*/
	       /*sprintf(textb[0],"x=%d y=%d",mouse_x,mouse_y);
	       draw_hint_box(0,textb[0],500);*/
	       break;
				
	    case ButtonPress:
	       mouse_x=Event.xbutton.x;
	       mouse_y=Event.xbutton.y;
	       /*left button*/
	       if (Event.xbutton.button==1)                
		   button='l'; /*left mouse button pressed*/
					
	       /*right button*/
	       else if (Event.xbutton.button==3)
		   button='r'; /*right mouse button pressed*/
					
	       got_event = 1;
	       break;
	       
	    case KeyPress:
	       button='k';
	       got_event = 1;
	       break;
	    case Expose:
		  /*Do nothing*/
		  break;			
	    case ConfigureNotify:
	       /*first, erase the cursor if it is showing*/
	          if (cursor_visible == 1)
		      bar(barleft, bartop, barright, barbottom);
	          /*now handle the window resize*/
		  if (resize_window()==1) got_event=2;
		  break;				
	     default:
		  /*Do nothing*/
                  break;
	  }	
      }/*if got event*/
 
      if (cursecolor!=255){ /*to suppress flashing cursor*/    	
         last_flash ++; /*using just usleep() is unreliable*/

         if (last_flash == 1) {
	   if (CursorFlash_auto_reset==0){
              gettimeofday(&tp,&tzp);
              starttime=(unsigned long)tp.tv_sec; //seconds
              starttime_m=(unsigned long)tp.tv_usec;//micro-sec 1/million
              /*fprintf(stderr,"\n%lu:starttime=%lu",
	           last_flash,starttime*1000000 + starttime_m);*/    
           }        
	 }

         usleep(1);     /*but this makes cpu=0*/		
         /*Flash the damn cursor*/
         thisflash=kCursorFlash/1000;
   	 if (thisflash==0) thisflash=1000; 

         if (last_flash == thisflash){
	     /*fprintf(stderr,"\n lastflash=%lu",last_flash);*/
	     bar(barleft, bartop, barright, barbottom);
	     cursor_visible = ! cursor_visible;
	     last_flash=0;

            if (CursorFlash_auto_reset==0){
               gettimeofday(&tp,&tzp);
               endtime=(unsigned long)tp.tv_sec; //seconds
               endtime_m=(unsigned long)tp.tv_usec; //micro-sec 1/million
               /*fprintf(stderr,"\n    endtime=%lu",
		         endtime*1000000 + endtime_m);*/
               
               elapsedtime=endtime-starttime;
               elapsedtime_m=endtime_m-starttime_m;     
               if (elapsedtime_m<0){ 
                  elapsedtime-=1;
                  elapsedtime_m=(1000000-starttime_m)+endtime_m;
               }
               timesec=(float)
                 ((elapsedtime%60)+((float)elapsedtime_m/1000000));
               if (elapsedtime>=60){
                  timemin=elapsedtime/60;
                  /*fprintf(stderr,"\n%dmin %fsec",timemin,timesec);*/
               }
               /*else fprintf(stderr,"\n%fsec",timesec);*/
               /*automatically adjust flash to about .5sec*/
               kCursorFlash*=.5/(timemin*60 + timesec);
               
               defCursorFlash=kCursorFlash;
               if (kCursorFlash==0) kCursorFlash=defCursorFlash;
               /*fprintf(stderr,"\nkCursorFlash=%lu\n",kCursorFlash);*/
               starttime=starttime_m=endtime=endtime_m=0;
               last_flash=0; 
	   }
         }
      }
   } /*While loop*/
   if (cursecolor!=255){ /*to suppress flashing cursor*/ 
      if (cursor_visible==1) /*erase the cursor if it is showing*/
          bar(barleft, bartop, barright, barbottom);   
      XSetFunction(D,Gc,GXcopy); /*restore drawing to positive*/
   }
   if (got_event==2) draw_bottom_title(0);
   setfillstyle(SOLID_FILL,black);	 
   moverel(0,-vgay(4));
   inside_rect=0;
}

/************************ input data, unix rec:2023  ************************************/
signed int input_string(char stringlength, char stringtype){    
 signed int incount=0,backwidth; 
 signed int tempx,tempy;
 unsigned char cursecolor;
 char test;
 
   if (bground=='w') cursecolor=red;
   else if (bground=='b') cursecolor=lightgreen;
   setfillstyle(SOLID_FILL,cursecolor);

   if (stppause!=2) draw_bottom_title(0);

   while(incount < 1024){
      start:  
         flashing_cursor(0,0,0,0,0,-1,CWidth,1+CHeight / 2,cursecolor);  
         test=keycheck(incount);
         /*show keypress info*/
         /*fprintf(stderr,"key =%d=%c\n",test,test);*/
	 if (stringtype!='L'){ //for a long string with any isalpha or ispunct symbols
	                       //as in "Labels" for interactive graphs
            if (test=='@'||test=='<'||test=='>'){  
               tempx=getx;
               tempy=gety;
               if (test=='@') printimage();      /*printout with @ key*/
	       else if (test=='>') save_screen();/*save screen image with >key*/
	       else if (test=='<') load_screen();/*load screen image with < key*/
	       moveto(tempx,tempy);
               goto start;             
            }
            if (test=='q' && incount==0){
	       is[0]='q';
	       return(0);          /*to stepback in setup*/
            }
	 }   
         /*arrows:128+  75=left,77=right,72=up,80=down [=up3d ]=down3d*/
         if (stringtype==-2 && incount==0){         
            is[0]=test;
	    return(0);            
         }
	  /*arrows:128+  75=left,77=right,72=up,80=down*/
         if (stringtype==-1 && incount==0 &&
                 (test==LeftArrow||test==RightArrow||
		    test==UpArrow||test== DownArrow||test==8
		    || test=='[' || test==']' )){
         
            is[0]=test;
	    return(0);            
         }
         
         if (test==13){
            is[incount]=13;
            break;
	 }
         /*arrows to terminate input only if stringtype==-1*/
         if (stringtype==-1 && (test==LeftArrow||test==RightArrow||
	    test==UpArrow||test==DownArrow)){  /*arrows*/
	    is[incount]=13;
	    is[20]=test;
	    break;
         }
         /*back space*/ //use 'q' in Label -- so dont backtrack with 'q', use backspace=8
         else if ((test==8 || (test=='q' && (stringtype!='L')))  &&  incount>0){      
             if (fonttype==0) backwidth=CWidth;
             else  backwidth=XTextWidth(propfont,is+incount-1,1);
             /*is[incount-1]=0;*/
             moverel(-backwidth,0); 
	     	setfillstyle(SOLID_FILL,black);
             bar(getx,gety,getx+backwidth,gety+CHeight);
             /*XFillRectangle(D,W,Gc,getx,gety,CWidth,CHeight);*/             
             incount--; 
         }           
         else if (stringtype==1 && incount<stringlength){
             /*test for digit*/  
            if (isdigit(test) || test=='.' || test=='-' ){ 
               /*XSetForeground(D,Gc,(unsigned long)white);*/
               sprintf(textb[0],"%c", test);
               outtext(textb[0]);
               is[incount]=test;
               incount++;
	    }
         }
	 //for labels in interactive graphs, dont include backslash -- 2023
	 else if (stringtype=='L' && incount<stringlength /*&&  test!='\\'*/){ // reverse backslash is linebreak
            /* test for any printing character*/	    
            if ( isalpha(test) || isdigit(test) || ispunct(test) //any other punctuation
		   //|| test=='\' //backslash sets a line break
		     || test==32 //32=space key
		     //|| test==163 //£ sign does not work
		     //|| test==156 //£ sign does not work
		     //|| test=='£' //£ sign does not work
                     ){ 
                  /*XSetForeground(D,Gc,(unsigned long)white);*/
	          //fprintf(stderr,"\n%c %d",test,test);
                  sprintf(textb[0],"%c",test);
                  outtext(textb[0]);
                  is[incount]=test;
                  incount++;
            }
         }
	 //limit inputs for filename etc
	 else if (stringtype<1 && incount<stringlength){
	      /*fprintf(stderr," %c",test);*/ 
              /* test for letters, numbers, or limited punctuation*/ 
                  if ( isalpha(test) || isdigit(test)
		     || test=='.' || test=='-' || test=='+'
                     || test=='_' || test=='?'		     
                  ){ 
                  /*XSetForeground(D,Gc,(unsigned long)white);*/
	          //fprintf(stderr,"\n%c %d",test,test);
                  sprintf(textb[0],"%c",test);
                  outtext(textb[0]);
                  is[incount]=test;
                  incount++;
            }
         }
         XFlush(D);
  }  
  return(incount);
}
/*------------------------ end UNIX graphics --------------------*/

/*--------------- WATCOM graphics ----------------------*/
# elif __WATCOM__
/***************  list of functions in this file ************/
/*char event_ready(void)*/
/*char keycheck(signed int incount)*/
/*void flashing_cursor(signed int xl,signed int yt,
     signed int xr,signed int yb, unsigned char cursecolor)*/
/*signed int input_string(char r,char stringtype)*/

/******************** check event *******************/
char event_ready(void){

   if( (is[0]=keycheck(0))!=0) return(1);
   else return(0);
}
/*********** check key press April 93, mouse Aug 94 ****************/
char keycheck(signed int incount){
 signed int test = 0;

   if (mouse_found){
      if (button=='l'){
	 test=13; /*left mouse button = return*/
	 button=0;
	 return(test);
      }
      if (button=='r'){
	 if (incount==0) test='q'; /*right mouse button stepback*/
	 else test=8;              /*               or backspace*/
	 button=0;
	 return(test);
      }
   }
   if (kbhit()){
      test = getch();
      if(!test){
	 test = getch();
	 test = -test;   /*+ 128*/
      }
      /*sprintf(textb[0],"key=%d ",test);
      outtext(textb[0]);*/
   }
   return(test);
}

/****** create flashing cursor - and check for mose button press *******/
void flashing_cursor(signed int xl,signed int yt,
      signed int xr,signed int yb, unsigned char cursecolor){
 unsigned long imsize;
 void *curseimage,*existimage;

   if (cursecolor!=255){ /*to suppress flashing cursor*/
#     if __WATCOM__
         xl+=3;
         yt-=2;
         xr+=3;
         yb-=2;
#     endif

      /*size of cursor*/
      imsize=imagesize(getx+xl,gety+yt,getx+xr,gety+yb);
      /*image under cursor*/
      if ((existimage = malloc(imsize)) == NULL){
         draw_prompt_box(1);
         sprintf(textb[0],"memory! image, return-abort");
         getch();
         final_quit();
      }
      getimage(getx+xl,gety+yt,getx+xr,gety+yb,existimage);
      /*image of cursor*/
      setfillstyle(SOLID_FILL,cursecolor);
      bar(getx+xl,gety+yt,getx+xr,gety+yb);
      if ((curseimage = malloc(imsize)) == NULL){
         draw_prompt_box(1);
         sprintf(textb[0],"memory! image, return-abort");
         getch();
         final_quit();
      }
      getimage(getx+xl,gety+yt,getx+xr,gety+yb,curseimage);
      sprintf(textb[9],"%s","existimage");
      putimage(getx+xl,gety+yt,existimage,COPY_PUT,textb[9]);
   }

   if (mouse_found){
      button=button_not_rel=0;
      mouse_call(1,0,0,0); /*display mouse*/

      /*reduce button press count to zero*/
      mouse_call(5,0,0,0);/*left*/
      while (globbx!=0) mouse_call(5,0,0,0);
      mouse_call(5,1,0,0);/*right*/
      while (globbx!=0) mouse_call(5,1,0,0);
      /*reduce button release count to zero*/
      mouse_call(6,0,0,0);/*left*/
      while (globbx!=0) mouse_call(6,0,0,0);
      mouse_call(6,1,0,0);/*left*/
      while (globbx!=0) mouse_call(6,1,0,0);
   }

   while (!kbhit()){
      if (mouse_found){
	 mouse_call(3,0,0,0);
	 mouse_x=globcx;
	 mouse_y=globdx;
	 /* show co-ordinates
	 sprintf(textb[0],"x=%d,y=%d",mouse_x,mouse_y);
	 draw_hint_box(0,0,textb[0],vgax(500));
	 */
	 /*check left mouse button pressed= return*/
	 mouse_call(5,0,0,0);
	 if (globbx){
	    button='l';
	    break;
	 }
	 /*check right mouse button = backtrack*/
	 mouse_call(5,1,0,0);
	 if (globbx){
	    button='r';
	    break;
	 }
      }
      if (cursecolor!=255){ /*to suppress flashing cursor*/
         sprintf(textb[9],"%s","curseimage");
         putimage(getx+xl,gety+yt,curseimage,XOR_PUT,textb[9]);
         delay(kCursorFlash);
         sprintf(textb[9],"%s","existimage");
         putimage(getx+xl,gety+yt,existimage,COPY_PUT,textb[9]);
         delay(kCursorFlash);
      }
   }
   if (mouse_found){
      mouse_call(2,0,0,0);/*hide mouse, this seems to be ness to avoid 
                            patches following mouse*/
      /*erase_free_hint_box(vgax(500)); show co-ordinates*/
   }
   if (cursecolor!=255){ /*to suppress flashing cursor*/
      sprintf(textb[9],"%s","existimage");
      putimage(getx+xl,gety+yt,existimage,COPY_PUT,textb[9]);
      free(existimage);
      free(curseimage);
   }
   setfillstyle(SOLID_FILL,black);

}

/*************** input data, April 93, watcom rev:2023 **************/
signed int input_string(char r,char stringtype){ /*r=max string length*/
	       /*stringtype: alfa-numeric=0, -1 with arrows, numeric only=1*/

 unsigned char tempcolor;
 signed int incount=0, /*counts key presses at end of while*/
	      tempx,tempy;
 char test;

   tempx=getx;
   tempy=gety;
   tempcolor=getcolor();

   //if (stppause!=2) draw_bottom_title(0); /*update the bottom title*/

   while (incount<1024){
     start: 
      flashing_cursor(0,CHeight-vgay(3),
			      CWidth,CHeight,green);
      test=keycheck(incount);
      if (stringtype!='L'){ //for a long string with any isalpha or ispunct symbols
	                       //as in "Labels" for interactive graphs
         if (test=='@'||test=='<'||test=='>'){
	    tempx=getx;
	    tempy=gety;
	    if (test=='@' || test==-71){  /*printout with @ or Home key*/
	       if (!stp || !in_startbasin || !in_startfield){
		  setcolor(red);
		  rectangle(0,0,getmaxx(),getmaxy());
		  line(0,getmaxy()-vgay(13),getmaxx(),getmaxy()-vgay(13));
	       }
	       printimage('L',0,0,0,0,getmaxx(),getmaxy());
	       setcolor(tempcolor);
	       test=0;
	       moveto(tempx,tempy);
	       goto start;
	    }
	    else if (test=='>') save_screen();/*save screen image with >key*/
	    else if (test=='<') load_screen();/*load screen image with < key*/
	    moveto(tempx,tempy);
	    goto start;
         }
         if (test=='q' && incount==0){
	    is[0]='q';
	    return(0);          /*to stepback in setup*/
         }
      }
      /*arrows:128+  75=left,77=right,72=up,80=down [=up3d ]=down3d*/
         if (stringtype==-2 && incount==0){         
            is[0]=test;
	    return(0);            
      }
      /*arrows:128+  75=left,77=right,72=up,80=down*/
      if (stringtype==-1 && incount==0
	  && (test==LeftArrow||test==RightArrow||
	     test==UpArrow||test==DownArrow||test==8
                  || test=='[' || test==']')){
	 is[0]=test;
	 return(0);
      }

      /*carrage return*/
      if (test==13){
	 is[incount]=13;
	 break;
      }
      /*arrows to terminate input only if stringtype==-1*/
      if (stringtype==-1 &&(test==LeftArrow||test==RightArrow||
	 test==UpArrow||test==DownArrow)){  /*arrows*/
	 is[incount]=13;
	 is[20]=test;
	 break;
      }
      /*back space*/ //use 'q' in Label -- so dont backtrack with 'q', use backspace=8
      else if ((test==8 || (test=='q' && (stringtype!='L'))) && incount>0){   /*back space*/
	 moveto(getx-CWidth,gety);
	 bar(getx+1,gety+1,getx+CWidth,gety+CHeight-1); //this works approximately
	 incount--;
      }
      else if (stringtype==1 && incount<r) {
	 /*test for digit*/
	 if ( isdigit(test) || test=='.' || test=='-' ){
	    bar(getx+1,gety+1,getx+CWidth,gety+CHeight-1);
	    sprintf(textb[0],"%c",test);
	    outtext(textb[0]);
	    is[incount]=test;
	    incount++;
	 }
      }
      //for labels in interactive graphs, dont include backslash -- 2023
      else if (stringtype=='L' && incount<r /*&&  test!='\\'*/){ // reverse backslash is linebreak
            /* test for any printing character*/	    
            if ( isalpha(test) || isdigit(test) || ispunct(test) //any other punctuation
		   //|| test=='\' //backslash sets a line break
		     || test==32 //32=space key
		     //|| test==163 //£ sign does not work
		     //|| test==156 //£ sign does not work
		     //|| test=='£' //£ sign does not work
                     ){ 
                  /*XSetForeground(D,Gc,(unsigned long)white);*/
	          //fprintf(stderr,"\n%c %d",test,test);
                  sprintf(textb[0],"%c",test);
                  outtext(textb[0]);
                  is[incount]=test;
                  incount++;
            }
      }
      else if (stringtype<1 && incount<r){
	 /* test for any printing character*/
	 if (isprint(test)){
#           if __WATCOM__
	       bar(getx+1,gety+1,getx+CWidth,gety+CHeight-1);
#           endif
	    sprintf(textb[0],"%c",test);
	    outtext(textb[0]);
	    is[incount]=test;
	    incount++;
	 }
      }
   }
   return(incount);
}
# endif
/*----------------------- end WATCOM/Borlandc --------------------*/




