///////////////////////////////////////////////////////////////////////////////
//
//      ##  ######          
//       ######  ###        Based on:
//  ## ###############      
//   ########## # # #       Shark 3D Engine (www.shark3d.com)
//    ########
//   ######### # # #        Copyright (c) 1996-2001 Spinor GmbH.
//  ##   ##########         
//      ##                  
//        
///////////////////////////////////////////////////////////////////////////////
//
// G A M E.O.N.E - LOW LEVEL LIB V1.0
// Copyright (C) 2001 LEVEL ONE ENTERTAINMENT, 
// Licensed under the terms of LGPL.
//:---------------------------------------------------------------------------
//:Description
//
// LOW LEVEL 3D TRANSFORM UTIL HEADERFILE
//
//:---------------------------------------------------------------------------

#ifndef _LL3D_TRANSF_H
#define _LL3D_TRANSF_H


#include "ll3d_vec3.h"

namespace ll3d {

template<typename T> struct CTranslQuat;

/*
 * Structure defining a transformation in 3-dimensional space,
 * consisting of a scaling and rotation descibed by an quanternion
 * and a translation.
 */
template<typename T>
struct CTranslQuat
{
    // Translation vector.
    // See also CTranslQuat::ApplyToPoint.
    CVec3<T> m_Transl;

    // Quanternion containing a rotation and scaling.
    // See also CTranslQuat::ApplyToPoint.
    CQuat<T> m_Quat;

    // Does not initialize the structure.
    CTranslQuat();
    CTranslQuat(const CTranslQuat<T> &r);
    CTranslQuat(const CVec3<T> &Transl, const CQuat<T> &Quat);
    CTranslQuat(T s);
    CTranslQuat<T> &operator=(const CTranslQuat<T> &r);
    CTranslQuat<T> &operator=(T s);
    bool operator==(const CTranslQuat<T> &r) const;
    bool operator!=(const CTranslQuat<T> &r) const;
    bool operator==(T s) const;
    bool operator!=(T s) const;
    
    bool IsNull() const;// Are all components zero?
    bool IsFinite() const;// Are all components finit values?

    // Apply the position on a point.
    // In contrast to CTranslQuat::ApplyToVector,
    // both the the matrix and the translation is applied.
    // x.ApplyToPoint(v) = x.m_Transl + x.m_Quat.GetMat() * v
    CVec3<T> ApplyToPoint(const CVec3<T> &v) const;

    // Apply the position on a vector,
    // for example a directional vector or the difference between two points.
    // In contrast to CTranslQuat::ApplyToPoint,
    // only the matrix is applied but no translation. 
    // x.ApplyToVector(v) = x.m_Quat.GetMat() * v
    CVec3<T> ApplyToVector(const CVec3<T> &v) const;

    // Returns the inverse position.
    // x.GetInv().m_Transl = - x.m_Quat.GetInvMat() * x.m_Transl
    // x.GetInv().m_Quat = x.m_Quat.GetInv()
    CTranslQuat<T> GetInv() const;

    // Returns the concatenation of two positions. 
    // Like for matrices, this operation is associative, 
    // but not commutative.
    // x.Concat(y).ApplyToPoint(p)
    //          = x.ApplyToPoint(y.ApplyToPoint(p))
    // x.Concat(y).ApplyToVector(p)
    //          = x.ApplyToVector(y.ApplyToVector(p))
    // x.Concat(y).m_Transl 
    //          = x.m_Transl + x.m_Quat.GetMat() * y.m_Transl
    // x.Concat(y).m_Quat 
    //          = x.m_Quat * y.m_Quat
    CTranslQuat<T> Concat(const CTranslQuat<T> &r) const;

    // Return the concatenation of this 
    // with the inverse of the second position.
    // x.ConcatInv(y) = x.Concat(y.GetInv())
    // x.ConcatInv(y).Concat(y) = x
    CTranslQuat<T> ConcatInv(const CTranslQuat<T> &r) const;

