/***************************************************************************
 *   Copyright (C) 2003-2004 by Raphael Langerhorst                        *
 *   raphael-langerhorst@gmx.at                                            *
 *                                                                         *
 *   Copyright (C) 2004 Gerald Degeneve <gerald.degeneve@gmx.at>           *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

#ifndef GMATRIX44H
#define GMATRIX44H

namespace GCS

{

class GVector3;

/**
  \class GMatrix44 GMatrix44.h
  \brief Provides a 4x4 matrix and various mathematical matrix operations
  @author Raphael Langerhorst
  @author Gerald Degeneve
*/

class GMatrix44
{

  public:
    /**
     * This union represents all elements of the matrix in three variations.
     */
    union
    {
        /**
        * The struct represents the matrix with normal doubles.
        */
        struct
        {
             double m11, m12, m13, m14,
                    m21, m22, m23, m24,
                    m31, m32, m33, m34,
                    m41, m42, m43, m44;
        };
    
        /**
        * The field that represents all elements of the matrix
        * interpreted as [rows][columns].
        */
        double m[4][4];

        /**
        * The field that represents all elements of the matrix
        * interpreted as [row*4+column].
        */
        double n[16];
    };

  public:

    /**
     * Default constructor initializes the matrix with an identity matrix.
     */
    GMatrix44();

    /**
     * Constructor initializing with given elements.
     */
    GMatrix44(double _m11, double _m12, double _m13, double _m14,
              double _m21, double _m22, double _m23, double _m24,
              double _m31, double _m32, double _m33, double _m34,
              double _m41, double _m42, double _m43, double _m44);
    
    /**
     * Copy constructor.
     */
    GMatrix44(const GMatrix44& original);
    
    
    /**
     * Casting Operator.
     */
    operator double* ();


    /**
     * Converts this matrix into an identity matrix and returns this matrix.
     */
    GMatrix44& loadIdentity();
     

    /**
     * Multiplies (scales) this matrix with given scalar,
     * stores the result in a new matrix and returns it.
     */
    GMatrix44 multiply(double scalar) const;

    /**
     * Multiplies (transforms) this matrix with given matrix,
     * stores the result in a new matrix and returns it.
     */
    GMatrix44 multiply(const GMatrix44& m) const;
    

    /**
     * Transforms given vector with this matrix and
     * return a new transformed vector.
     */
    GVector3 transform(const GVector3& v) const;
    
    /**
     * Adds a matrix to the given matrix and
     * return a new Matrix.
     */
    GMatrix44 operator + (const GMatrix44& m) const;
    
    
    /**
     * Subtracts a matrix from the given matrix and
     * return a new Matrix.
     */    
    GMatrix44 operator - (const GMatrix44& m) const;
    
    
    /**
     * Multiplies (transforms) this matrix with given matrix,
     * stores the result in a new matrix and returns it.
     */
    GMatrix44 operator * (const GMatrix44& m) const;

    
    /**
     * Multiplies (scales) this matrix with given scalar,
     * stores the result in a new matrix and returns it.
     */
    GMatrix44 operator * (const double scalar) const;
    
    
    /**
     * Compares a this Matrix with another.
     * @return true when matrices are equal.
     */
    inline bool operator == (const GMatrix44& m) const;

    
    /**
     * Compares a this Matrix with another.
     * @return true when matrices are different.
     */
    inline bool operator != (const GMatrix44& m) const;
    
    
    //BEGIN static matrix creation methods

    
    /**
     * @returns an identity matrix.
     */
    static GMatrix44 createIdentityMatrix();
    
    /**
     * @returns a scale matrix with equal scaling for x, y and z.
     */
    static GMatrix44 createScaleMatrix(double scalar);
    
    /**
     * @returns a scale matrix with different scaling for x, y and z.
     */
    static GMatrix44 createScaleMatrix(const GVector3& v);
    
    /**
     * @returns a translation matrix for given vector.
     */
    static GMatrix44 createTranslationMatrix(const GVector3& v);
    
    /**
     * @param angle_rad angle in radians.
     * @returns a rotation matrix, turning for given angle around x axis.
     */
    static GMatrix44 createRotationAroundX(double angle_rad);
    
    /**
     * Same as above but for the Y axis.
     */
    static GMatrix44 createRotationAroundY(double angle_rad);
    
    /**
     * Same as above but for the Y axis.
     */
    static GMatrix44 createRotationAroundZ(double angle_rad);
    
    /**
     * @returns a rotation matrix that turns for given angle around given axis.
     * @param angle_rad angle to rotate in radians (2*pi for a full turn)
     */
    static GMatrix44 createRotationAroundAxis(const GVector3& v, double angle_rad);
    
    //END static matrix creation methods
};  
  
}

#endif
