/*****************************************************************************
*   "Irit" - the 3d polygonal solid modeller.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
*   Module to handle overloaded operators for the input parser module.	     *
* Note this module should be accessed by Input Parser only (InptPrsr.c).     *
*****************************************************************************/

#ifdef __MSDOS__
#include <alloc.h>
#endif /* __MSDOS__ */

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include "program.h"
#include "objects.h"
#include "allocatg.h"
#include "booleang.h"
#include "inptprsg.h"
#include "inptprsl.h"
#include "overload.h"
#include "geomat3d.h"

/* The following table help to decide if the operand are legal for the given */
/* operator. 5 entries for PLUS, MINUS, MULT, DIV, POWER. Each entry is a    */
/* square matrix of number of object types by number of object types:	     */
/* GEOMETRIC_EXPR NUMERIC_EXPR VECTOR_EXPR MATRIX_EXPR (num. from 1 to 4).   */

static ByteType OverLoadDiadicTable[5][4][4] =
/*	GEOMETRIC_EXPR	NUMERIC_EXPR	VECTOR_EXPR	MATRIX_EXPR	*/
{ { {	GEOMETRIC_EXPR,	0,		0,		0 },	/* PLUS */
    {	0,		NUMERIC_EXPR,	0,		0 },
    {	0,		0,		VECTOR_EXPR,	0 },
    {	0,		0,		0,		MATRIX_EXPR } },

  { {	GEOMETRIC_EXPR,	0,		0,		0 },	/* MINUS */
    {	0,		NUMERIC_EXPR,	0,		0 },
    {	0,		0,		VECTOR_EXPR,	0 },
    {	0,		0,		0,		MATRIX_EXPR } },

  { {	GEOMETRIC_EXPR,	0,		0,		GEOMETRIC_EXPR }, /*MULT */
    {	0,		NUMERIC_EXPR,	VECTOR_EXPR,	MATRIX_EXPR },
    {	0,		VECTOR_EXPR,	NUMERIC_EXPR,	VECTOR_EXPR },
    {	GEOMETRIC_EXPR,	MATRIX_EXPR,	VECTOR_EXPR,	MATRIX_EXPR } },

  { {	0,		0,		0,		0 },	/* DIV */
    {	0,		NUMERIC_EXPR,	0,		0 },
    {	0,		0,		0,		0 },
    {	0,		0,		0,		0 } },

  { {	0,		0,		0,		0 },	/* POWER */
    {	0,		NUMERIC_EXPR,	0,		MATRIX_EXPR },
    {	0,		0,		0,		0 },
    {	0,		MATRIX_EXPR,	0,		0 } }
};

/* The following table help to decide if the operand are legal for the given */
/* operator. 1 entry for UNARMINUS. Each entry is a linear vector of length  */
/* of number of object types:						     */
/* GEOMETRIC_EXPR NUMERIC_EXPR VECTOR_EXPR MATRIX_EXPR (num. from 1 to 4).   */

static ByteType OverLoadMonadicTable[1][NUM_OF_OBJECT_TYPES] =
/*	GEOMETRIC_EXPR	NUMERIC_EXPR	VECTOR_EXPR	MATRIX_EXPR	*/
{	GEOMETRIC_EXPR,	NUMERIC_EXPR,	VECTOR_EXPR,	MATRIX_EXPR	};
								/* UNARMINUS */

/*****************************************************************************
*   Routine to perform type checking on the overloaded operators. Returns    *
* TRUE if legal, and set Result to returned object type.		     *
*****************************************************************************/
int OverLoadTypeCheck(int Operator, int Right, int Left, int *Result)
{
    if (!(Right == NUMERIC_OBJ ||
	  Right == VECTOR_OBJ ||
	  Right == MATRIX_OBJ ||
	  Right == GEOMETRIC_OBJ) &&
	 (Left  == NUMERIC_OBJ ||
	  Left  == VECTOR_OBJ ||
	  Left  == MATRIX_OBJ ||
	  Left  == GEOMETRIC_OBJ)) return FALSE;

    switch(Operator) {
	case PLUS:
	case MINUS:
	case MULT:
	case DIV:
	case POWER:
	    *Result = OverLoadDiadicTable[Operator - OPERATORS_OFFSET]
							[Right - 1][Left - 1];
	    return *Result != 0;

	case UNARMINUS:
	    *Result = OverLoadMonadicTable[0][Right - 1];
	    return *Result != 0;
	default:
	    FatalError("OverLoadTypeCheck: undefined operator\n");
    }
    return FALSE;				    /* Makes warning silent. */
}

