/*
	FRACTINT - The Ultimate Fractal Generator
			Main Routine
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#ifndef XFRACT
#include <io.h>
#include <dos.h>
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include <ctype.h>

#include "prototyp.h"
#include "fractype.h"
#include "helpdefs.h"

struct videoinfo videoentry;
int helpmode;

long timer_start,timer_interval;	/* timer(...) start & total */
int	adapter;		/* Video Adapter chosen from list in ...h */
char *fract_dir1="", *fract_dir2="";

#ifdef __TURBOC__

/* yes, I *know* it's supposed to be compatible with Microsoft C,
   but some of the routines need to know if the "C" code
   has been compiled with Turbo-C.  This flag is a 1 if FRACTINT.C
   (and presumably the other routines as well) has been compiled
   with Turbo-C. */
int compiled_by_turboc = 1;

/* set size to be used for overlays, a bit bigger than largest (help) */
unsigned _ovrbuffer = 54 * 64; /* that's 54k for overlays, counted in paragraphs */

#else

int compiled_by_turboc = 0;

#endif

/*
   the following variables are out here only so
   that the calcfract() and assembler routines can get at them easily
*/
	int	active_system = 0;	/* 0 for DOS, WINFRAC for Windows */
	int	dotmode;		/* video access method	    */
	int	textsafe2;		/* textsafe override from videotable */
	int	oktoprint;		/* 0 if printf() won't work */
	int	sxdots,sydots;		/* # of dots on the physical screen    */
	int	sxoffs,syoffs;		/* physical top left of logical screen */
	int	xdots, ydots;		/* # of dots on the logical screen     */
	double	dxsize, dysize; 	/* xdots-1, ydots-1	    */
	int	colors; 		/* maximum colors available */
	long	maxit;			/* try this many iterations */
	int	boxcount;		/* 0 if no zoom-box yet     */
	int	zrotate;		/* zoombox rotation	    */
	double	zbx,zby;		/* topleft of zoombox	    */
	double	zwidth,zdepth,zskew;	/* zoombox size & shape     */

	int	fractype;		/* if == 0, use Mandelbrot  */
	char	stdcalcmode;		/* '1', '2', 'g', 'b'       */
	long	creal, cimag;		/* real, imag'ry parts of C */
	long	delx, dely;		/* screen pixel increments  */
	long	delx2, dely2;		/* screen pixel increments  */
        LDBL   delxx, delyy;           /* screen pixel increments  */
        LDBL   delxx2, delyy2;         /* screen pixel increments  */
	long	delmin; 		/* for calcfrac/calcmand    */
	double	ddelmin;		/* same as a double	    */
	double	param[MAXPARAMS];	/* parameters               */
	double	potparam[3];		/* three potential parameters*/
	long	fudge;			/* 2**fudgefactor	    */
	long	l_at_rad;		/* finite attractor radius  */
	double	f_at_rad;		/* finite attractor radius  */
	int	bitshift;		/* fudgefactor		    */

	int	badconfig = 0;		/* 'fractint.cfg' ok?       */
	int	diskisactive;		/* disk-video drivers flag  */
	int	diskvideo;		/* disk-video access flag   */
	int hasinverse = 0;
	/* note that integer grid is set when integerfractal && !invert;    */
	/* otherwise the floating point grid is set; never both at once     */
	long	far *lx0, far *ly0;	/* x, y grid		    */
	long	far *lx1, far *ly1;	/* adjustment for rotate    */
	/* note that lx1 & ly1 values can overflow into sign bit; since     */
	/* they're used only to add to lx0/ly0, 2s comp straightens it out  */
	double far *dx0, far *dy0;	/* floating pt equivs */
	double far *dx1, far *dy1;
	int	integerfractal; 	/* TRUE if fractal uses integer math */

	/* usr_xxx is what the user wants, vs what we may be forced to do */
	char	usr_stdcalcmode;
	int	usr_periodicitycheck;
	int	usr_distest;
	char	usr_floatflag;

	int	viewwindow;		/* 0 for full screen, 1 for window */
	float	viewreduction;		/* window auto-sizing */
	int	viewcrop;		/* nonzero to crop default coords */
	float	finalaspectratio;	/* for view shape and rotation */
	int	viewxdots,viewydots;	/* explicit view sizing */

	HISTORY  far *history = NULL;
	int maxhistory = 10;

