/*****************************************************************************
*   Main module of "Irit" - the 3d polygonal solid modeller.		     *
******************************************************************************
* Usage:								     *
*   Irit [-z]								     *
*									     *
* Written by:  Gershon Elber				Ver 2.1, Apr. 1990   *
*****************************************************************************/

#ifdef __MSDOS__
#include <stdlib.h>
#include <graphics.h>
#include <conio.h>
#include <dir.h>
#include <dos.h>
#endif /* __MSDOS__ */

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include "program.h"
#include "dataprsg.h"
#include "inptprsg.h"
#include "ctrl-brk.h"
#include "config.h"
#include "matherr.h"
#include "windowsg.h"
#include "objects.h"
#include "dosintrg.h"

#ifdef __MSDOS__
#include "mousedrv.h"
#else
#include "xgraphic.h"
#endif /* __MSDOS__ */

#include "graphgng.h"

#ifdef __MSDOS__
static char *VersionStr = "Irit		IBMPC " VERSION
		"	Gershon Elber,	" __DATE__ ",   " __TIME__ "\n"
		"(C) Copyright 1989 Gershon Elber, Non commercial use only.";
#else
static char *VersionStr = "Irit		Unix Vertion 2.1, Gershon Elber,\n\
		(C) Copyright 1989 Gershon Elber, Non commercial use only.";
#endif /* __MSDOS__ */

static char *CtrlStr = "Irit [-z] [file.irt]";

char *HelpFileName = "irit.hlp";

char *PrgmHeader = "Irit - the polygonal solid modeller";
#ifdef __MSDOS__
char *CopyRight  = "(C) Copyright 1989 Gershon Elber,  IBMPC " VERSION ",   "
		__DATE__;
#else
char *CopyRight  = "(C) Copyright 1989 Gershon Elber,  Unix  Version 2.1";
#endif /* __MSDOS__ */
char *AuthorName = "Written by Gershon Elber";

struct ObjectStruct *GlblObjList = NULL;   /* All objects defined on system. */

char CurrentWorkingDir[LINE_LEN];      /* Save start CWD to recover on exit. */

int  GlblFatalError = FALSE;	  /* True if disaster in system - must quit! */
int  PrintLogFile = FALSE;	     /* If TRUE everything goes to log file. */

FILE *LogFile = NULL;

jmp_buf LongJumpBuffer;			          /* Used in error recovery. */

/* The following are setable variables (via configuration file poly3d-h.cfg).*/
#ifdef __MSDOS__
int  MouseExists = FALSE,
     GraphDriver = DETECT;
#else
int  MouseExists = TRUE;
#endif /* __MSDOS__ */

int  DelayConstant = DELAY_CONST_DEFAULT,
     LoadColor = DEFAULT_LOAD_COLOR,
     BoolColor = DEFAULT_BOOL_COLOR,
     ICrvColor = DEFAULT_ICRV_COLOR,
     PrimColor = DEFAULT_PRIM_COLOR;
char *StartFileName = "",
     *LogFileName = "",
     *EditPrgm = "";

static ConfigStruct SetUp[] =
{
#ifdef __MSDOS__
  { "Mouse",		(VoidPtr) &MouseExists,		SU_BOOLEAN_TYPE },
  { "GraphDriver",	(VoidPtr) &GraphDriver,		SU_INTEGER_TYPE },
#endif /* __MSDOS__ */
  { "DelayConst",	(VoidPtr) &DelayConstant,	SU_INTEGER_TYPE },
  { "LoadColor",	(VoidPtr) &LoadColor,		SU_INTEGER_TYPE },
  { "BoolColor",	(VoidPtr) &BoolColor,		SU_INTEGER_TYPE },
  { "ICrvColor",	(VoidPtr) &ICrvColor,		SU_INTEGER_TYPE },
  { "PrimColor",	(VoidPtr) &PrimColor,		SU_INTEGER_TYPE },
  { "StartFile",	(VoidPtr) &StartFileName,	SU_STRING_TYPE },
  { "LogFile",		(VoidPtr) &LogFileName,		SU_STRING_TYPE },
  { "EditPrgm",		(VoidPtr) &EditPrgm,		SU_STRING_TYPE },
};
#define NUM_SET_UP	(sizeof(SetUp) / sizeof(ConfigStruct))

#ifdef __MSDOS__
extern unsigned int _stklen = 16384;	     /* Increase default stack size. */
#endif /* __MSDOS__ */

static void Interact(void);
static void PrintInptPrsrError(void);

