//<copyright>
//
// Copyright (c) 1996,97
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
//
// This file is part of VRweb.
//
// VRweb is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// VRweb is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with VRweb; see the file LICENCE. If not, write to the
// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
// Note that the GNU General Public License does not permit incorporating
// the Software into proprietary or commercial programs. Such usage
// requires a separate license from IICM.
//
//</copyright>

//<file>
//
// Name:        polygon.C
//
// Purpose:     Interface to class Face
//
// Created:      8 Feb 1996  Georg Meszaros
//
// Modified:    11 Jun 1996  Georg Meszaros 
//
// Modified:     7 Jan 97    Michael Pichler
//
// Description:
//
// $Id: polygon.C,v 1.5 1997/02/25 17:03:58 mpichler Exp $
//
//</file>


//<class>
//
// Name:    Face
//
// Purpose: Everything that belongs to a face.
//
//
// Public Interface:
//
//
//
// Description:
//
//
//
//
//</class>

#include "polygon.h"

#include "vrml/QvMFLong.h"
#include "ge3d/vectors.h"
#include "ge3d/ge3d.h"
#include <hyperg/utils/types.h>

#include "clipping.h"
#include "faceattr.h"
#include "bsptree.h"
#include "attributes.h"

#include <iostream.h>
#include <math.h>


static materialsGE3D* material_ptr = nil;
static int backface_culling = 1 ; // only initialized to turned on
static AnyLight* light_array[NUM_LIGHTS] = { 0 };

unsigned long Face::face_number_ = 0;
unsigned long Face::counter_ = 0;
float Face::max_area_ = 0;


Face::Face()
{
  id_ = counter_++; 
  face_number_++;
  vert_index_list_ = nil;
  faceattr_ = nil;
  status_ = VIS;
  area_ = 0;
}



Face::Face(int vertex_num,
         int* vert_index_list,
         int normal_num,
         const vector3D* normal, 
         FaceAttr* faceattr)
{
  id_ = counter_++; 

//  cout << "*:CONSTRUCT for face: " << id_ << " faceattr: " << faceattr->id() << "\n";
 

  face_number_++;
  
  faceattr_ = faceattr;
  if (faceattr_) faceattr_->newFaceRef();

  status_ = VIS;

  if (normal) normal_ = *normal;
  else init3D(normal_,0,0,0);


  if (vert_index_list)
  {
    vertex_num_ = vertex_num;
    vert_index_list_ = vert_index_list;
    calcPlane();  // calculates the normal vector if it is (0,0,0);
    calcArea();
  }
  else vertex_num_ = 0;

  if (normal_num == vertex_num) normal_num_ = normal_num; else normal_num_ = 0;

//  Clipping::print3D(normal_);

//cout << "*: face constructor end of face:" << id_ << "\n"; 

}




Face::Face(const Face& face)
{
  copy(face);
}




Face::~Face()
{
//cout << "Face destructor id: " << id_ << "\n";
  free();
  face_number_--;
  //cerr << "Face: " << face_number_ << " deleted\n";
//cout << "Face destructor ends here\n";
}


void Face::free()
{
  //cout << "*Face::free id: " << id_ << " faceattr: " << faceattr_->id() << "\n";
  delete [] vert_index_list_; 
  faceattr_->unFaceRef();
}


void Face::copy(const Face& face)
{
//cout << "*:Face::copy\n";
  
  normal_ = face.normal_;
  d_ = face.d_;             
  area_ = face.area_;
  status_ = face.status_;

  faceattr_ = face.faceattr_;      
  if (faceattr_) faceattr_->newFaceRef();
  

  //cout << "vert index list:\n";
  vertex_num_  = face.vertex_num_; 
  if (vertex_num_ > 0)
  {  
    vert_index_list_ = new int[vertex_num_ + 1]; // the final -1 !!!

    for (int i=0; i < vertex_num_ + 1; i++)
      vert_index_list_[i] = face.vert_index_list_[i];
  }

  //cout << "normal index list:\n"; 
  normal_num_ = face.normal_num_;  
  
}


Face& Face::operator=(const Face& face)
{
//cout << "*: operator=\n"; 
  if (this != &face)
  {
    free();
    copy(face);
  }
 
  return *this;
}




const vector3D& Face::normal() const
{
  return normal_;
}


void Face::calcArea()
{
 //cout << "*: calcArea:\n";

  if (vertex_num_ >= 3)
  { 
    point3D* vertex_list;
    vertex_list = vertices();
    vector3D diff_vector_1, diff_vector_2;
    vector3D cross_product;
    float length;
 
    length = 0;
    sub3D(vertex_list[1], vertex_list[0], diff_vector_1);
    for (int i=1; i < vertex_num_; i++)
    {
      sub3D(vertex_list[i], vertex_list[0], diff_vector_2);
      crp3D(diff_vector_1, diff_vector_2, cross_product);
      length += norm3D(cross_product);
      diff_vector_1 = diff_vector_2;
    }  
    area_ = length / 2; 
    if (area_ > max_area_) max_area_ = area_;
    delete [] vertex_list;
  } 

}
   