/* variables defined by the command line/files processor */
int	comparegif=0;			/* compare two gif files flag */
int	timedsave=0;			/* when doing a timed save */
int	resave_flag=0;			/* tells encoder not to incr filename */
int	started_resaves=0;		/* but incr on first resave */
int	save_system;			/* from and for save files */
int	tabmode = 1;			/* tab display enabled */

/* for historical reasons (before rotation):	     */
/*    top    left  corner of screen is (xxmin,yymax) */
/*    bottom left  corner of screen is (xx3rd,yy3rd) */
/*    bottom right corner of screen is (xxmax,yymin) */
double	xxmin,xxmax,yymin,yymax,xx3rd,yy3rd; /* selected screen corners  */
long	xmin, xmax, ymin, ymax, x3rd, y3rd;  /* integer equivs		 */
double	sxmin,sxmax,symin,symax,sx3rd,sy3rd; /* displayed screen corners */
double	plotmx1,plotmx2,plotmy1,plotmy2;     /* real->screen multipliers */

int calc_status; /* -1 no fractal		    */
		 /*  0 parms changed, recalc reqd   */
		 /*  1 actively calculating	    */
		 /*  2 interrupted, resumable	    */
		 /*  3 interrupted, not resumable   */
		 /*  4 completed		    */
long calctime;

int max_colors;				/* maximum palette size */
int	   zoomoff;			/* = 0 when zoom is disabled	*/
int	   savedac;			/* save-the-Video DAC flag	*/
int browsing;                 /* browse mode flag */
char file_name_stack[16][13]; /* array of file names used while browsing */
int name_stack_ptr ;
double toosmall;
int  minbox;
int no_sub_images;
int autobrowse,doublecaution;
char brwscheckparms,brwschecktype;
char browsemask[13];

#define RESTART           1
#define IMAGESTART        2
#define RESTORESTART      3
#define CONTINUE          4

static void check_samename(void)       
   {
      char drive[FILE_MAX_DRIVE];
      char dir[FILE_MAX_DIR];
      char fname[FILE_MAX_FNAME];
      char ext[FILE_MAX_EXT];
      char path[FILE_MAX_PATH];
      splitpath(savename,drive,dir,fname,ext);
      if(strcmp(fname,"fract001"))
      {
         makepath(path,drive,dir,fname,"gif");
         if(access(path,0)==0)
	 exit(0);
      }	 
   }

/* Do nothing if math error */
static void my_floating_point_err(int sig)
{
   if(sig != 0)
      overflow = 1;
}

