//<copyright>
// 
// Copyright (c) 1994,95
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
// 
//</copyright>

//<file>
//
// Name:        scene3d.C (formerly scene.C)
//
// Purpose:     implementation of class scene3D, which manages SceneData
//
// Created:     18 Mar 92   Michael Hofer and Michael Pichler
//
// Changed:      2 Nov 95   Michael Pichler
//
//
//</file>

#ifdef __PC__
#include "stdafx.h"
#endif

#include "scene3d.h"
#include "memleak.h"

#include "sdfscene.h"
#include "geomobj.h"
#include "srcanch.h"
#include "camera.h"

#include "vrmlscene.h"
#include <vrml/QvDB.h>
#include <vrml/QvInput.h>

#include "vecutil.h"
#include <ge3d/ge3d.h>

#include <hyperg/hyperg/message.h>
#include <hyperg/hyperg/verbose.h>

#include <hyperg/utils/str.h>

#include <ctype.h>
#include <string.h>
#include <iostream.h>
#include <stdio.h>
#include <math.h>



Scene3D::Scene3D ()
{ 
  data_ = 0;

  winaspect_ = 4.0/3.0;

  mode_ = ge3d_smooth_shading;
  modeInt_ = same_mode;
  interact_ = 0;
  twosidedpolys_ = twosided_auto;
  dolighting_ = lighting_auto;
  viewinglight_ = 0;  // automatically on for files without light
  texlighting_ = 1;  // default: do texture lighting (same as ge3d default)
  linksactive_ = 0;
  linkscolor_ = l_brightness;
  arbitraryrot_ = 1;  // default: allow arbitrary camera rotations

  init3D (worldmin_, 0, 0, 0);
  init3D (worldmax_, 1, 1, 1);
  size_ = 1.0;
  selectedobj_ = NULL;
  selectedgroup_ = NULL;
  selectednode_ = NULL;
  selectedanchor_ = NULL;
  *pathtofiles_ = '\0';
  curfilename_ = 0;

  // default colours
  initRGB (col_background, 0.75, 0.75, 0.75);  // 75 % grey
  ge3dBackgroundColor (&col_background);
  initRGB (col_hudisplay, 0.5, 0, 1.0);  // purple
  initRGB (col_anchorface, 1.0, 0.25, 0);  // red-brown
  initRGB (col_anchoredge, 1.0, 1.0, 0.25);  // yellow
  initRGB (col_viewinglight, 1.0, 1.0, 1.0);  // white
//  ge3d_initialise ();  // too early - no window
}



void Scene3D::clear ()
{
  delete data_;
  data_ = 0;

  delete curfilename_;
  curfilename_ = 0;

  ge3dBackgroundColor (&col_background);  // user background

  if (selectedobj_ || selectedgroup_)
  {
    selectedobj_ = NULL;
    selectedgroup_ = NULL;
    selectionChanged ();
  }
  if (selectednode_ || selectedanchor_)
  {
    selectednode_ = NULL;
    selectedanchor_ = NULL;
    selectionChanged ();
  }

} // clear



// ***** readScene *****

// short form

// filepath should be the path and the name of the scene description file
// (if there is no extension present, .sdf is appended)
// returns 0 if no error
// returns errorcode otherwise

int Scene3D::readScene (const char* filepath)
{
#ifdef _MAC
  int retval = readScene(NULL, filepath);

  curfilename_ = new RString (filepath);
#else
  char path [256], filename [64];

  strcpy (path, filepath);
  char* slpos = strrchr (path, '/');    // last slash in path

  if (slpos)  // "path/.../filename"
  { strcpy (filename, slpos + 1);
    *++slpos = '\0';  // path ends after '/'
  }
  else  // filename only: current directory
  { *path = '\0';
    strcpy (filename, filepath);
  }

  // char* ptpos = strrchr (filename, '.');  // search dot in filename (without extension)
  // if (!strchr (filename, '.'))
  //   strcat (filename, ".sdf");  // anachronism

  int retval = readScene (path, filename);

  curfilename_ = new RString (filepath);

#endif // _MAC
  return retval;

} // readScene (filepath)


