// ============================================================================
// $Id: hminfo.h,v 1.14 2003/08/19 14:06:11 havran Exp $
//
// hminfo.h
//
// Class: CHitPointInfo
//
// Licence and usage conditions in November 2003:
// This file can be used and modified only with written permission of
// Vlastimil Havran, Pohodli 27, 57001 Litomysl, the Czech Republic.
// This library is granted to be used by Polina Kondratieva within
// her MPI HIWI job working on BRDFLIB library. The usage condition
// may change in the next version of the library. At the moment,
// the library development is supported by Max-Planck-Institut for
// Informatik, Stuhlsatzenhausweg 85, 66123 Saarbruecken Germany
// See WWW: http://www.mpi-sb.mpg.de
// REPLACEMENT_STRING
//
// Initial coding by Vlastimil Havran + Petr Mladek and Jiri Liska
// in December 1999.
// Given for the use to BRDFLIB project for the Polina Kondratieva's
// HIWI JOB assignment in November 2003.
// ============================================================================

#ifndef __HMINFO_H__
#define __HMINFO_H__

// BRDFLIB headers
#include "configh.h"
#include "color.h"
#include "mapinfo.h"
#include "mappibas.h"
#include "ray.h"
#include "texture.h"
#include "vector3.h"
#include "vector2.h"

// standard headers

__BEGIN_BRDFLIB_HEADER

// forward declarations

// --------------------------------------------------------------------------
// This class includes information about intersection of the ray and the object
// it is used for computation MapPointInfo
// and for the intersection computation as well
// --------------------------------------------------------------------------
class CHitPointInfo
{
protected:
  // the smallest signed distance for intersection computation from
  // the origin of the ray. It is modified by the intersection routines.
  float t;

  // the time of intersection - either input or output value or both.
  TTime time;

  // point of intersection of the object's surface, it is used for normal
  // computation as the input. The information about signed distance cannot
  // be used as input for normal computation ! Use Extrap() before computing
  // normal.
  CVector3D point;

  // the normal of object in point of intersection
  CVector3D normal;
  // the anisotropy axis of object in point of intersection. This axis
  // will be treated as X axis in local coordinate system at the
  // hit point, therefore it should be specified for the Global
  // coordinate system
  CVector3D anisAxis;
  // if normal is valid (was computed by object) the value is true
  bool normalValid;
  // if anisotropy direction is valid (computed by object) the value is true
  bool anisAxisValid;
  // if texture coordinates are valid (was set by the object) the value is true
  bool texCoordsValid;
  // the DotProd(ray.dir, normal) for current intersection - this is computed
  // when normal of the object is asked for
  float dotProd;
  // this is the place to store the flags
  int flags;
  // reference to the ray for which the intersection was computed
  const CRay *ray;
  // this is precomputed direction of ideally reflected ray (mirrored)
  CVector3D idealMirrorDir;
  // For texture antialiasing etc.
  // differential vector in direction u, perpendicular to normal.
  CVector3D diffU;
  // differential vector in direction v, perpendicular to normal.
  CVector3D diffV;
  // texture coordinates, which can be set directly when hit occurs by the affected primitive
  CVector2D texCoords;
public:
  // This is the variable to be used for mapping, when no mapping texture
  // coordinates are used
  static const CMapPointInfo constMapInfo;
public:
  // -----------------------------------------------------------------------
  // constructor
  CHitPointInfo(const CRay *Ray = NULL,
		const TTime Time = 0.0): time(Time), ray(Ray)
  {
    normalValid = false;
    anisAxisValid = false;
	texCoordsValid = false;
    anisAxis = CVector3D(1,0,0); 
    normal   = CVector3D(1,0,0); 
	texCoords = CVector2D(0,0);
    flags = 0;
#ifdef _DEBUG
    point.SetValue(MAXFLOAT);
#endif
  }
  // copy constructor
  CHitPointInfo(const CHitPointInfo &info);

  // destructor 
  ~CHitPointInfo() { }

  // assignment operator
  CHitPointInfo& operator=(const CHitPointInfo &info);

  // the flags that can be associated with the info
  enum EFlags {
    // the precomputed direction was/not computed
    EE_IdealMirrorValid = 0x1,
    // if diffU and diffV are/not valid
    EE_diffsValid = 0x2,
  };

  // flags associated with the HitPointInfo
  const int& Flags() const { return flags; }

  // sets the specified bits of flags
  int SetFlags(CHitPointInfo::EFlags bitsToSet) {
    flags |= bitsToSet;
    return flags;
  }

  // resets the specified bits of flags
  int ResetFlags(CHitPointInfo::EFlags bitsToReset) {
    flags &= ~bitsToReset;
    return flags;
  }

  // (re)initialize the info, when it should be used again for new ray,
  // intersection etc.
  void Init() {
	ray = NULL; normalValid = false; anisAxisValid = false; texCoordsValid = false;
    flags = 0;
#ifdef _DEBUG
    point.SetValue(MAXFLOAT);
#endif
  }

  // reset the validity of normal
  void InvalidateNormal() {
    normalValid = false;
  }

  // reset the validity of anisotropy axis
  void InvalidateAnisAxis() {
    anisAxisValid = false;
  }

  // reset the validity of texture coordinates
  void InvalidateTexCoords() {
	texCoordsValid = false;
  }

