/*****************************************************************************
*   Main module of "Irit" - the 3d (not only polygonal) solid modeller.      *
******************************************************************************
* Usage:								     *
*   Irit [-t] [-z]							     *
*									     *
* Written by:  Gershon Elber				Ver 3.0, Apr. 1990   *
*****************************************************************************/

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include "program.h"
#include "config.h"
#include "ctrl-brk.h"
#include "dosintr.h"
#include "inptprsg.h"
#include "iritprsr.h"
#include "objects.h"
#include "windows.h"
#include "iritgrap.h"
#include "irit_soc.h"
#ifdef __WINNT__
#include <direct.h>
#endif /* __WINNT__ */

#ifdef NO_CONCAT_STR
static char *VersionStr = "Irit		Version 4.0, Gershon Elber,\n\
	(C) Copyright 1989/90/91/92/93 Gershon Elber, Non commercial use only.";
#else
static char *VersionStr = "Irit	        " VERSION
	",	Gershon Elber,	" __DATE__ ",   " __TIME__ "\n"
	COPYRIGHT ", Non commercial use only.";
#endif /* NO_CONCAT_STR */

static char *CtrlStr = "Irit [-t] [-z] [file.irt]";

char *GlblHelpFileName = "irit.hlp";

static char
     *GlblPrgmHeader = "                Irit - the not only polygonal solid modeller";
static char
     *GlblCopyRight  =
#ifdef DJGCC
     COPYRIGHT ",  DJGCC " VERSION ",   " __DATE__;
#else
#ifdef OS2GCC
     COPYRIGHT ",  OS2   " VERSION ",   " __DATE__;
#else
#ifdef AMIGA
     COPYRIGHT ", Amiga  " VERSION ",   " __DATE__;
#else
#ifdef NO_CONCAT_STR
     "(C) Copyright 1989/90/91/92 Gershon Elber,  Unix  Version 4.0";
#else
     COPYRIGHT ",  Unix  " VERSION;
#endif /* NO_CONCAT_STR */
#endif /* AMIGA */
#endif /* OS2GCC */
#endif /* DJGCC */
static char
    *GlblAuthorName = "                         Written by Gershon Elber";

IPObjectStruct
    *GlblObjList = NULL;		   /* All objects defined on system. */

char GlblCrntWorkingDir[LINE_LEN];     /* Save start CWD to recover on exit. */

FILE
    *GlblLogFile = NULL;

jmp_buf GlblLongJumpBuffer;		          /* Used in error recovery. */

int
#ifdef DJGCC					   /* Defaults for intr_lib. */
    GlblWindowFrameWidth = 8,
    GlblViewFrameColor   = INTR_COLOR_RED,
    GlblViewBackColor    = INTR_COLOR_BLACK,
    GlblTransFrameColor  = INTR_COLOR_GREEN,
    GlblTransBackColor   = INTR_COLOR_BLACK,
    GlblStatusFrameColor = INTR_COLOR_MAGENTA,
    GlblStatusBackColor  = INTR_COLOR_BLACK,
    GlblInputFrameColor  = INTR_COLOR_YELLOW,
    GlblInputBackColor   = INTR_COLOR_BLACK,
    GlblDrawHeader       = FALSE,
    GlblSmoothTextScroll = TRUE,
    GlblIntrSaveMethod   = INTR_SAVE_DISK,
    GlblMouseSensitivity = 10,       /* Sensitivity control of mouse device. */
    GlblJoystickExists   = FALSE,
#endif /* DJGCC */
    GlblLoadColor        = DEFAULT_LOAD_COLOR,
    GlblBoolColor        = DEFAULT_BOOL_COLOR,
    GlblICrvColor        = DEFAULT_ICRV_COLOR,
    GlblPrimColor        = DEFAULT_PRIM_COLOR,
    GlblDoGraphics       = TRUE,/* Control if running in graphics/text mode. */
    GlblFatalError       = FALSE, /* True if disaster in system - must quit! */
    GlblPrintLogFile     = FALSE,    /* If TRUE everything goes to log file. */
    GlblPointLenAux      = IG_POINT_DEFAULT_LENGTH;

RealType
    GlblPointLen = 0.02;		       /* Scaler for point if drawn. */

char
#ifdef DJGCC					   /* Defaults for intr_lib. */
    *GlblViewWndwPos   = "0.02, 0.02, 0.72, 0.66",
    *GlblTransWndwPos  = "0.75, 0.02, 0.98, 0.66",
    *GlblStatusWndwPos = "0.75, 0.02, 0.98, 0.66",
    *GlblInputWndwPos  = "0.04, 0.7,  0.98, 0.98",
    *GlblIntrSaveDisk  = "c:\\",
