/**
 * @file llcoordframe.h
 * @brief LLCoordFrame class header file.
 *
 * $LicenseInfo:firstyear=2000&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LL_COORDFRAME_H
#define LL_COORDFRAME_H

#include "v3math.h"
#include "v4math.h"
#include "llerror.h"

// XXX : The constructors of the LLCoordFrame class assume that all vectors
//       and quaternion being passed as arguments are normalized, and all matrix
//       arguments are unitary.  VERY BAD things will happen if these assumptions fail.
//       Also, segfault hazzards exist in methods that accept F32* arguments.


class LLCoordFrame
{
public:
    LLCoordFrame();                                         // Inits at zero with identity rotation
    explicit LLCoordFrame(const LLVector3 &origin);         // Sets origin, and inits rotation = Identity
    LLCoordFrame(const LLVector3 &x_axis,
                 const LLVector3 &y_axis,
                 const LLVector3 &z_axis);                  // Sets coordinate axes and inits origin at zero
    LLCoordFrame(const LLVector3 &origin,
                 const LLVector3 &x_axis,
                 const LLVector3 &y_axis,
                 const LLVector3 &z_axis);                  // Sets the origin and coordinate axes
    LLCoordFrame(const LLVector3 &origin,
                 const LLMatrix3 &rotation);                // Sets axes to 3x3 matrix
    LLCoordFrame(const LLVector3 &origin,
                 const LLVector3 &direction);               // Sets origin and calls lookDir(direction)
    explicit LLCoordFrame(const LLQuaternion &q);           // Sets axes using q and inits mOrigin to zero
    LLCoordFrame(const LLVector3 &origin,
                 const LLQuaternion &q);                    // Uses quaternion to init axes
    explicit LLCoordFrame(const LLMatrix4 &mat);            // Extracts frame from a 4x4 matrix
    // The folowing two constructors are dangerous due to implicit casting and have been disabled - SJB
    //LLCoordFrame(const F32 *origin, const F32 *rotation); // Assumes "origin" is 1x3 and "rotation" is 1x9 array
    //LLCoordFrame(const F32 *origin_and_rotation);         // Assumes "origin_and_rotation" is 1x12 array

    bool isFinite() { return mOrigin.isFinite() && mXAxis.isFinite() && mYAxis.isFinite() && mZAxis.isFinite(); }

    void reset();
    void resetAxes();

    void setOrigin(F32 x, F32 y, F32 z);                    // Set mOrigin
    void setOrigin(const LLVector3 &origin);
    void setOrigin(const F32 *origin);
    void setOrigin(const LLCoordFrame &frame);

    inline void setOriginX(F32 x) { mOrigin.mV[VX] = x; }
    inline void setOriginY(F32 y) { mOrigin.mV[VY] = y; }
    inline void setOriginZ(F32 z) { mOrigin.mV[VZ] = z; }

    void setAxes(const LLVector3 &x_axis,                   // Set axes
                 const LLVector3 &y_axis,
                 const LLVector3 &z_axis);
    void setAxes(const LLMatrix3 &rotation_matrix);
    void setAxes(const LLQuaternion &q);
    void setAxes(const F32 *rotation_matrix);
    void setAxes(const LLCoordFrame &frame);

    void translate(F32 x, F32 y, F32 z);                    // Move mOrgin
    void translate(const LLVector3 &v);
    void translate(const F32 *origin);

    void rotate(F32 angle, F32 x, F32 y, F32 z);            // Move axes
    void rotate(F32 angle, const LLVector3 &rotation_axis);
    void rotate(const LLQuaternion &q);
    void rotate(const LLMatrix3 &m);

    void orthonormalize();  // Makes sure axes are unitary and orthogonal.

    // These methods allow rotations in the LLCoordFrame's frame
    void roll(F32 angle);       // RH rotation about mXAxis, radians
    void pitch(F32 angle);      // RH rotation about mYAxis, radians
    void yaw(F32 angle);        // RH rotation about mZAxis, radians

    inline const LLVector3 &getOrigin() const { return mOrigin; }

    inline const LLVector3 &getXAxis() const  { return mXAxis; }
    inline const LLVector3 &getYAxis() const  { return mYAxis; }
    inline const LLVector3 &getZAxis() const  { return mZAxis; }

    inline const LLVector3 &getAtAxis() const   { return mXAxis; }
    inline const LLVector3 &getLeftAxis() const { return mYAxis; }
    inline const LLVector3 &getUpAxis() const   { return mZAxis; }

    // These return representations of the rotation or orientation of the LLFrame
    // it its absolute frame.  That is, these rotations acting on the X-axis {1,0,0}
    // will produce the mXAxis.
    //      LLMatrix3 getMatrix3() const;               // Returns axes in 3x3 matrix
    LLQuaternion getQuaternion() const;         // Returns axes in quaternion form

    // Same as above, except it also includes the translation of the LLFrame
    //      LLMatrix4 getMatrix4() const;               // Returns position and axes in 4x4 matrix

    // Returns matrix which expresses point in local frame in the parent frame
    void getMatrixToParent(LLMatrix4 &mat) const;
    // Returns matrix which expresses point in parent frame in the local frame
    void getMatrixToLocal(LLMatrix4 &mat) const; // Returns matrix which expresses point in parent frame in the local frame

    void getRotMatrixToParent(LLMatrix4 &mat) const;

    // Copies mOrigin, then the three axes to buffer, returns number of bytes copied.
    size_t writeOrientation(char *buffer) const;

    // Copies mOrigin, then the three axes from buffer, returns the number of bytes copied.
    // Assumes the data in buffer is correct.
    size_t readOrientation(const char *buffer);

    LLVector3 rotateToLocal(const LLVector3 &v) const;      // Returns v' rotated to local
    LLVector4 rotateToLocal(const LLVector4 &v) const;      // Returns v' rotated to local
    LLVector3 rotateToAbsolute(const LLVector3 &v) const;   // Returns v' rotated to absolute
    LLVector4 rotateToAbsolute(const LLVector4 &v) const;   // Returns v' rotated to absolute

    LLVector3 transformToLocal(const LLVector3 &v) const;       // Returns v' in local coord
    LLVector4 transformToLocal(const LLVector4 &v) const;       // Returns v' in local coord
    LLVector3 transformToAbsolute(const LLVector3 &v) const;    // Returns v' in absolute coord
    LLVector4 transformToAbsolute(const LLVector4 &v) const;    // Returns v' in absolute coord

    // Write coord frame orientation into provided array in OpenGL matrix format.
    void getOpenGLTranslation(F32 *ogl_matrix) const;
    void getOpenGLRotation(F32 *ogl_matrix) const;
    void getOpenGLTransform(F32 *ogl_matrix) const;

    // lookDir orients to (xuv, presumed normalized) and does not affect origin
    void lookDir(const LLVector3 &xuv, const LLVector3 &up);
    void lookDir(const LLVector3 &xuv); // up = 0,0,1
    // lookAt orients to (point_of_interest - origin) and sets origin
    void lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest, const LLVector3 &up);
    void lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest); // up = 0,0,1

    // deprecated
    void setOriginAndLookAt(const LLVector3 &origin, const LLVector3 &up, const LLVector3 &point_of_interest)
    {
        lookAt(origin, point_of_interest, up);
    }

    friend std::ostream& operator<<(std::ostream &s, const LLCoordFrame &C);

    // These vectors are in absolute frame
    LLVector3 mOrigin;
    LLVector3 mXAxis;
    LLVector3 mYAxis;
    LLVector3 mZAxis;
};


#endif