// readScene - long form
// pathtofiles should be "" (current directory), or end with "/", or NULL (meaning that
// the directory of a previous call to readScene is used [pathtofiles_]).

int Scene3D::readScene (const char* pathtofiles, const char* fname)
{
//cerr << "*** reading scene {" << (long) this << "} ***" << endl;

  char filename [256];  // including path

  if (pathtofiles)  // save path
    strcpy (pathtofiles_, pathtofiles);

  strcpy (filename, pathtofiles_);
  strcat (filename, fname);

  FILE* sdffile;  // open sdffile

  if (!(sdffile = fopen (filename, "r")))
  { 
//    cerr << "Scene Viewer. Error: scene file '" << filename << "' not found." << endl;
    return (100);  // sdffile not found
  }

  int retval = readScene (sdffile);
  fclose (sdffile);

  return retval;

} // readScene (pathtofiles, filename)



// readScene - from a stream (FILE*)
// caller responsible for closing the file
// caller responsible for decompression (OS dependent)

int Scene3D::readScene (FILE* file)
{
  // clear (delete) old data
  clear ();
  int errcode = 0;

  // check input format and create correct subclass of SceneData

  int chr = getc (file);

// due to Mac we have to do a seek instead of putback!
#ifdef _MAC
  fseek(file,0L,SEEK_SET);
#else
  ungetc (chr, file);
#endif

  if (chr == '#')  // evaluate header line
  {
    QvDB::init ();
    QvInput in;
    in.setFilePointer (file);

    if (in.getVersion ())  // check header
    {
      DEBUGNL ("Scene Viewer: QvLib: valid VRML header");
      VRMLScene* vrmlscene = new VRMLScene (this);
      errcode = vrmlscene->readInput (in);
      data_ = vrmlscene;
    }
    else
    {
      #ifdef _MAC
        fseek(file,0L,SEEK_SET);
      #endif
      DEBUGNL ("Scene Viewer: QvLib: invalid VRML header; assuming SDF file");
      SDFScene* sdfscene = new SDFScene (this);
      errcode = sdfscene->readFile (file);
      data_ = sdfscene;
    }
  }
  else if (chr == EOF)  // empty file
  { HgMessage::error ("empty scene file");
    return 0;  // no error
  }
  else  // no header line
  {
    HgMessage::message ("warning: scene file without header, assuming SDF");
    SDFScene* sdfscene = new SDFScene (this);
    errcode = sdfscene->readFile (file);
    data_ = sdfscene;
  }

  if (errcode)  // destroy invalid scenes immediately
  {
    delete data_;
    data_ = 0;
  }

  return errcode;
} // readScene (file)


// formatName

const char* Scene3D::formatName () const
{
  if (data_)
    return data_->formatName ();
  return 0;
}


// requestTextures
// stand-alone version requests all texture files to be loaded at once
// overridden by integrated Harmony Scene Viewer, which sends requests to database

void Scene3D::requestTextures ()
{
  if (data_)
    data_->loadTextures ();
}


void Scene3D::loadTextureFile (Material*, const char*)
{
// reading texture files (i.e. pixmaps) is better done by GUI libraries
}


int Scene3D::readInlineVRML (QvWWWInline* node, const char* filename)
{
  // TODO: uncompress input if necessary!!!
  if (data_)
    return data_->readInlineVRML (node, filename);
  return 0;  // failed
}


void Scene3D::errorMessage (const char* str) const
{
  //cerr << str;  // derived class may redirect them into a dialog
  // newline etc. should be part of string
}


// output internal scene data in write_... format

int Scene3D::writeScene (ostream& os, int format)
{
  if (!os || !data_)
    return -1;

  if (format != write_ORIGINAL)  // export current scene
    return data_->writeData (os, format);

  // save original input data
  if (curfilename_)
    return copyFile (fopen (*curfilename_, "r"), *curfilename_, os);

  return -1;  // cannot write current scene
}