void Face::calcNormal()
{
  //cout << "*: calcNormal:\n";

  if (vertex_num_ >= 3)
  { 
    point3D* vertex_list;
    vertex_list = vertices();
    vector3D vector1, vector2;
    float norm;

    sub3D(vertex_list[1], vertex_list[0], vector1);
    sub3D(vertex_list[2], vertex_list[0], vector2);

    if ((vector1.x != vector2.x) || (vector1.y != vector2.y) || (vector1.z != vector2.z))
    {
      crp3D(vector1, vector2, normal_);
      norm = norm3D(normal_);
      if (norm!=0) scl3D(normal_, 1/norm);
    }

    delete [] vertex_list;
  } 
}


void Face::calcPlane()
{
  //cout << "*:calcPlane\n";

  point3D any_vertex;
  any_vertex = anyVertex();
  if ((normal_.x == 0) && (normal_.y == 0) && (normal_.z == 0)) calcNormal();
  d_ = dot3D(normal_, any_vertex);
}



void Face::resetAttributes()
{
  material_ptr = nil;
  backface_culling = 1; // turned on    

  for (int i=0; i < NUM_LIGHTS; i++)
  {
    if (light_array[i])
      light_array[i]->switchOff();
    light_array[i] = nil; 
  }
}

void Face::draw() const
{
  //cout << "*Face::draw face id/faceattr id: " << id_ << " / " << faceattr_->id() << "\n";
  //print();
  if (status_ != NOT_VISIBLE) forceDraw();
}

void Face::draw(const point3D& position, BSPMode mode) const
{
  if (mode == BSP_PLAIN)
     forceDraw();
  else if (mode == BSP_BACK_FACE_CULLING)
  {
   vector3D n;
   float scalar;

   point3D any_vertex = anyVertex();
   sub3D(any_vertex, position, n);
   scalar = dot3D(n, normal_);  
   // opposite direction or hint no backfaceculling
  
  int new_backface = faceattr_->backfaceCulling();
  if (backface_culling != new_backface)
     backface_culling = new_backface; 

   if ( (scalar < 0) || !backface_culling ) 
      forceDraw();
  }
  else if ((mode == BSP_SHADOW_VOLUME) && (status_ != NOT_VISIBLE)) forceDraw();
}




void Face::forceDraw() const
{  
//cout << "*:Face::draw() for face: " << id_ << " faceattr: " << faceAttr()->id() << "\n";

  //cout << "*: lightNum:" << faceattr_->lightNum() << "\n";
  // set active light sources, ge3d lights from index 1 upwards

  //cerr << "Status: " << status_ << "\n";

  static colorRGB white = { 1.0, 1.0, 1.0 };
  // always check if the current setting is different from the new one

  AnyLight* new_light;
  for (int i=0; i < faceattr_->lightNum(); i++)
  {
    // cerr << "light test: " << i << "\n";
    // faceattr_->light()[i]->print();
    new_light = faceattr_->light()[i];  
    if (light_array[i] != new_light) 
    {
      // cerr << "lightswitch:\n";
      new_light->setLight(i+1); // light with index 0 is the viewing light
      light_array[i] = new_light;
    }
  }


  // set material
  materialsGE3D* new_material = faceattr_->material();
  if (material_ptr != new_material) 
  {
    // cerr << "material switch:\n";
    if (new_material)
      ge3dMaterial(mat_front_and_back, new_material);
    else
    {
      ge3dFillColor (&white);
      ge3dDefaultMaterial ();
    }
    material_ptr = new_material;  
  }


  ge3dFaceSet(faceattr_->vertexList(), 
              vertex_num_, 
              vert_index_list_, 
              faceattr_->material(), matb_default, 0, nil,
              faceattr_->normalList(), 
              normal_num_, 
              vert_index_list_, 
              &normal_,
              nil /*texturelist */, 
              0 /* texturenum */, 
              nil /* textureindexlist */);


  // draws face normals in  every vertex
/*
  point3D point1;
  point3D point2;
  point3D* vertices = faceattr_->vertexList();
  for (int i=0; i < vertex_num_; i++)
  { 
    if ((i < vertex_num_) && (vert_index_list_[i] != -1))
    {   
       point1 = vertices[vert_index_list_[i]];
       add3D(point1, normal_, point2);
       ge3dLine(&point1, &point2);
    }
  }
*/



  // draws vertex normal vectors for each vertex
/* 
  point3D point1;
  vector3D normal1;
  point3D point2;
  //cerr << "size of normal index list " << normal_num_ << "\n";
  for (int i=0; i < normal_num_; i++)
  { 
    if ((i < vertex_num_)  && (vert_index_list_[i] != -1))
    {   
       point1 = faceattr_->vertexList()[vert_index_list_[i]];
       normal1 = faceattr_->normalList()[vert_index_list_[i]];
       add3D(point1, normal1, point2);
       ge3dLine(&point1, &point2);
    }
  }
*/

  // draws the bounding box
  // faceattr_->drawBoundBox();

}


