#ifndef XDKWRL_TRANSFORMATOR_H
#define XDKWRL_TRANSFORMATOR_H

#include <xdkwrl/config.h>
#include <iostream>
#include <deque>

namespace wrl
{
  //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  // Interface of Transformator
  //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  class XDKWRL_API Transformator
  {
  public:
    Transformator();

    void loadIdentity();
    void translate(float x,float y,float z);
    void rotate(float rad,float x,float y,float z);
    void scale(float x,float y,float z);
    void scale(float s);
    void postMultMatrix(const float *m);
    void preMultMatrix(const float *m);
    void loadMatrix(const float* m);

    inline operator const float*() const;
    void getMatrix(float* m) const;
    void transform(const float* src,float* dst) const;
    Transformator inverseTranspose() const;

    friend std::ostream& operator<<(std::ostream& s,
				    const wrl::Transformator& t);
    inline float& element(unsigned short i,
			  unsigned short j);
    inline float element(unsigned short i,
			 unsigned short j) const;
    
    static inline Transformator translation(float x,float y,float z);
    static inline Transformator rotation(float rad,float x,float y,float z);
    static inline Transformator scaling(float x,float y,float z);
    static inline Transformator scaling(float s);

    friend bool operator==(const Transformator& t0,const Transformator& t1);
    friend bool operator!=(const Transformator& t0,const Transformator& t1);
    friend bool operator<(const Transformator& t0,const Transformator& t1);
  private:
    float m_[16];	
  };
  //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  // Interface of TransformatorHierarchy
  //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  class XDKWRL_API TransformatorHierarchy
  {
  public:
    TransformatorHierarchy();
    inline void push(const Transformator& t);
    void pop();
    
    inline void transform(const float* src,float* dst) const;
    inline void transformByInverseTranspose(const float* src,
					    float* dst) const;

    friend bool operator==(const TransformatorHierarchy& th0,
			   const TransformatorHierarchy& th1);
    friend bool operator!=(const TransformatorHierarchy& th0,
			   const TransformatorHierarchy& th1);
    friend bool operator<(const TransformatorHierarchy& th0,
			  const TransformatorHierarchy& th1);
    friend std::ostream& operator<<(std::ostream& s,
				    const wrl::TransformatorHierarchy& t);
  private:
    std::deque<Transformator> t_;
    Transformator             result_;
    Transformator             invTransResult_;
  };
}
//***************************************************************
// Implementation of Transformator
//***************************************************************
/*!
 * Returns the internal matrix. Convenient alternative to getMAtrix(),
 * especially to work with postMultMatrix and preMultMatrix~:
 \code
 Transformator t;
 // ....
 Transformator t2;
 // ...
 t2.postMultMatrix(t1);  // valid code
 \endcode
*/
inline
wrl::Transformator::operator const float*() const
{
  return m_;
}
/*!
 * Return element on line \p i and colum \p j
 * (numbered from 0 to 3).
 */
inline float&
wrl::Transformator::element(unsigned short i,
			    unsigned short j)
{
  return m_[i*4+j];
}
/*!
 * Return element on line \p i and colum \p j
 * (numbered from 0 to 3).
 */
inline float
wrl::Transformator::element(unsigned short i,
			    unsigned short j) const
{
  return m_[i*4+j];
}
/*!
 * Returns a transformation object for translation.
 */
inline wrl::Transformator
wrl::Transformator::translation(float x,float y,float z)
{
  Transformator t;
  t.translate(x,y,z);
  return t;
}
/*!
 * Returns a transformation object for rotation.
 * Provided for convenience.
 */
inline wrl::Transformator
wrl::Transformator::rotation(float rad,float x,float y,float z)
{
  Transformator t;
  t.rotate(rad,x,y,z);
  return t;
}    
/*!
 * Returns a transformation object for scaling.
 * Provided for convenience.
 */
inline wrl::Transformator
wrl::Transformator::scaling(float x,float y,float z)
{
  Transformator t;
  t.scale(x,y,z);
  return t;
}    
/*!
 * Returns a transformation object for scaling.
 * \overload
 */
inline wrl::Transformator
wrl::Transformator::scaling(float s)
{
  Transformator t;
  t.scale(s);
  return t;
} 
//************************************************************
// Implementation of TransformatorHierarchy
//************************************************************
inline void
wrl::TransformatorHierarchy::push(const wrl::Transformator& t)
{
  t_.push_back(t);
  result_.postMultMatrix(t);
  invTransResult_ = result_.inverseTranspose();
}
/*!
 * Apply the sequence of Transformator pushed in the hierarchy by applying
 * it in reverse order, that is last pushed first. The src and dst vectors
 * can be the same (temporary values are used).
 */
inline void
wrl::TransformatorHierarchy::transform(const float* src,float* dst) const
{
  result_.transform(src,dst);
}
/*!
 * Apply the sequence of Transformator pushed in the hierarchy by applying
 * it in reverse order, that is last pushed first. Inverse transpose are
 * used. This function should be used
 * when transforming normals.
 */
inline void
wrl::TransformatorHierarchy::transformByInverseTranspose(const float* src,
							 float* dst) const
{
  invTransResult_.transform(src,dst);
}
#endif //XDKWRL_TRANSFORMATOR_H

// Local variables section.
// This is only used by emacs!
// Local Variables:
// ff-search-directories: ("." "../../../src/xdkwrl/tools/")
// End:
