//
//  Vrml 97 library
//  Copyright (C) 1998 Chris Morley
//
//  %W% %G%
//  VrmlNodeAppearance.cpp
//

#include "VrmlNodeAppearance.h"
#include "VrmlNodeType.h"

#include "Viewer.h"
#include "VrmlNodeMaterial.h"
#include "VrmlNodeTexture.h"

static VrmlNode *creator( VrmlScene *scene ) 
{ return new VrmlNodeAppearance(scene); }


// Define the built in VrmlNodeType:: "Appearance" fields

VrmlNodeType *VrmlNodeAppearance::defineType(VrmlNodeType *t)
{
  static VrmlNodeType *st = 0;

  if (! t)
    {
      if (st) return st;		// Only define the type once.
      t = st = new VrmlNodeType("Appearance", creator);
    }

  VrmlNodeChild::defineType(t);	// Parent class
  t->addExposedField("material", VrmlField::SFNODE);
  t->addExposedField("texture", VrmlField::SFNODE);
  t->addExposedField("textureTransform", VrmlField::SFNODE);

  return t;
}

VrmlNodeType *VrmlNodeAppearance::nodeType() const { return defineType(0); }


VrmlNodeAppearance::VrmlNodeAppearance(VrmlScene *scene) :
  VrmlNodeChild(scene)
{
}

VrmlNodeAppearance::~VrmlNodeAppearance()
{
}

VrmlNode *VrmlNodeAppearance::cloneMe() const
{
  return new VrmlNodeAppearance(*this);
}

void VrmlNodeAppearance::cloneChildren(VrmlNamespace *ns)
{
  // Replace references with clones
  if (d_material.get())
    d_material.set(d_material.get()->clone(ns));
  if (d_texture.get())
    d_texture.set(d_texture.get()->clone(ns));
  if (d_textureTransform.get())
    d_textureTransform.set(d_textureTransform.get()->clone(ns));
}


VrmlNodeAppearance* VrmlNodeAppearance::toAppearance() const
{
  return (VrmlNodeAppearance*) this;
}


bool VrmlNodeAppearance::isModified() const
{
  return ( d_modified ||
	   (d_material.get() && d_material.get()->isModified()) ||
	   (d_texture.get() && d_texture.get()->isModified()) ||
	   (d_textureTransform.get() &&
	    d_textureTransform.get()->isModified()) );
}

void VrmlNodeAppearance::clearFlags()
{
  VrmlNode::clearFlags();
  if (d_material.get()) d_material.get()->clearFlags();
  if (d_texture.get()) d_texture.get()->clearFlags();
  if (d_textureTransform.get()) d_textureTransform.get()->clearFlags();
}

void VrmlNodeAppearance::addToScene( VrmlScene *s, const char *rel )
{
  d_scene = s;
  if (d_material.get()) d_material.get()->addToScene(s, rel);
  if (d_texture.get()) d_texture.get()->addToScene(s, rel);
  if (d_textureTransform.get())
    d_textureTransform.get()->addToScene(s,rel);
}

// Copy the routes to nodes in the given namespace.

void VrmlNodeAppearance::copyRoutes( VrmlNamespace *ns ) const
{
  VrmlNode::copyRoutes(ns);  // Copy my routes

  // Copy subnode routes
  if (d_material.get()) d_material.get()->copyRoutes(ns);
  if (d_texture.get()) d_texture.get()->copyRoutes(ns);
  if (d_textureTransform.get())
    d_textureTransform.get()->copyRoutes(ns);
}


ostream& VrmlNodeAppearance::printFields(ostream& os, int indent)
{
  if (d_material.get()) PRINT_FIELD(material);
  if (d_texture.get()) PRINT_FIELD(texture);
  if (d_textureTransform.get()) PRINT_FIELD(textureTransform);

  return os;
}


void VrmlNodeAppearance::render(Viewer *viewer)
{
  VrmlNodeMaterial *m = d_material.get() ? d_material.get()->toMaterial() : 0;
  VrmlNodeTexture *t = d_texture.get() ? d_texture.get()->toTexture() : 0;

  if (m)
    {
      float trans = m->transparency();
      float *diff = m->diffuseColor();
      float diffuse[3] = { diff[0], diff[1], diff[2] };
      int nTexComponents = t ? t->nComponents() : 0;
      if (nTexComponents == 2 || nTexComponents == 4)
	trans = 0.0;
      if (nTexComponents >= 3)
	diffuse[0] = diffuse[1] = diffuse[2] = 1.0;

      viewer->setMaterial(m->ambientIntensity(),
			  diffuse,
			  m->emissiveColor(),
			  m->shininess(),
			  m->specularColor(),
			  trans);

      m->clearModified();
    }
  else
    {
      viewer->setColor(1.0, 1.0, 1.0); // default color
      viewer->enableLighting(false);   // turn lighting off for this object
    }

  if (t)
    {
      if (d_textureTransform.get())
	d_textureTransform.get()->render(viewer);
      else
	viewer->setTextureTransform( 0, 0, 0, 0 );

      t->render(viewer);
    }

  clearModified();
}

// Set the value of one of the node fields.

void VrmlNodeAppearance::setField(const char *fieldName,
				  const VrmlField &fieldValue)
{
  if TRY_SFNODE_FIELD(material, Material)
  else if TRY_SFNODE_FIELD(texture, Texture)
  else if TRY_SFNODE_FIELD(textureTransform, TextureTransform)
  else
    VrmlNodeChild::setField(fieldName, fieldValue);
}