void Face::print() const
{
  cerr << "Face ID:" << id_ << "\n";
  if (status_ == VIS) cerr << "VIS\n";
  if (status_ == PARTLY_VISIBLE) cerr << "PARTLY_VISIBLE\n";
  if (status_ == NOT_VISIBLE) cerr << "NOT_VISIBLE\n";
  cerr << "Area: " << area_ << "\n";
  Clipping::print3D(normal_);
  cerr << "d: " << d_ << "\n";
  cerr << "FaceAttr ID:" << faceattr_->id() << "\n";
  cerr << vertex_num_ << " vertex_num_, " << normal_num_ << " normal_num_\n"; 
  printIndices();
  cerr << "Vertices:\n";
  printVertices();
  cerr << "Normals:\n";
  printNormals(); 
  cerr << "\n\n";
}


void Face::printIndices() const
{
  int index;
  if (vertex_num_ == 0) 
    cerr << "keine Indices!!!\n";
  else cerr << "Indexlist: ";
  for (int i=0; i < vertex_num_ + 1; i++)
  {       
    index = vert_index_list_[i];    
    cerr << index << " ";
    if (index == -1) 
    {
      cerr << "\n";
      return;
    }
  }
  cerr << "indexlist ended without -1\n";  
}




void Face::printVertices() const
{
  point3D* face_vertices;
   
  face_vertices = vertices();

  for (int i=0; (i < vertex_num_) && (vert_index_list_[i] != -1); i++)
  {       
    Clipping::print3D(face_vertices[i]);
  }
  delete [] face_vertices;
}


void Face::printNormals() const
{
  point3D* vertex_normals = 0;
   
  if (normal_num_ > 0) vertex_normals = normals();

  for (int i=0; (i < normal_num_) && (vert_index_list_[i] != -1); i++)
  {       
    Clipping::print3D(vertex_normals[i]);
  }
  if (normal_num_) delete [] vertex_normals;
}



point3D* Face::vertices() const
{
//cout << "*:vertices\n";
  int index;
  int number=0;

  point3D* vertexlist = faceattr_->vertexList();

//cout << "got vertexlist\n";
  
  int cut = 0;
  if (vert_index_list_[vertex_num_ - 1] == -1) cut = 1;
 
  point3D* face_vertices = new point3D[vertex_num_ - cut];

//cout << "new array allocated of size " << vertex_num_ - cut << "\n";

  for (int i=0; i < (vertex_num_ - cut); i++)
  {    
    index = vert_index_list_[i];
    //cout << "index:" << index << "\n";
    face_vertices[number++] = vertexlist[index];
    //Clipping::print3D(face_vertices[number-1]);       
  }

//cout << "end vertices\n";
  
  return face_vertices;
}




vector3D* Face::normals() const
{

//cout << "*:normals\n";
  int index;
  int number=0;

  vector3D* normallist = faceattr_->normalList();

//cout << "got normallist\n";
  
  int cut = 0;
  if (vert_index_list_[normal_num_ - 1] == -1) cut = 1;
 
  vector3D* vertex_normals = new vector3D[normal_num_ - cut];

//cout << "new array allocated of size " << normal_num_ - cut << "\n";

  for (int i=0; i < (normal_num_ - cut); i++)
  {    
    index = vert_index_list_[i];
    //cout << "index:" << index << "\n";
    vertex_normals[number++] = normallist[index];
    //Clipping::print3D(vertex_normals[number-1]);       
  }

//cout << "end normals\n";
  
  return vertex_normals; 
 
}


point3D& Face::anyVertex() const
{
  return faceattr_->vertexList()[vert_index_list_[0]];
}


/*
int Face::inFront(const Plane& plane)
{
  point3D* vertex_list = vertices();
  int result = 1;  
  for (int i=0; i < vertex_num_; i++)
  {
     // This is not the same like !inBack because of arithmetic errors
     result = result && ( Clipping::inFront(vertex_list[i], plane.normal, plane.d) ||
                          Clipping::onPlane(vertex_list[i], plane.normal, plane.d)   );
  }
  delete [] vertex_list;
  return result;
}
*/   