/*****************************************************************************
* Main routine - Read Parameter	line and do what you need...		     *
*****************************************************************************/
void main(int argc, char **argv)
{
    char *ErrorMsg, *FullPathStartFileName;

    getcwd(CurrentWorkingDir, LINE_LEN-1);

    SetUpCtrlBrk();	     /* Set up control break trap routine (int 1bh). */
    SetUpHardErr();	    /* Set up hardware error trap routine (int 24h). */
    signal(SIGFPE, DefaultFPEHandler);	 /* Will trap floating point errors. */

#ifdef __MSDOS__
    MouseExists = MouseDetect();	   /* Auto test for mouse existance. */
#endif /* __MSDOS__ */

    Config("irit", SetUp, NUM_SET_UP);       /* Read config. file if exists. */

    if (argc == 2 &&
	(strncmp(argv[1], "-z", 2) == 0 || strncmp(argv[1], "-Z", 2) == 0)) {
	argv++;
	argc--;
	fprintf(stderr, "\n%s\n\nUsage: %s\n", VersionStr, CtrlStr);
	ConfigPrint(SetUp, NUM_SET_UP);
	MyExit(0);
    }

    /* Execute the file specified in the command line if was one: */
    if (argc == 2) FileInclude(argv[1]);

#ifdef __MSDOS__
    GGInitGraph(); 			     /* Initiate the graphic driver. */
#else
    GGInitGraph(argc, argv);
#endif /* __MSDOS__ */

    WndwSetUpAllWndws();			      /* Set up the windows. */
    SetUpPredefObjects();		  /* Prepare the predefined objects. */

    /* Execute the start up file first by inserting it to the include stack. */
    if (strlen(StartFileName) > 0 &&
	(FullPathStartFileName = searchpath(StartFileName)) != NULL)
	FileInclude(FullPathStartFileName);

#ifdef __MSDOS__
    if (MouseExists)
	/* Must be called before any usage! */
	if ((ErrorMsg = MouseInit()) != 0) {
	    WndwInputWindowPutStr(ErrorMsg, RED);
	    MouseExists = FALSE;
	    getch();
	}

    DosGetTime(1.0);				    /* Reset the time count. */
#endif /* __MSDOS__ */

    AliasReset();

    Interact();				      /* Go and do some real work... */

    MyExit(0);
}

/*****************************************************************************
* Interact - the main read/eval/print routine. This routine reads data from  *
* standart input and execute it for ever (using Input Parser).		     *
* Note exit from this program is controled by input parser itself.	     *
*****************************************************************************/
static void Interact(void)
{
    /* setjmp return 0 on first install time. Will return 1 if error is      */
    /* recoverable, 2 if cannt continue - must quit the program now.	     */
    switch (setjmp(LongJumpBuffer)) {	          /* Used in error recovery. */
	case 0:
	case 1:
	    while (TRUE) {
		if (!InputParser())		 /* Print the error message. */
		    PrintInptPrsrError();
	    }
	    break;
	case 2:
	    WndwInputWindowPutStr("Press return to die...", RED);
#ifdef __MSDOS__
	    while (kbhit()) getch();		/* Flush any old keystrokes. */
	    getch();
#else
	    getchar();
#endif /* __MSDOS__ */
	    break;
    }
}