void main(int argc, char **argv)
{
   int     resumeflag;
   int	   kbdchar;			/* keyboard key-hit value	*/
   int	   kbdmore;			/* continuation variable	*/
   char stacked=0;			/* flag to indicate screen stacked */

   /* this traps non-math library floating point errors */
   signal( SIGFPE, my_floating_point_err );

   initasmvars();			/* initialize ASM stuff */
   checkfreemem(0);
   load_videotable(1); /* load fractint.cfg, no message yet if bad */
#ifdef XFRACT
   UnixInit();
#endif
   init_help();

restart:   /* insert key re-starts here */
   autobrowse     = FALSE;
   brwschecktype  = TRUE;
   brwscheckparms = TRUE;
   doublecaution  = TRUE;
   no_sub_images = FALSE;
   toosmall = 6;
   minbox   = 3;
   strcpy(browsemask,"*.GIF");
   strcpy(browsename,"            ");
   name_stack_ptr= -1; /* init loaded files stack */
   if (fract_dir1==NULL) 
       fract_dir1 = ".";
   cmdfiles(argc,argv); 	/* process the command-line */
   dopause(0);			/* pause for error msg if not batch */
   init_msg(0,"",NULL,0);  /* this causes getakey if init_msg called on runup */
   checkfreemem(1);
   if(debugflag==450 && initbatch==1)   /* abort if savename already exits */
       check_samename();
#ifdef XFRACT
   initUnixWindow();
#endif
   memcpy(olddacbox,dacbox,256*3);	/* save in case colors= present */

   if (debugflag == 8088)		 cpu =	86; /* for testing purposes */
   if (debugflag == 2870 && fpu >= 287 ) fpu = 287; /* for testing purposes */
   if (debugflag ==  870 && fpu >=  87 ) fpu =	87; /* for testing purposes */
   if (debugflag ==   70)		 fpu =	 0; /* for testing purposes */
   if (getenv("NO87")) fpu = 0;
   fract_dir1 = getenv("FRACTDIR");
   if (fract_dir1==NULL) {
       fract_dir1 = ".";
   }
#ifdef SRCDIR
   fract_dir2 = SRCDIR;
#else
   fract_dir2 = ".";
#endif
   if (fpu == 0)       iit = 0;
   if (fpu >= 287 && debugflag != 72)	/* Fast 287 math */
      setup287code();

   /* IIT math coprocessor chip logic */
   if(iit == 0 && fpu)
      if(IITCoPro())
         iit = 3;  /* detect iit chip */
   if(iit < 0) iit = 0;  /* user wants chip turned off */
   if(iit>0)
   {
      if(F4x4Check())
         iit = 2;    /* semaphore TSR is installed */
      else if(iit == 3)
         iit = 0;    /* we detetct chip but no tsr - don't use */
      /* else user forced iit == 1 */
   }

   adapter_detect();			/* check what video is really present */
   if (debugflag >= 9002 && debugflag <= 9100) /* for testing purposes */
      if (video_type > (debugflag-9000)/2)     /* adjust the video value */
	 video_type = (debugflag-9000)/2;

   diskisactive = 0;			/* disk-video is inactive */
   diskvideo = 0;			/* disk driver is not in use */
   setvideotext();			/* switch to text mode */
   calc_status = -1;			/* no active fractal image */
   savedac = 0; 			/* don't save the VGA DAC */

#ifndef XFRACT
   if (debugflag == 10000)		/* check for free memory */
      showfreemem();

   if (badconfig < 0)			/* fractint.cfg bad, no msg yet */
      bad_fractint_cfg_msg();
#endif

   max_colors = 256;                    /* the Windows version is lower */
   max_kbdcount=(cpu==386) ? 80 : 30;   /* check the keyboard this often */

   if (showfile && initmode < 0) {
      intro();				/* display the credits screen */
      if (keypressed() == ESC) {
	 getakey();
	 goodbye();
	 }
      }

   browsing = FALSE;

   if (!functionpreloaded)
      set_if_old_bif();
   stacked = 0;
restorestart:
   if (colorpreloaded)
      memcpy(dacbox,olddacbox,256*3);	/* restore in case colors= present */

   lookatmouse = 0;			/* ignore mouse */

   while (showfile <= 0) {		/* image is to be loaded */
      char *hdg;
      tabmode = 0;
      if (!browsing )     /*RB*/
      {
      if (overlay3d) {
	 hdg = "Select File for 3D Overlay";
	 helpmode = HELP3DOVLY;
	 }
      else if (display3d) {
	 hdg = "Select File for 3D Transform";
	 helpmode = HELP3D;
	 }
      else {
	 hdg = "Select File to Restore";
	 helpmode = HELPSAVEREST;
	 }
      if (showfile < 0 && getafilename(hdg,gifmask,readname) < 0) {
	 showfile = 1;		     /* cancelled */
	 initmode = -1;
	 break;
	 }

	   name_stack_ptr = 0; /* 'r' reads first filename for browsing */
	   strcpy(file_name_stack[name_stack_ptr],browsename);
     }

      showfile = 0;
      helpmode = -1;
      tabmode = 1;
      if(stacked)
      {
         discardscreen();
         setvideotext();
         stacked = 0;
      }
      if (read_overlay() == 0)	     /* read hdr, get video mode */
	 break; 		     /* got it, exit */
      showfile = -1;		     /* retry */
      }

   helpmode = HELPMENU; 		/* now use this help mode */
   tabmode = 1;
   lookatmouse = 0;			/* ignore mouse */

   if ((overlay3d || stacked) && initmode < 0) {	/* overlay command failed */
      unstackscreen();			/* restore the graphics screen */
      stacked = 0;
      overlay3d = 0;			/* forget overlays */
      display3d = 0;			/* forget 3D */
      if (calc_status > 0 && calc_status !=2)
	 calc_status = 0;
      resumeflag = 1;
      goto resumeloop;			/* ooh, this is ugly */
      }

   savedac = 0; 			/* don't save the VGA DAC */
imagestart:				/* calc/display a new image */
   if(stacked)
   {
      discardscreen();
      stacked = 0;
   }
#ifdef XFRACT
   usr_floatflag = 1;
#endif
   got_status = -1;			/* for tab_display */

   if (showfile)
      if (calc_status > 0)		/* goto imagestart implies re-calc */
	 calc_status = 0;

   if (initbatch == 0)
      lookatmouse = -PAGE_UP;		/* just mouse left button, == pgup */

   cyclelimit = initcyclelimit; 	/* default cycle limit	 */


   adapter = initmode;			/* set the video adapter up */
   initmode = -1;			/* (once)		    */

   while (adapter < 0) {		/* cycle through instructions */
      if (initbatch) {				/* batch, nothing to do */
         initbatch = 4;			/* exit with error condition set */
         goodbye();
      }
      kbdchar = main_menu(0);
      if (kbdchar == INSERT) goto restart;	/* restart pgm on Insert Key */
      if (kbdchar == DELETE)			/* select video mode list */
	 kbdchar = select_video_mode(-1);
      if ((adapter = check_vidmode_key(0,kbdchar)) >= 0)
	 break; 				/* got a video mode now */
#ifndef XFRACT
      if ('A' <= kbdchar && kbdchar <= 'Z')
	 kbdchar = tolower(kbdchar);
#endif
      if (kbdchar == 'd') {                     /* shell to DOS */
	 setclear();
	 printf("\n\nShelling to DOS - type 'exit' to return\n\n");
	 shell_to_dos();
	 goto imagestart;
	 }

#ifndef XFRACT
      if (kbdchar == '@' || kbdchar == '2') {    /* execute commands */
#else
      if (kbdchar == F2 || kbdchar == '@') {     /* We mapped @ to F2 */
#endif
	 if ((get_commands() & 4) == 0) 
	    goto imagestart;
	 kbdchar = '3';                         /* 3d=y so fall thru '3' code */
	 }
#ifndef XFRACT
      if (kbdchar == 'r' || kbdchar == '3' || kbdchar == '#') {
#else
      if (kbdchar == 'r' || kbdchar == '3' || kbdchar == F3) {
#endif
	 display3d = 0;
	 if (kbdchar == '3' || kbdchar == '#' || kbdchar == F3)
	    display3d = 1;
         if(colorpreloaded)
            memcpy(olddacbox,dacbox,256*3);	/* save in case colors= present */
	 setvideotext(); /* switch to text mode */
	 showfile = -1;
	 goto restorestart;
	 }
      if (kbdchar == 't') {                     /* set fractal type */
         julibrot = 0;
	 get_fracttype();
	 goto imagestart;
	 }
      if (kbdchar == 'x') {                     /* generic toggle switch */
	 get_toggles();
	 goto imagestart;
	 }
      if (kbdchar == 'y') {                     /* generic toggle switch */
	 get_toggles2();
	 goto imagestart;
	 }
      if (kbdchar == 'z') {                     /* type specific parms */
	 get_fract_params(1);
	 goto imagestart;
	 }
      if (kbdchar == 'v') {                     /* view parameters */
	 get_view_params();
	 goto imagestart;
	 }
      if (kbdchar == 2) {                       /* ctrl B = browse parms*/
	 get_browse_params();
	 goto imagestart;
	 }
      if (kbdchar == 'f') {                     /* floating pt toggle */
	 if (usr_floatflag == 0)
	    usr_floatflag = 1;
	 else
	    usr_floatflag = 0;
	 goto imagestart;
	 }
      if (kbdchar == 'i') {                     /* set 3d fractal parms */
	 get_fract3d_params(); /* get the parameters */
	 goto imagestart;
	 }
      if (kbdchar == 'g') {
	 get_cmd_string(); /* get command string */
	 goto imagestart;
         }
      /* buzzer(2); */				/* unrecognized key */
      }

   zoomoff = 1; 		/* zooming is enabled */
   helpmode = HELPMAIN; 	/* now use this help mode */
   resumeflag = 0;  /* allows taking goto inside big_while_loop() */
resumeloop:   
   /* this switch processes gotos that are now inside function */
   switch(big_while_loop(&kbdmore,&stacked,resumeflag))
   {
   case RESTART:
      goto restart;
   case IMAGESTART:
      goto imagestart;
   case RESTORESTART:
      goto restorestart;
   default:
      break;   
   }
}

int check_key()
{
   int key;
   if((key = keypressed()) != 0) {
      if(key != 'o' && key != 'O') {
	 fflush(stdout);
	 return(-1);
      }
      getakey();
      if (dotmode != 11)
	 show_orbit = 1 - show_orbit;
   }
   return(0);
}

/* timer function:
     timer(0,(*fractal)())		fractal engine
     timer(1,NULL,int width)		decoder
     timer(2)				encoder
  */
#ifndef XFRACT
int timer(int timertype,int(*subrtn)(),...)
#else
int timer(va_alist)
va_dcl
#endif
{
   va_list arg_marker;	/* variable arg list */
   char *timestring;
   time_t ltime;
   FILE *fp = NULL;
   int out=0;
   int i;
   int do_bench;

#ifndef XFRACT
   va_start(arg_marker,subrtn);
#else
   int timertype;
   int (*subrtn)();
   va_start(arg_marker);
   timertype = va_arg(arg_marker, int);
   subrtn = (int (*)())va_arg(arg_marker, int *);
#endif

   do_bench = timerflag; /* record time? */
   if (timertype == 2)	 /* encoder, record time only if debug=200 */
      do_bench = (debugflag == 200);
   if(do_bench)
      fp=dir_fopen(workdir,"bench","a");
   timer_start = clock_ticks();
   switch(timertype) {
      case 0:
	 out = (*(int(*)(void))subrtn)();
	 break;
      case 1:
	 i = va_arg(arg_marker,int);
	 out = (int)decoder((short)i); /* not indirect, safer with overlays */
	 break;
      case 2:
	 out = encoder();	     /* not indirect, safer with overlays */
	 break;
      }
   /* next assumes CLK_TCK is 10^n, n>=2 */
   timer_interval = (clock_ticks() - timer_start) / (CLK_TCK/100);

   if(do_bench) {
      time(&ltime);
      timestring = ctime(&ltime);
      timestring[24] = 0; /*clobber newline in time string */
      switch(timertype) {
	 case 1:
	    fprintf(fp,"decode ");
	    break;
	 case 2:
	    fprintf(fp,"encode ");
	    break;
	 }
      fprintf(fp,"%s type=%s resolution = %dx%d maxiter=%ld",
	  timestring,
	  curfractalspecific->name,
	  xdots,
	  ydots,
	  maxit);
      fprintf(fp," time= %ld.%02ld secs\n",timer_interval/100,timer_interval%100);
      if(fp != NULL)
	 fclose(fp);
      }
   return(out);
}