/*****************************************************************************
*   Routine to evaluate	an monadic or diadic expression. It is assumed the   *
* two operands are valid for the given expression - a test which can be made *
* using OverLoadTypeCheck routine (see above). returned pointer to node with *
* the result, NULL in case of error (should not happen actually).	     *
* Root holds the simple sub-expression (Root has the operator). PObj holds   *
* all the currently defined objects available in the system. IError & CError *
* will be set to relevant error if was one.				     *
*****************************************************************************/
struct ParseTree *OverLoadEvalOper(ParseTree *Root, ParseTree *TempR,
				ParseTree *TempL, int *IError, char *CError)
{
    int i;
    RealType R;
    struct ParseTree *Temp;
    struct ParseTree *RetVal = Root;

    switch(Root->NodeKind) {		      /* Dies if undefined operator. */
	case PLUS:
	case MINUS:
	case MULT:
	case DIV:
	case POWER:
	    if (TempR == NULL || TempL == NULL) return NULL;	   /* Error! */
	    break;

	case UNARMINUS:
	    if (TempR == NULL) return NULL;			   /* Error! */
	    break;

	default:
	    FatalError("OverLoadEvalOper: Undefined operator\n");
    }

    if (Root -> NodeKind != UNARMINUS &&
	TempR -> ObjType > TempL -> ObjType) {	    /* Make TempL be bigger! */
	Temp = TempR;
	TempR = TempL;
	TempL = Temp;
    }

    switch(Root->NodeKind) {
	case PLUS:
	    if (IS_NUM_NODE(TempR) && IS_NUM_NODE(TempL)) {
		Root->ObjType = NUMERIC_OBJ;
		Root->U.R = TempL->U.R + TempR->U.R;
	    }
	    else if (IS_VEC_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root->ObjType = VECTOR_OBJ;
		Root->U.PObj = AllocObject("", VECTOR_OBJ, NULL);
		PT_ADD(Root->U.PObj->U.Vec,
			TempL->U.PObj->U.Vec, TempR->U.PObj->U.Vec);
	    }
	    else if (IS_MAT_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root->ObjType = MATRIX_OBJ;
		Root->U.PObj = AllocObject("", MATRIX_OBJ, NULL);
                MatAddTwo4by4(Root->U.PObj->U.Mat,
			TempL->U.PObj->U.Mat, TempR->U.PObj->U.Mat);
	    }
	    else if (IS_GEOM_NODE(TempR) && IS_GEOM_NODE(TempL)) {
		Root->ObjType = GEOMETRIC_OBJ;
		Root->U.PObj = BooleanOR(TempL -> U.PObj, TempR -> U.PObj);
		if (Root->U.PObj == NULL) {
		    IPGlblEvalError = IE_ERR_BooleanErr;
		    UpdateCharError("Operator ", PLUS);
		    RetVal = NULL;
		}
	    }
	    else RetVal = NULL;
	    break;
	case MINUS:
	    if (IS_NUM_NODE(TempR) && IS_NUM_NODE(TempL)) {
		Root->ObjType = NUMERIC_OBJ;
		Root->U.R = TempL->U.R - TempR->U.R;
	    }
	    else if (IS_VEC_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root->ObjType = VECTOR_OBJ;
		Root->U.PObj = AllocObject("", VECTOR_OBJ, NULL);
		PT_SUB(Root->U.PObj->U.Vec,
			TempL->U.PObj->U.Vec, TempR->U.PObj->U.Vec);
	    }
	    else if (IS_MAT_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root->ObjType = MATRIX_OBJ;
		Root->U.PObj = AllocObject("", MATRIX_OBJ, NULL);
                MatSubTwo4by4(Root->U.PObj->U.Mat,
			TempL->U.PObj->U.Mat, TempR->U.PObj->U.Mat);
	    }
	    else if (IS_GEOM_NODE(TempR) && IS_GEOM_NODE(TempL)) {
		Root->ObjType = GEOMETRIC_OBJ;
		Root->U.PObj = BooleanSUB(TempL -> U.PObj, TempR -> U.PObj);
		if (Root->U.PObj == NULL) {
		    IPGlblEvalError = IE_ERR_BooleanErr;
		    UpdateCharError("Operator ", MINUS);
		    RetVal = NULL;
		}
	    }
	    else RetVal = NULL;
	    break;
	case MULT:
	    if (IS_NUM_NODE(TempR) && IS_NUM_NODE(TempL)) {
		Root->ObjType = NUMERIC_OBJ;
		Root->U.R = TempL->U.R * TempR->U.R;
	    }
	    else if (IS_VEC_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root->ObjType = NUMERIC_OBJ;
		Root->U.R = DOT_PROD(TempL->U.PObj->U.Vec,
				     TempR->U.PObj->U.Vec);
	    }
	    else if (IS_MAT_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root->ObjType = MATRIX_OBJ;
		Root->U.PObj = AllocObject("", MATRIX_OBJ, NULL);
                MatMultTwo4by4(Root->U.PObj->U.Mat,
			TempL->U.PObj->U.Mat, TempR->U.PObj->U.Mat);
	    }
	    else if (IS_NUM_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root->ObjType = VECTOR_OBJ;
		Root->U.PObj = AllocObject("", VECTOR_OBJ, NULL);
		PT_COPY(Root->U.PObj->U.Vec, TempL->U.PObj->U.Vec);
		PT_SCALE(Root->U.PObj->U.Vec, TempR->U.R);
	    }
	    else if (IS_NUM_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root->ObjType = MATRIX_OBJ;
		Root->U.PObj = AllocObject("", MATRIX_OBJ, NULL);
		MatScale4by4(Root->U.PObj->U.Mat,
					TempL->U.PObj->U.Mat, &TempR->U.R);
	    }
	    else if (IS_GEOM_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root->ObjType = GEOMETRIC_OBJ;
		Root->U.PObj = CopyObject(NULL, TempR->U.PObj, FALSE);
		TransformObject(Root->U.PObj, TempL->U.PObj->U.Mat);
	    }
	    else if (IS_VEC_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root->ObjType = VECTOR_OBJ;
		Root->U.PObj = AllocObject("", VECTOR_OBJ, NULL);
		MatMultVecby4by4(Root->U.PObj->U.Vec,
				 TempR->U.PObj->U.Vec, TempL->U.PObj->U.Mat);
	    }
	    else if (IS_GEOM_NODE(TempR) && IS_GEOM_NODE(TempL)) {
		Root->ObjType = GEOMETRIC_OBJ;
		Root->U.PObj = BooleanAND(TempL -> U.PObj, TempR -> U.PObj);
		if (Root->U.PObj == NULL) {
		    IPGlblEvalError = IE_ERR_BooleanErr;
		    UpdateCharError("Operator ", MULT);
		    RetVal = NULL;
		}
	    }
	    else RetVal = NULL;
	    break;
	case DIV:
	    if (IS_NUM_NODE(TempR) && IS_NUM_NODE(TempL)) {  /* Numeric div. */
		Root->ObjType = NUMERIC_OBJ;
		if (Root->Right->U.R != 0.0) {
		    Root->U.R = TempL->U.R / TempR->U.R;
		}
		else {
		    *IError = IE_ERR_DivByZero;
		    strcpy(CError, "/");
		    RetVal = NULL;
		}
	    }
	    else RetVal = NULL;
	    break;
	case POWER:
	    if (IS_NUM_NODE(TempR) && IS_NUM_NODE(TempL)) {/* Numeric power. */
		Root->ObjType = NUMERIC_OBJ;
		Root->U.R = pow(TempL->U.R, TempR->U.R);
	    }
	    else if (IS_NUM_NODE(TempR) && IS_MAT_NODE(TempL)) {
		i = (int) TempR->U.R; /* Power MUST be integer in this case. */
		if (!APX_EQ(i, TempR->U.R) || i < -1) {
		    *IError = IE_ERR_MatPower;
		    strcpy(CError, "^");
		    RetVal = NULL;
		}
		Root->ObjType = MATRIX_OBJ;
		Root->U.PObj = AllocObject("", MATRIX_OBJ, NULL);
		if (i == -1) {		     /* Generate the inverse matrix: */
		    if (!MatInverseMatrix(TempL->U.PObj->U.Mat,
					  Root->U.PObj->U.Mat)) {
			*IError = IE_ERR_MatPower;
			strcpy(CError, "^");
			RetVal = NULL;
		    }
		}
		else {			      /* I must be positive integer. */
		    MatGenUnitMat(Root->U.PObj->U.Mat);
		    while (i--) MatMultTwo4by4(Root->U.PObj->U.Mat,
					       Root->U.PObj->U.Mat,
					       TempL->U.PObj->U.Mat);
		}
	    }
	    else RetVal = NULL;
	    break;
	case UNARMINUS:
	    if (IS_NUM_NODE(TempR)) {
		Root->ObjType = NUMERIC_OBJ;
		Root->U.R = -TempR->U.R;
	    }
	    else if (IS_VEC_NODE(TempR)) {
		Root->ObjType = VECTOR_OBJ;
		Root->U.PObj = AllocObject("", VECTOR_OBJ, NULL);
		PT_COPY(Root->U.PObj->U.Vec, TempR->U.PObj->U.Vec);
		PT_SCALE(Root->U.PObj->U.Vec, -1.0);
	    }
	    else if (IS_MAT_NODE(TempR)) {
		Root->ObjType = MATRIX_OBJ;
		Root->U.PObj = AllocObject("", MATRIX_OBJ, NULL);
		R = -1.0;
		MatScale4by4(Root->U.PObj->U.Mat, TempR->U.PObj->U.Mat, &R);
	    }
	    else if (IS_GEOM_NODE(TempR)) {
		Root->ObjType = GEOMETRIC_OBJ;
		Root->U.PObj = BooleanNEG(TempR -> U.PObj);
	    }
	    else RetVal = NULL;
	    break;
    }
    if (RetVal == NULL && *IError == 0) {      /* Put general error message: */
	*IError = IE_ERR_TypeMismatch;
	switch (Root->NodeKind) {
	    case PLUS:
		strcpy(CError, "Operator +");
		break;
	    case MINUS:
		strcpy(CError, "Operator -");
		break;
	    case MULT:
		strcpy(CError, "Operator *");
		break;
	    case DIV:
		strcpy(CError, "Operator /");
		break;
	    case POWER:
		strcpy(CError, "Operator ^");
		break;
	    case UNARMINUS:
		strcpy(CError, "Operator (unary) -");
		break;
	}
    }

    return RetVal;
}