  // returns the shading=geometric normal. We do not consider
  // bump mapping possibility.
  const CVector3D& GetNormal() const;

  // returns the the direction of anisotropy.
  const CVector3D& GetAnisAxis() const;

  // returns texture coordinates.
  const CVector2D& GetTexCoords() const;

  void SetNormal(CVector3D new_normal) {
    //  Changed by KP
    ((CVector3D&)normal).SetValue(new_normal[0],new_normal[1],new_normal[2]);
    (bool&)normalValid = true;

    if (!normalValid) {
      assert(ray);
      // !!!!
      // This function must be implemented by the user of this library
      // accessing some geometric object data.
      // !!!!
      // temporary version
      ((CVector3D&)normal).SetValue(0.0, 0.0, 1.0);
      // !!!!

      // compute the dot product
      (float&)dotProd = DotProd(normal, ray->GetDir());
      assert(dotProd <=  1.00001);
      assert(dotProd >= -1.00001);
      (bool&)normalValid = true;
    }
    else {
      (float&)dotProd = DotProd(normal, ray->GetDir());
      assert(dotProd <=  1.00001);
      assert(dotProd >= -1.00001);
    }

  }

  void SetTexCoords(CVector2D& new_tex_coords) {
    //  Changed by KP
    ((CVector2D&)texCoords).SetValue(new_tex_coords[0],new_tex_coords[1]);
    (bool&)texCoordsValid = true;
  }

  void SetAnisAxis(CVector3D newAnisAxis) {
    anisAxis.SetValue(newAnisAxis[0], newAnisAxis[1],
                                    newAnisAxis[2]);
    anisAxisValid = true;
  }

  // sets the dotProd when calculating normal, it should be used by
  // CObject3Ds' member functions for setting normal of by bumpNormal
  float SetDotProd(float newDotProd) {
    return (dotProd = newDotProd);
  }
  // just return the dotProd, it can be used after the normal was computed
  const float& GetDotProd() const {
    assert(normalValid);
    return dotProd;
  }
  // returns the ideally mirrored vector, assumes the ray is set and
  // that the intersection exist, and the point was extrapolated.
  const CVector3D& GetReflectedDir() const;

  // returns the signed distance for this info
  float GetT() const { return t;}

  // -----------------------------------------------------------------------
  // returns two tangent vectors to the normal in the direction of common
  // two-dimensional mapping along the surface object.
  void GetDifferentials(CVector3D &u, CVector3D &v) const;
  // returns value of the variable differsValid 
  bool AreDifferentialsValid() const {
    return (flags & EE_diffsValid) ? true : false;
  }
  // this is one way how to set the tangent vectors to the normal
  void SetDifferentials(CVector3D &uVec, CVector3D &vVec) {
    diffU = uVec; diffV = vVec;
  }

  // -----------------------------------------------------------------------
  // return the point of intersection
  const CVector3D& GetPoint() const {
#ifdef _DEBUG
    if (point.x == MAXFLOAT) {
      FATAL << "Uninitialized point of intersection from signed distance" << endl;
      FATAL_ABORT;
    }
#endif
    return point;
  }
  // return the pointer to the associated ray
  const CRay* GetRay() const {
    return ray;
  }

  // computes the intersection point from a given signed distance
  void Extrap() {
    if (ray)
      point = ray->Extrap(t);
  }
  
  void SetT(float newt) {t = newt;} 

  // for debugging purposes
  friend ostream& operator<< (ostream &s, const CHitPointInfo &h);

  /**
     Computation of mapping for some parameter for this particular hitInfo.
     This function should be implemented by the user of this library, since
     it is necessary to have the access to the objects, their geometric
     mappings, etc. It return false on success and sets correspondingly
     the mapping info parameters.

     @param mapinfo      output mapping info

     @param whichParam   input parameter

     @return false/true  success/failure
  */
  bool ComputeMapping(CMapPointInfo &mapinfo,
		      CMappingBase::EParamType whichParam) const;


  /**
     Computation of mapping for some parameter for this particular hitInfo.
     This function should be implemented by the user of this library, since
     it is necessary to have the access to the objects, their geometric
     mappings, etc. It return false on success and sets correspondingly
     the mapping info parameters.

     @param mapinfo      output mapping info
     @param whichParam   input parameter
	 @param incCos	     arbitrary cosine of incidence angle, that 
						 should be used instead of normal incidence

     @return false/true  success/failure
  */
  bool ComputeArbitraryMapping(CMapPointInfo &mapinfo,
		      CMappingBase::EParamType whichParam,
			  const float incCos) const;

  /**
     Computes the color of the texture given the mapping to be used.

     @param values      output color value

     @param texture     input texture to be used

     @param whichParam  the mapping to be used for this particular texture
  */
  void GetTextureValue(CValues &values, CTexture &texture,
		       const CMappingBase::EParamType whichParam) const;

  /**
     Computes the color of the texture given the mapping to be used.

     @param values      output color value
     @param texture     input texture to be used
	 @param incCos		arbitrary cosine of incidence angle used instead of normal incidence

     @param whichParam  the mapping to be used for this particular texture
  */
  void GetTextureValue(CValues &values, CTexture &texture,
		       const CMappingBase::EParamType whichParam,
			   const float incCos) const;
};

__END_BRDFLIB_HEADER

#endif // __HMINFO_H__