int Scene3D::supportsOutputformat (int format)
{
  DEBUG ("supportsOutputformat request for " << format);
  DEBUGNL ("; current filename: " << (((const char*) curfilename_) ? *curfilename_ : "NONE"));

  if (format == write_ORIGINAL)  // can save original file when filname known
    return (curfilename_ != 0);

  // check possibility to export to desired format
  return (data_ && data_->supportsOutputformat (format));
}


int Scene3D::copyFile (FILE* infile, const char* filename, ostream& os)
{
  if (!infile || !os)
    return -1;

  int numchr;

  const int copybufsize = 4096;
  char copybuf [copybufsize];
  while ((numchr = fread (copybuf, 1, copybufsize, infile)) > 0)
    os.write (copybuf, numchr);

  return 0;
}


// Scene3D::printInfo

void Scene3D::printInfo (int all)
{
  if (data_)
    data_->printInfo (all);
  else
    cout << "no scene loaded." << endl;
}


// get number of faces (polygons)

unsigned long Scene3D::getNumFaces () const
{
  if (data_)
    return data_->getNumFaces ();
  return 0L;
}

unsigned long Scene3D::getNumPrimitives () const
{
  if (data_)
    return data_->getNumPrimitives ();
  return 0L;
}


// find object by name

GeometricObject* Scene3D::findObject (const char* objname)
{
  if (data_)
    return data_->findObject (objname);
  return 0;
}



// find object by object number

GeometricObject* Scene3D::findObject (int objnum)
{
  if (data_)
    return data_->findObject (objnum);
  return 0;
}



Material* Scene3D::findMaterial (const char* matname)
{
  if (data_)
    return data_->findMaterial (matname);
  return 0;
}



// clearanchors
// clear all anchors of the scene

void Scene3D::clearAnchors ()
{
  if (data_)
    data_->clearAnchors ();
}



// defineAnchor
// associate anchor with id with a geometric object, optional group anchor

void Scene3D::defineAnchor (GeometricObject* obj, long id, const RString& aobj, const char* group)
{
  if (obj)
  { DEBUG ("Scene3D: anchor for object " << obj->name () << " with id " << id);
    DEBUGNL ((group ? " and group " : " (object anchor)") << (group ? group : ""));
    obj->setAnchor (id, aobj, group);
  }
  else
  { DEBUGNL ("Scene3D: anchor with id " << id
             << " for non-existent object (ignored)");
  }
}



// requestInlineURL
// request a VRML scene by a URL that will be merged (inlined) into the current scene
// should be implemented by derived class

void Scene3D::requestInlineURL (QvWWWInline*, const char* url, const char* docurl)
{
  //cerr << "requesting inline URL " << url << " for document " << docurl << '\n'
  //     << "  (not supported by stand-alone scene viewer)" << endl;
}



// pickObject

void* Scene3D::pickObject (
  float fx, float fy,
  GeometricObject** gobj,
  QvNode** node,
  QvWWWAnchor** anchor,
  point3D* hitpoint, vector3D* normal,
  const StringArray** groups,
  float* hittime
)
{
  if (data_)
    return data_->pickObject (fx, fy, gobj, node, anchor, hitpoint, normal, groups, hittime);
  return 0;
}



// selectObj - SDF version
// select a geometric object (which is specially highlighted),
// unless a NULL pointer is passed,
// always unselect the previously selected object and group
// returns non zero, iff the selection changed
// to select a group the object to which the group belongs must
// be given, because group names are local to geometric objects

int Scene3D::selectObj (GeometricObject* obj, const char* group)
{
  if (obj == selectedobj_ && group == selectedgroup_)
    return 0;  // no change

  if (selectedobj_)
    selectedobj_->unselect ();
    // no need to checkAnchorSelection because only selected object relevant for selected anchor

  if (obj)
  { obj->select (group);
    obj->checkAnchorSelection ();
  }

  selectedobj_ = obj;
  selectedgroup_ = group;

  selectionChanged ();
  return 1;  // change
}