#endif /* DJGCC */
    *GlblStartFileName = "",
    *GlblLogFileName = "",
#ifdef DOUBLE
    *GlblFloatFormat = "%-8.6lg";
#else
    *GlblFloatFormat = "%-8.6g";
#endif /* DOUBLE */

static ConfigStruct SetUp[] =
{
#ifdef DJGCC
  { "Joystick",		(VoidPtr) &GlblJoystickExists,	SU_BOOLEAN_TYPE },
  { "MouseSensitivity",	(VoidPtr) &GlblMouseSensitivity,SU_INTEGER_TYPE },
  { "WndwWidth",	(VoidPtr) &GlblWindowFrameWidth,SU_INTEGER_TYPE },
  { "WndwHeader",	(VoidPtr) &GlblDrawHeader,	SU_BOOLEAN_TYPE },
  { "WndwSmthTxtScrl",	(VoidPtr) &GlblSmoothTextScroll,SU_BOOLEAN_TYPE },
  { "WndwViewClr",	(VoidPtr) &GlblViewFrameColor,	SU_INTEGER_TYPE },
  { "WndwTransClr",	(VoidPtr) &GlblTransFrameColor,	SU_INTEGER_TYPE },
  { "WndwInputClr",	(VoidPtr) &GlblInputFrameColor,	SU_INTEGER_TYPE },
  { "WndwViewPos",	(VoidPtr) &GlblViewWndwPos,	SU_STRING_TYPE },
  { "WndwTransPos",	(VoidPtr) &GlblStatusWndwPos,	SU_STRING_TYPE },
  { "WndwInputPos",	(VoidPtr) &GlblInputWndwPos,	SU_STRING_TYPE },
  { "WndwBackSave",	(VoidPtr) &GlblIntrSaveMethod,	SU_INTEGER_TYPE },
  { "WndwBackSavePath",	(VoidPtr) &GlblIntrSaveDisk,	SU_STRING_TYPE },
#endif /* DJGCC */
  { "DoGraphics",	(VoidPtr) &GlblDoGraphics,	SU_BOOLEAN_TYPE },
  { "PointLength",	(VoidPtr) &GlblPointLenAux,	SU_INTEGER_TYPE },
  { "LoadColor",	(VoidPtr) &GlblLoadColor,	SU_INTEGER_TYPE },
  { "BoolColor",	(VoidPtr) &GlblBoolColor,	SU_INTEGER_TYPE },
  { "ICrvColor",	(VoidPtr) &GlblICrvColor,	SU_INTEGER_TYPE },
  { "PrimColor",	(VoidPtr) &GlblPrimColor,	SU_INTEGER_TYPE },
  { "StartFile",	(VoidPtr) &GlblStartFileName,	SU_STRING_TYPE },
  { "LogFile",		(VoidPtr) &GlblLogFileName,	SU_STRING_TYPE },
  { "FloatFrmt",	(VoidPtr) &GlblFloatFormat,	SU_STRING_TYPE },
};
#define NUM_SET_UP	(sizeof(SetUp) / sizeof(ConfigStruct))

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 *FullPathStartFileName;

    IritCPUTime(TRUE);					 /* Reset the clock. */

    Config("irit", SetUp, NUM_SET_UP);       /* Read config. file if exists. */

    while (argc >= 2) {
    	if (strncmp(argv[1], "-z", 2) == 0) {
	    fprintf(stderr, "\n%s\n\nUsage: %s\n", VersionStr, CtrlStr);
	    ConfigPrint(SetUp, NUM_SET_UP);
	    exit(0);
    	}
	else if (strncmp(argv[1], "-t", 2) == 0) {
	    GlblDoGraphics = FALSE;
	}
	else {
	    break;
	}
    	argv++;
	argc--;
    }

    getcwd(GlblCrntWorkingDir, LINE_LEN - 1);

    SetUpCtrlBrk();	     /* Set up control break trap routine (int 1bh). */
    signal(SIGFPE, DefaultFPEHandler);	 /* Will trap floating point errors. */

    CagdSetFloatFormat(GlblFloatFormat);
    IritPrsrSetFloatFormat(GlblFloatFormat);
    IritPrsrSetFlattenObjects(FALSE);
    IritPrsrSetPolyListCirc(TRUE);

    if (GlblDoGraphics) {
#	ifdef DJGCC
	    GGSetStatusInputWindows(FALSE, TRUE);
	    GGInitGraph(0, FALSE);
#	else
	    SocServerCreateSocket();
#       endif /* DJGCC */
    }

    /* Print some copyright messages: */
    WndwInputWindowPutStr(GlblPrgmHeader);
    WndwInputWindowPutStr(GlblCopyRight);
    WndwInputWindowPutStr(GlblAuthorName);

    SetUpPredefObjects();		  /* Prepare the predefined objects. */

    /* Execute the file specified in the command line if was one: */
    if (argc == 2)
	FileInclude(argv[1]);

    /* Execute the start up file first by inserting it to the include stack. */
    if ((int) strlen(GlblStartFileName) > 0 &&
	(FullPathStartFileName = searchpath(GlblStartFileName)) != NULL)
	FileInclude(FullPathStartFileName);

    Interact();				      /* Go and do some real work... */

    IritExit(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(GlblLongJumpBuffer)) {         /* Used in error recovery. */
	case 0:
	case 1:
	    while (TRUE) {
		if (!InputParser())		 /* Print the error message. */
		    PrintInptPrsrError();
	    }
	case 2:
	    WndwInputWindowPutStr("Press return to die...");
	    getchar();
	    break;
    }
}