    // Returns the concatenation of the inverse this 
    // with the second position.
    // This is the relative transformation from x to y.
    // x.InvConcat(y) = x.GetInv().Concat(y))
    // x.Concat(x.InvConcat(y)) = y
    CTranslQuat<T> InvConcat(const CTranslQuat<T> &r) const;
};

/*
 * Implementation CTranslQuat
 */
template<typename T>
inline CTranslQuat<T>::CTranslQuat()
{
}

template<typename T>
inline CTranslQuat<T>::CTranslQuat(const CTranslQuat<T> &r)
: m_Transl(r.m_Transl), m_Quat(r.m_Quat) 
{
}

template<typename T>
inline CTranslQuat<T>::CTranslQuat(const CVec3<T> &Transl, 
                                const CQuat<T> &Quat)
: m_Transl(Transl), m_Quat(Quat) 
{
}

template<typename T>
inline CTranslQuat<T>::CTranslQuat(T s)
: m_Transl(0), m_Quat(s) 
{
}

template<typename T>
inline CTranslQuat<T> &CTranslQuat<T>::operator=(
                                const CTranslQuat<T> &r)
{ 
    m_Transl = r.m_Transl; 
    m_Quat = r.m_Quat; 
    return *this; 
}

template<typename T>
inline CTranslQuat<T> &CTranslQuat<T>::operator=(T s)
{ 
    m_Transl = 0; 
    m_Quat = s; 
    return *this; 
}

template<typename T>
inline bool CTranslQuat<T>::operator==(
                                const CTranslQuat<T> &r) const
{
    return m_Transl == r.m_Transl && m_Quat == r.m_Quat; 
}

template<typename T>
inline bool CTranslQuat<T>::operator!=(
                                const CTranslQuat<T> &r) const
{ 
    return m_Transl != r.m_Transl || m_Quat != r.m_Quat; 
}

template<typename T>
inline bool CTranslQuat<T>::operator==(T s) const
{ 
    return m_Transl.IsNull() && m_Quat == s; 
}

template<typename T>
inline bool CTranslQuat<T>::operator!=(T s) const
{ 
    return !m_Transl.IsNull() || m_Quat != s; 
}

///////////////////////////////////////////////////////////////////////////////

template<typename T>
inline bool CTranslQuat<T>::IsNull() const
{
    return m_Transl.IsNull() && m_Quat.IsNull();
}

template<typename T>
inline bool CTranslQuat<T>::IsFinite() const
{
    return m_Transl.IsFinite() && m_Quat.IsFinite();
}

///////////////////////////////////////////////////////////////////////////////

template<typename T>
inline CVec3<T> CTranslQuat<T>::ApplyToPoint(
                                const CVec3<T> &v) const
{
    return m_Transl + m_Quat.GetMat() * v;
}

template<typename T>
inline CVec3<T> CTranslQuat<T>::ApplyToVector(
                                const CVec3<T> &v) const
{
    return m_Quat.GetMat() * v;
}

template<typename T>
inline CTranslQuat<T> CTranslQuat<T>::GetInv() const
{
    CQuat<T> InvQuat = m_Quat.GetInv();
    CMat3x3<T> InvMat = InvQuat.GetMat();
    CVec3<T> InvTransl = - InvMat * m_Transl;
    return CTranslQuat(InvTransl, InvQuat);
}

template<typename T>
inline CTranslQuat<T> CTranslQuat<T>::Concat(
                                    const CTranslQuat<T> &r) const
{
    CMat3x3<T> Mat = m_Quat.GetMat();
    CQuat<T> TotQuat = m_Quat * r.m_Quat;
    CVec3<T> TotTransl = m_Transl + Mat * r.m_Transl;
    return CTranslQuat(TotTransl, TotQuat);
}

template<typename T>
inline CTranslQuat<T> CTranslQuat<T>::ConcatInv(
                                    const CTranslQuat<T> &r) const
{
    CQuat<T> TotQuat = m_Quat * r.m_Quat.GetInv();
    CMat3x3<T> TotMat = TotQuat.GetMat();
    CVec3<T> TotTransl = m_Transl - TotMat * r.m_Transl;
    return CTranslQuat(TotTransl, TotQuat);
}

template<typename T>
inline CTranslQuat<T> CTranslQuat<T>::InvConcat(
                                    const CTranslQuat<T> &r) const
{
    CMat3x3<T> InvMat = m_Quat.GetInvMat();
    CQuat<T> TotQuat = m_Quat.GetInv() * r.m_Quat;
    CVec3<T> TotTransl = InvMat * (r.m_Transl - m_Transl);
    return CTranslQuat(TotTransl, TotQuat);
}


}//namespace ll3d
#endif //_LL3D_TRANSF_H