// selectNode - VRML version
// select a node (which may be an anchor) unless NULL
// always remove old selection
// returns non zero, iff the selection changed

int Scene3D::selectNode (QvNode* node, QvWWWAnchor* anchor)
{
  if (node == selectednode_ && anchor == selectedanchor_)
    return 0;  // no change

/*
  // TODO: node/anchor highlighting
  if (selectednode_)
    selectednode_->unselect ();

  if (node)
    node->select ();
    // TODO: poss. need equivalent for checkAnchorSelection?
*/
  selectednode_ = node;
  selectedanchor_ = anchor;

  selectionChanged ();
  return 1;  // change
}


// setSourceAnchor
// define selected object as source anchor
// just prints the number of the selected object
// should be overridden by derived class

void Scene3D::setSourceAnchor ()
{
  if (selectedobj_)
  { cout << "Position=Object " << selectedobj_->getobj_num ();
    if (selectedgroup_)
      cout << '/' << selectedgroup_;
    cout << endl;
  }
  else
    cout << "no source object/group selected." << endl;
}


// deleteSourceAnchor
// delete selected source anchor
// does not delete the anchor from the internal data structure
// because assumes that anchors are redefined after this call

void Scene3D::deleteSourceAnchor ()
{
  if (selectedobj_)
    deleteSourceAnchor (selectedobj_->getCurrentAnchor ());
  else
  { DEBUGNL ("no object selected - no source anchor to delete.");
  }
}


// deleteSourceAnchor
// just prints the ID of the selected anchor
// should be overridden by derived class

void Scene3D::deleteSourceAnchor (const SourceAnchor* anchor)
{
  if (anchor)
    cout << "I would delete the selected anchor with id " << anchor->id () << endl;
  else
    cout << "object, but no source anchor selected to be deleted." << endl;
}


// setDestinationAnchor
// (just prints current view)
// should be overriden by derived class
// to define current view as destination anchor

void Scene3D::setDestinationAnchor ()
{
  Camera* cam = getCamera ();

  if (cam)
  {
    cout << "Position= ";
    point3D p;
    cam->getposition (p);
    cout << p << ' ';
    cam->getlookat (p);
    cout << p << endl;
  }
  else
    cout << "no camera active." << endl;
}


// setDefDestAnchor
// (does nothing)
// should be overriden by derived class
// to define default view as destination anchor

void Scene3D::setDefDestAnchor ()
{
}


// history functions: back, forward, history

void Scene3D::back ()
{
  //cerr << "supposed to go back in history" << endl;
}

void Scene3D::forward ()
{
  //cerr << "supposed to go forward in history" << endl;
}

void Scene3D::history ()
{
  //cerr << "supposed to show up history" << endl;
}

// hold current document

void Scene3D::hold ()
{
  //cerr << "supposed to hold current viewer" << endl;
}

// document or anchor information

void Scene3D::docinfo (int)
{
  //cerr << "supposed to print document information" << endl;
}

// info on current anchor

void Scene3D::anchorinfo ()
{
  if (selectedobj_)
    anchorInfo (selectedobj_->getCurrentAnchor ());
}

// info on specific anchor

void Scene3D::anchorInfo (const SourceAnchor*)
{
  //cerr << "supposed to print anchor attributes" << endl;
}


Camera* Scene3D::getCamera () const
{
  if (data_)
    return data_->getCamera ();
  return 0;
}


void Scene3D::storeCamera () const
{
  if (data_)
    data_->storeCamera ();
}


void Scene3D::restoreCamera () const
{
  if (data_)
    data_->restoreCamera ();
}


void Scene3D::activateCamera (
  QvPerspectiveCamera* pcam, QvOrthographicCamera* ocam
)
{
  if (data_)
    data_->activateCamera (pcam, ocam);
}


// anachronisms getCameraPosition, getCameraLookat, setCameraParams removed
// (call the equivalent functions on the active camera)


// draw

void Scene3D::draw ()
{
  ge3d_clearscreen ();

  if (data_)
    data_->draw (currentMode ());

} // Scene3D::draw