/*****************************************************************************
* Routine to query (and print) the errors found in InputParser:		     *
*****************************************************************************/
static void PrintInptPrsrError(void)
{
    int ErrorNum;
    char *ErrorMsg;
    char Line[LINE_LEN_LONG];

    if ((ErrorNum = InptPrsrParseError(&ErrorMsg)) != 0) {/* Error in parse. */
	sprintf(Line, "Parsing Error: ");
	switch(ErrorNum) {
	    case IP_ERR_WrongSyntax:
		sprintf(&Line[strlen(Line)], "Wrong syntax\n");
		break;
	    case IP_ERR_ParamExpect:
		sprintf(&Line[strlen(Line)], "Parameter Expected\n");
		break;
	    case IP_ERR_OneOperand:
	    case IP_ERR_TwoOperand:
		sprintf(&Line[strlen(Line)],
				"Wrong # of operands - %s\n", ErrorMsg);
		break;
	    case IP_ERR_StackOV:
		sprintf(&Line[strlen(Line)],
				"Internal Stack OverFlow at - %s\n", ErrorMsg);
		break;
	    case IP_ERR_ParaMatch:
		sprintf(&Line[strlen(Line)],
				"Parenthesis mismatch - %s\n", ErrorMsg);
		break;
	    case IP_ERR_UndefToken:
		sprintf(&Line[strlen(Line)],
				"Undefined token - %s\n", ErrorMsg);
		break;
	    case IP_ERR_UndefFunc:
		sprintf(&Line[strlen(Line)],
				"Undefined function - %s\n", ErrorMsg);
		break;
	    case IP_ERR_NameTooLong:
		sprintf(&Line[strlen(Line)],
				"Object name too long - %s\n", ErrorMsg);
		break;
	    case IP_ERR_ParamFunc:
		sprintf(&Line[strlen(Line)],
				"Parameters expected in func %s\n", ErrorMsg);
		break;
	    case IP_ERR_NoParamFunc:
		sprintf(&Line[strlen(Line)],
			"No Parameters expected in func %s\n", ErrorMsg);
		break;
	}
        WndwInputWindowPutStr(Line, RED);
	return;
    }

    if ((ErrorNum = InptPrsrEvalError(&ErrorMsg)) != 0) {  /* Error in eval. */
	sprintf(Line, "Eval Error: ");
	switch(ErrorNum) {
	    case IE_ERR_FatalError:
		sprintf(&Line[strlen(Line)], "Fatal Error - %s\n", ErrorMsg);
		break;
	    case IE_ERR_DivByZero:
		sprintf(&Line[strlen(Line)],
					"Division by zero - %s\n", ErrorMsg);
		break;
	    case IE_ERR_NoObjMethod:
		sprintf(&Line[strlen(Line)],
				"No such method for object - %s\n", ErrorMsg);
		break;
	    case IE_ERR_TypeMismatch:
		sprintf(&Line[strlen(Line)],
				"Parameter type mismatch - %s\n", ErrorMsg);
		break;
	    case IE_ERR_AssignLeftOp:
		sprintf(&Line[strlen(Line)],
				"Lval is not a parameter - %s\n", ErrorMsg);
		break;
	    case IE_ERR_MixedObj:
		sprintf(&Line[strlen(Line)],
				"Mixed types in expression - %s\n", ErrorMsg);
		break;
	    case IE_ERR_UndefObject:
		sprintf(&Line[strlen(Line)],
				"No such object defined - %s\n", ErrorMsg);
		break;
	    case IE_ERR_NoAssignment:
		sprintf(&Line[strlen(Line)],
				"Assignment was expected\n");
		break;
	    case IE_ERR_FPError:
		sprintf(&Line[strlen(Line)],
				"Floating Point Error - %s\n", ErrorMsg);
		break;
	    case IE_ERR_NumPrmMismatch:
		sprintf(&Line[strlen(Line)],
			"Number of func. param. mismatch - %s\n", ErrorMsg);
		break;
	    case IE_ERR_MatPower:
		sprintf(&Line[strlen(Line)],
		     "Wrong range or not exists, operator - %s\n", ErrorMsg);
		break;
	    case IE_ERR_FreeSimple:
		sprintf(&Line[strlen(Line)],
			"Free only Geometric Objects - %s\n", ErrorMsg);
		break;
	    case IE_ERR_ModifIterVar:
		sprintf(&Line[strlen(Line)],
			"Iteration var. type modified or freed - %s\n", ErrorMsg);
		break;
	    case IE_ERR_BooleanErr:
		sprintf(&Line[strlen(Line)],
			"Geometric Boolean operation error - %s\n", ErrorMsg);
		break;
	    case IE_ERR_ListTooLong:
		sprintf(&Line[strlen(Line)],
			"List length too big - up to %d only.\n", MAX_OBJ_LIST);
		break;
	    case IE_ERR_NonParamInList:
		sprintf(&Line[strlen(Line)],
			"List element not an object (expression!?)\n");
		break;
	    case IE_ERR_FreeNoRefObj:
		sprintf(&Line[strlen(Line)],
			"Cannt free referenced (by others!) object %s\n", ErrorMsg);
		break;
	    case IE_ERR_DataPrsrError:
		sprintf(&Line[strlen(Line)], "%s", ErrorMsg);
		break;
	}
        WndwInputWindowPutStr(Line, RED);
	return;
    }
}

/*****************************************************************************
* Exit routine - called when this program should terminate.		     *
*****************************************************************************/
void MyExit(int ExitCode)
{
    GGCloseGraph();				/* Close the graphic driver. */
#ifdef __MSDOS__
    MouseClose();				/* Disable mouse interrupts. */
#endif /* __MSDOS__ */
    RestoreCtrlBrk();			      /* Restore ctrl-brk interrupt. */

    chdir(CurrentWorkingDir);	  /* Recover original directory before exit. */
#ifdef __MSDOS__
    setdisk(CurrentWorkingDir[0] - 'A');	    /* Move to the old disk. */
#endif /* __MSDOS__ */

    if (PrintLogFile) fclose(LogFile);    /* Close log file if one was open. */

    exit(ExitCode);
}

/*****************************************************************************
* FatalEror routine. print a fatal error message and go back to cursor mode  *
*****************************************************************************/
void FatalError(char *ErrorMsg)
{
    if (ErrorMsg != NULL) {
#ifdef __MSDOS__
	WndwInputWindowPutStr("Fatal error occured, please report it:", RED);
	WndwInputWindowPutStr(ErrorMsg, RED);
#else
	fprintf(stderr, "%s\n%s\n",
	    "Fatal error occured, please report it:", ErrorMsg);
#endif /* __MSDOS__ */
    }

    longjmp(LongJumpBuffer, 1);			/* Go back to menu directly. */
}

/*****************************************************************************
*   Routine that is called from the floating point package in case of fatal  *
* floating point error. Print error message, long jump to main loop. Default *
* FPE handler - must be reset after redirected to other module.		     *
*****************************************************************************/
void DefaultFPEHandler(int Sig, int Type, int *RegList)
{
    PrintFPError(Type);		     /* Print the floating point error type. */

    longjmp(LongJumpBuffer, 1);
}
