/* Surface
 * (C) Copyright 1995 by Ashton Mason (amason@cs.uct.ac.za)
 *
 * Permission to use, modify, copy and distribute this source code for
 * any purpose and without fee is granted, provided that this copyright
 * notice appears in all copies and supporting documentation, and that
 * credit is given where due. This source code is provided "as is" with
 * no express or implied warranty.
 */

#include "headers.h"


	// constructor


expr::expr(void)
{
  for (int n = 0; n < MAXSUBS; n++)
  {
    sub[n] = NULL;
  }

  refs = 0;
  uses = 0;

  operation = NOP;
}


	// deconstructor


expr::~expr(void)
{
	// should delete some nodes and stuff here :-)
}


	// evaluate expression subtree


value expr::evaluate()
{
  value selector, result;

  if (refs && uses)		// check for valid common subexpression
  {
    uses++;			// mark use of previous value
    if (uses > refs)		// if all refs done, reset uses
    {
      uses = 0;
    }

    return val;			// return previous value
  }

  switch (operation)
  {
    case INTER:
      result = vars.getInter();
      break;

    case NORMAL:
      result = vars.getNormal();
      break;

    case GRADIENT:
      result = vars.getGradient();
      break;

    case TEXTURE:
      selector = sub[0]->evaluate();
      if (selector.type != v1d)
      {
	error("single value expected as argument 1 of 'texture'");
      }
      result = sub[1]->evaluate();
      if (result.type != v3d)
      {
	error("vector expected as argument 2 of 'texture'");
      }
      result.value3d = maps.textureMap((int)selector.value1d, result.value3d);
      break;

    case PALETTE:
      selector = sub[0]->evaluate();
      if (selector.type != v1d)
      {
	error("single value expected as argument 1 of 'palette'");
      }
      result = sub[1]->evaluate();
      if (result.type == v3d)
      {
	result.value3d = maps.paletteMap((int)selector.value1d, result.value3d);
	break;
      }
      result.type = v3d;
      result.value3d = maps.paletteMap((int)selector.value1d, result.value1d);
      break;

    case ADD:
      result = sub[0]->evaluate() + sub[1]->evaluate();
      break;

    case SUB:
      result = sub[0]->evaluate() - sub[1]->evaluate();
      break;

    case MULT:
      result = sub[0]->evaluate() * sub[1]->evaluate();
      break;

    case DIV:
      result = sub[0]->evaluate() / sub[1]->evaluate();
      break;

    case CONST:
      result = val;
      break;

    case XCOORD:
      result = xcoord(sub[0]->evaluate());
      break;

    case YCOORD:
      result = ycoord(sub[0]->evaluate());
      break;

    case ZCOORD:
      result = zcoord(sub[0]->evaluate());
      break;

    case CREATE:
      result = create(sub[0]->evaluate(), sub[1]->evaluate(), sub[2]->evaluate());
      break;

    case IF:
      if (trueval(sub[0]->evaluate()))
	result = sub[1]->evaluate();
      else
	result = sub[2]->evaluate();
      break;

    case AND:
      result = sub[0]->evaluate() && sub[1]->evaluate();
      break;

    case OR:
      result = sub[0]->evaluate() || sub[1]->evaluate();
      break;

    case NOT:
      result = !sub[0]->evaluate();
      break;

    case EQUAL:
      result = (sub[0]->evaluate() == sub[1]->evaluate());
      break;

    case NOT_EQUAL:
      result = (sub[0]->evaluate() != sub[1]->evaluate());
      break;

    case L_THAN:
      result = (sub[0]->evaluate() < sub[1]->evaluate());
      break;

    case G_THAN:
      result = (sub[0]->evaluate() > sub[1]->evaluate());
      break;

    case LT_EQUAL:
      result = (sub[0]->evaluate() <= sub[1]->evaluate());
      break;

    case GT_EQUAL:
      result = (sub[0]->evaluate() >= sub[1]->evaluate());
      break;

    case UNIT:
      result = unitval(sub[0]->evaluate());
      break;

    case SQUARE:
      result = squareval(sub[0]->evaluate());
      break;

    case SQRT:
      result = sqrtval(sub[0]->evaluate());
      break;

    case LENGTH:
      result = lengthval(sub[0]->evaluate());
      break;

    case MAX:
      result = maxval(sub[0]->evaluate(), sub[1]->evaluate());
      break;

    case MIN:
      result = minval(sub[0]->evaluate(), sub[1]->evaluate());
      break;

    case NOISE:
      result = vars.getNoise();
      break;

    case DNOISE:
      result = vars.getDNoise();
      break;

    case SIN:
      result = sinval(sub[0]->evaluate());
      break;

    case COS:
      result = cosval(sub[0]->evaluate());
      break;

    case REM:
      result = rem(sub[0]->evaluate(), sub[1]->evaluate());
      break;

    case LOG:
      result = logval(sub[0]->evaluate());
      break;

    case POW:
      result = powval(sub[0]->evaluate(), sub[1]->evaluate());
      break;

    case DOT:
      result = dotval(sub[0]->evaluate(), sub[1]->evaluate());
      break;

    case CROSS:
      result = crossval(sub[0]->evaluate(), sub[1]->evaluate());
      break;

    case RANDOM:
      result = vars.getRandom();
      break;

    case FLOOR:
      result = floorval(sub[0]->evaluate());
      break;

    case CEIL:
      result = ceilval(sub[0]->evaluate());
      break;

    case ABS:
      result = absval(sub[0]->evaluate());
      break;

    case TAN:
      result = tanval(sub[0]->evaluate());
      break;

    case ASIN:
      result = asinval(sub[0]->evaluate());
      break;

    case ACOS:
      result = acosval(sub[0]->evaluate());
      break;

    case ATAN:
      result = atanval(sub[0]->evaluate());
      break;

    case PIE:
      result.type = v1d;
      result.value1d = M_PI;
      break;

    default:
      printf("opcode: %d\n", operation);
      error("unrecognised opcode - program error");
  }

  if (refs)			// check for common subexpression
  {
    val = result;		// save value for next use
    uses = 1;			// mark first use of value
  }

  return result;
}


	// add a subexpression


int expr::addSubexpression(int number, expr *subexpression)
{
  if (sub[number])
  {
    error("subexpression already exists - program error");
    return 0;
  }

  sub[number] = subexpression;
  return 1;
}


	// destroy an expression


int expr::destroy()
{
  if (refs)
  {
    error("attempt to delete referenced node - program error");
  }

	// for each child

  for (int child = 0; child < MAXSUBS; child++)
  {
    if (sub[child])			// if child exists
    {
      sub[child]->destroy();		// tell it to destroy its children
      delete sub[child];		// destroy it
    }
  }

  return 1;
}