/*****************************************************************************
* Routine to query (and print) the errors found in InputParser:		     *
*****************************************************************************/
static void PrintInptPrsrError(void)
{
    InptPrsrEvalErrType ErrorNum;
    char *ErrorMsg, *p;
    char Line[LINE_LEN_LONG];

    if ((ErrorNum = InptPrsrParseError(&ErrorMsg)) != IPE_NO_ERR) {/*Prsr err*/
	sprintf(Line, "Parsing Error: ");
	p = &Line[strlen(Line)];
	switch (ErrorNum) {
	    case IP_ERR_WRONG_SYNTAX:
		sprintf(p, "Wrong syntax\n");
		break;
	    case IP_ERR_PARAM_EXPECT:
		sprintf(p, "Parameter Expected\n");
		break;
	    case IP_ERR_ONE_OPERAND:
	    case IP_ERR_TWO_OPERAND:
		sprintf(p, "Wrong # of operands - %s\n", ErrorMsg);
		break;
	    case IP_ERR_STACK_OV:
		sprintf(p, "Internal Stack OverFlow at - %s\n", ErrorMsg);
		break;
	    case IP_ERR_PARAM_MATCH:
		sprintf(p, "Parenthesis mismatch - %s\n", ErrorMsg);
		break;
	    case IP_ERR_UNDEF_TOKEN:
		sprintf(p, "Undefined token - %s\n", ErrorMsg);
		break;
	    case IP_ERR_UNDEF_FUNC:
		sprintf(p, "Undefined function - %s\n", ErrorMsg);
		break;
	    case IP_ERR_NAME_TOO_LONG:
		sprintf(p, "Object name too long - %s\n", ErrorMsg);
		break;
	    case IP_ERR_PARAM_FUNC:
		sprintf(p, "Parameters expected in func %s\n", ErrorMsg);
		break;
	    case IP_ERR_NO_PARAM_FUNC:
		sprintf(p, "No Parameters expected in func %s\n", ErrorMsg);
		break;
	    case IP_ERR_STR_TOO_LONG:
		sprintf(p, "String too long - %s\n", ErrorMsg);
		break;
	    default:
		sprintf(p, "Undefined error %d", ErrorNum);
		break;
	}
	WndwInputWindowPutStr(Line);
	return;
    }

    if ((ErrorNum = InptPrsrEvalError(&ErrorMsg)) != IPE_NO_ERR) {/*Eval err.*/
	sprintf(Line, "Eval Error: ");
	p = &Line[strlen(Line)];
	switch (ErrorNum) {
	    case IE_ERR_FATAL_ERROR:
		sprintf(p, "Fatal error - %s\n", ErrorMsg);
		break;
	    case IE_ERR_DIV_BY_ZERO:
		sprintf(p, "Division by zero - %s\n", ErrorMsg);
		break;
	    case IE_ERR_NO_OBJ_METHOD:
		sprintf(p, "No such method for object - %s\n", ErrorMsg);
		break;
	    case IE_ERR_TYPE_MISMATCH:
		sprintf(p, "Parameter type mismatch - %s\n",
			ErrorMsg);
		break;
	    case IE_ERR_ASSIGN_LEFT_OP:
		sprintf(p, "Lval is not a parameter - %s\n", ErrorMsg);
		break;
	    case IE_ERR_MIXED_OBJ:
		sprintf(p, "Mixed types in expression - %s\n", ErrorMsg);
		break;
	    case IE_ERR_IP_OBJ_UNDEFINED:
		sprintf(p, "No such object defined - %s\n", ErrorMsg);
		break;
	    case IE_ERR_NO_ASSIGNMENT:
		sprintf(p, "Assignment was expected\n");
		break;
	    case IE_ERR_FP_ERROR:
		sprintf(p, "Floating Point Error - %s\n", ErrorMsg);
		break;
	    case IE_ERR_NUM_PRM_MISMATCH:
		sprintf(p, "Number of func. param. mismatch - %s\n", ErrorMsg);
		break;
	    case IE_ERR_MAT_POWER:
		sprintf(p, "Wrong range or not exists, operator - %s\n", ErrorMsg);
		break;
	    case IE_ERR_FREE_SIMPLE:
		sprintf(p, "Free only geometric objects - %s\n", ErrorMsg);
		break;
	    case IE_ERR_MODIF_ITER_VAR:
		sprintf(p, "Iteration var. type modified or freed - %s\n", ErrorMsg);
		break;
	    case IE_ERR_BOOLEAN_ERR:
		sprintf(p, "Geometric Boolean operation error - %s\n", ErrorMsg);
		break;
	    case IE_ERR_OUT_OF_RANGE:
		sprintf(p, "Out of range.\n");
		break;
	    case IE_ERR_DATA_PRSR_ERROR:
		sprintf(p, "%s", ErrorMsg);
		break;
	    case IE_ERR_USER_FUNC_NO_RETVAL:
		sprintf(p, "User defined function \"%s\" has no returned value\n",
			ErrorMsg);
		break;
	    case IE_ERR_INCOMPARABLE_TYPES:
		sprintf(p, "Incomparable object types found");
		break;
	    case IE_ERR_ONLYEQUALITY_TEST:
		sprintf(p, "Only equality or non equality test is valid for these objects");
		break;
	    case IE_ERR_IF_HAS_NO_COND:
		sprintf(p, "Condition of if clause is illegal");
		break;
	    case IE_ERR_IP_USERFUNC_DUP_VAR:
		sprintf(p, "Duplicated variable, %s", ErrorMsg);
		break;
	    default:
		sprintf(p, "Undefined error %d\n", ErrorNum);
		break;
	}
	WndwInputWindowPutStr(Line);
	return;
    }
}

/*****************************************************************************
* Exit routine with a return code of 0.					     *
*****************************************************************************/
void IritExit0(void)
{
    IritExit(0);
}

/*****************************************************************************
* Exit routine - called when this program should terminate.		     *
*****************************************************************************/
void IritExit(int ExitCode)
{
    if (GlblDoGraphics) {
    	WndwViewExit();
    }

    chdir(GlblCrntWorkingDir);	  /* Recover original directory before exit. */

    if (GlblPrintLogFile)
	fclose(GlblLogFile);		      /* Close log file if was open. */

    exit(ExitCode);
}

/*****************************************************************************
* Trap Cagd_lib errors right here.					     *
*****************************************************************************/
void CagdFatalError(CagdFatalErrorType ErrID)
{
    char Line[LINE_LEN],
	*ErrorMsg = CagdDescribeError(ErrID);

    sprintf(Line, "CAGD_LIB: %s", ErrorMsg);
    WndwInputWindowPutStr(Line);

    longjmp(GlblLongJumpBuffer, 1);		/* Go back to menu directly. */
}

/*****************************************************************************
* FatalEror routine. print a fatal error message and go back to cursor mode  *
*****************************************************************************/
void IritFatalError(char *ErrorMsg)
{
    if (ErrorMsg != NULL) {
	WndwInputWindowPutStr("Fatal error occured, please report it:");
	WndwInputWindowPutStr(ErrorMsg);
    }

    longjmp(GlblLongJumpBuffer, 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 Type)
{
    char Line[LINE_LEN];

    sprintf(Line, "Floating point error %d.", Type);
    WndwInputWindowPutStr(Line);

    longjmp(GlblLongJumpBuffer, 1);
}

#define DEBUG
#ifdef DEBUG
void DummyLinkCagdDebug(void)
{
    CagdDbg(NULL);
}
#endif /* DEBUG */
