/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/** @file rs_matrix.rsh
 *  \brief Matrix functions.
 *
 * These functions let you manipulate square matrices of rank 2x2, 3x3, and 4x4.
 * They are particularly useful for graphical transformations and are
 * compatible with OpenGL.
 *
 * A few general notes:
 *
 * \li We use a zero-based index for rows and columns.  E.g. the last element of
 * a \ref rs_matrix4x4 is found at (3, 3).
 *
 * \li RenderScript uses column-based vectors.  Transforming a vector is done by
 * postmultiplying the vector, e.g. <em>(matrix * vector)</em>, as provided by
 * \ref rsMatrixMultiply.
 *
 * \li To create a transformation matrix that performs two transformations at
 * once, multiply the two source matrices, with the first transformation as the
 * right argument.  E.g. to create a transformation matrix that applies the
 * transformation \e s1 followed by \e s2, call
 * </c>rsMatrixLoadMultiply(&combined, &s2, &s1)</c>.
 * This derives from <em>s2 * (s1 * v)</em>, which is <em>(s2 * s1) * v</em>.
 *
 * \li We have two style of functions to create transformation matrices:
 * rsMatrixLoad<em>Transformation</em> and rsMatrix<em>Transformation</em>.  The
 * former style simply stores the transformation matrix in the first argument.
 * The latter modifies a pre-existing transformation matrix so that the new
 * transformation happens first.  E.g. if you call \ref rsMatrixTranslate
 * on a matrix that already does a scaling, the resulting matrix when applied
 * to a vector will first do the translation then the scaling.
 *
 */

#ifndef __RS_MATRIX_RSH__
#define __RS_MATRIX_RSH__

/**
 * Set an element of a matrix.
 *
 * @param m The matrix that will be modified.
 * @param col The zero-based column of the element to be set.
 * @param row The zero-based row of the element to be set.
 * @param v The value to set.
 *
 * \warning The order of the column and row parameters may be
 * unexpected.
 *
 * @return void
 */
_RS_RUNTIME void __attribute__((overloadable))
rsMatrixSet(rs_matrix4x4 *m, uint32_t col, uint32_t row, float v);
/**
 * \overload
 */
_RS_RUNTIME void __attribute__((overloadable))
rsMatrixSet(rs_matrix3x3 *m, uint32_t col, uint32_t row, float v);
/**
 * \overload
 */
_RS_RUNTIME void __attribute__((overloadable))
rsMatrixSet(rs_matrix2x2 *m, uint32_t col, uint32_t row, float v);

/**
 * Returns one element of a matrix.
 *
 * @param m The matrix to extract the element from.
 * @param col The zero-based column of the element to be extracted.
 * @param row The zero-based row of the element to extracted.
 *
 * \warning The order of the column and row parameters may be
 * unexpected.
 *
 * @return float
 */
_RS_RUNTIME float __attribute__((overloadable))
rsMatrixGet(const rs_matrix4x4 *m, uint32_t col, uint32_t row);
/**
 * \overload
 */
_RS_RUNTIME float __attribute__((overloadable))
rsMatrixGet(const rs_matrix3x3 *m, uint32_t col, uint32_t row);
/**
 * \overload
 */
_RS_RUNTIME float __attribute__((overloadable))
rsMatrixGet(const rs_matrix2x2 *m, uint32_t col, uint32_t row);

/**
 * Set the elements of a matrix to the identity matrix.
 *
 * @param m The matrix to set.
 */
extern void __attribute__((overloadable)) rsMatrixLoadIdentity(rs_matrix4x4 *m);
/**
 * \overload
 */
extern void __attribute__((overloadable)) rsMatrixLoadIdentity(rs_matrix3x3 *m);
/**
 * \overload
 */
extern void __attribute__((overloadable)) rsMatrixLoadIdentity(rs_matrix2x2 *m);

/**
 * Set the elements of a matrix from an array of floats.
 *
 * The array of floats should be in row-major order, i.e. the element a
 * <em>row 0, column 0</em> should be first, followed by the element at
 * <em>row 0, column 1</em>, etc.
 *
 * @param m The matrix to set.
 * @param v The array of values to set the matrix to. These arrays should be
 * 4, 9, or 16 floats long, depending on the matrix size.
 */
extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix4x4 *m, const float *v);
/**
 * \overload
 */
extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix3x3 *m, const float *v);
/**
 * \overload
 */
extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix2x2 *m, const float *v);
/**
 * Set the elements of a matrix from another matrix.
 *
 * If the source matrix is smaller than the destination, the rest of the
 * destination is filled with elements of the identity matrix.  E.g.
 * loading a rs_matrix2x2 into a rs_matrix4x4 will give:
 *
 * \htmlonly<table>
 * <tr><td>m00</td><td>m01</td><td>0.0</td><td>0.0</td></tr>
 * <tr><td>m10</td><td>m11</td><td>0.0</td><td>0.0</td></tr>
 * <tr><td>0.0</td><td>0.0</td><td>1.0</td><td>0.0</td></tr>
 * <tr><td>0.0</td><td>0.0</td><td>0.0</td><td>1.0</td></tr>
 * </table>\endhtmlonly
 *
 * @param m The matrix to set.
 * @param v The source matrix.
 */
extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix4x4 *m, const rs_matrix4x4 *v);
/**
 * \overload
 */
extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix4x4 *m, const rs_matrix3x3 *v);
/**
 * \overload
 */
extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix4x4 *m, const rs_matrix2x2 *v);
/**
 * \overload
 */
extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix3x3 *m, const rs_matrix3x3 *v);
/**
 * \overload
 */
extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix2x2 *m, const rs_matrix2x2 *v);

/**
 * Load a rotation matrix.
 *
 * This function creates a rotation matrix.  The axis of rotation is the
 * <em>(x, y, z)</em> vector.
 *
 * To rotate a vector, multiply the vector by the created matrix
 * using \ref rsMatrixMultiply.
 *
 * See http://en.wikipedia.org/wiki/Rotation_matrix .
 *
 * @param m The matrix to set.
 * @param rot How much rotation to do, in degrees.
 * @param x The x component of the vector that is the axis of rotation.
 * @param y The y component of the vector that is the axis of rotation.
 * @param z The z component of the vector that is the axis of rotation.
 */
extern void __attribute__((overloadable))
rsMatrixLoadRotate(rs_matrix4x4 *m, float rot, float x, float y, float z);

/**
 * Load a scale matrix.
 *
 * This function creates a scaling matrix, where each component of a
 * vector is multiplied by a number.  This number can be negative.
 *
 * To scale a vector, multiply the vector by the created matrix
 * using \ref rsMatrixMultiply.
 *
 * @param m The matrix to set.
 * @param x The multiple to scale the x components by.
 * @param y The multiple to scale the y components by.
 * @param z The multiple to scale the z components by.
 */
extern void __attribute__((overloadable))
rsMatrixLoadScale(rs_matrix4x4 *m, float x, float y, float z);

/**
 * Load a translation matrix.
 *
 * This function creates a translation matrix, where a
 * number is added to each element of a vector.
 *
 * To translate a vector, multiply the vector by the created matrix
 * using \ref rsMatrixMultiply.
 *
 * @param m The matrix to set.
 * @param x The number to add to each x component.
 * @param y The number to add to each y component.
 * @param z The number to add to each z component.
 */
extern void __attribute__((overloadable))
rsMatrixLoadTranslate(rs_matrix4x4 *m, float x, float y, float z);

/**
 * Multiply two matrices.
 *
 * Sets \e m to the matrix product of <em>lhs * rhs</em>.
 *
 * To combine two 4x4 transformaton matrices, multiply the second transformation matrix
 * by the first transformation matrix.  E.g. to create a transformation matrix that applies
 * the transformation \e s1 followed by \e s2, call
 * </c>rsMatrixLoadMultiply(&combined, &s2, &s1)</c>.
 *
 * \warning Prior to version 21, storing the result back into right matrix is not supported and
 * will result in undefined behavior.  Use rsMatrixMulitply instead.   E.g. instead of doing
 * rsMatrixLoadMultiply (&m2r, &m2r, &m2l), use rsMatrixMultiply (&m2r, &m2l).
 * rsMatrixLoadMultiply (&m2l, &m2r, &m2l) works as expected.
 *
 * @param m The matrix to set.
 * @param lhs The left matrix of the product.
 * @param rhs The right matrix of the product.
 */
extern void __attribute__((overloadable))
rsMatrixLoadMultiply(rs_matrix4x4 *m, const rs_matrix4x4 *lhs, const rs_matrix4x4 *rhs);
/**
 * \overload
 */
extern void __attribute__((overloadable))
rsMatrixLoadMultiply(rs_matrix3x3 *m, const rs_matrix3x3 *lhs, const rs_matrix3x3 *rhs);
/**
 * \overload
 */
extern void __attribute__((overloadable))
rsMatrixLoadMultiply(rs_matrix2x2 *m, const rs_matrix2x2 *lhs, const rs_matrix2x2 *rhs);

/**
 * Multiply a matrix into another one.
 *
 * Sets \e m to the matrix product <em>m * rhs</em>.
 *
 * When combining two 4x4 transformation matrices using this function, the resulting
 * matrix will correspond to performing the \e rhs transformation first followed by
 * the original \e m transformation.
 *
 * @param m The left matrix of the product and the matrix to be set.
 * @param rhs The right matrix of the product.
 */
extern void __attribute__((overloadable))
rsMatrixMultiply(rs_matrix4x4 *m, const rs_matrix4x4 *rhs);
/**
 * \overload
 */
extern void __attribute__((overloadable))
rsMatrixMultiply(rs_matrix3x3 *m, const rs_matrix3x3 *rhs);
/**
 * \overload
 */
extern void __attribute__((overloadable))
rsMatrixMultiply(rs_matrix2x2 *m, const rs_matrix2x2 *rhs);

/**
 * Multiply the matrix \e m with a rotation matrix.
 *
 * This function modifies a transformation matrix to first do a rotation.
 * The axis of rotation is the <em>(x, y, z)</em> vector.
 *
 * To apply this combined transformation to a vector, multiply
 * the vector by the created matrix using \ref rsMatrixMultiply.
 *
 * @param m The matrix to modify.
 * @param rot How much rotation to do, in degrees.
 * @param x The x component of the vector that is the axis of rotation.
 * @param y The y component of the vector that is the axis of rotation.
 * @param z The z component of the vector that is the axis of rotation.
 */
extern void __attribute__((overloadable))
rsMatrixRotate(rs_matrix4x4 *m, float rot, float x, float y, float z);

/**
 * Multiply the matrix \e m with a scaling matrix.
 *
 * This function modifies a transformation matrix to first do a scaling.
 * When scaling, each component of a vector is multiplied by a number.
 * This number can be negative.
 *
 * To apply this combined transformation to a vector, multiply
 * the vector by the created matrix using \ref rsMatrixMultiply.
 *
 * @param m The matrix to modify.
 * @param x The multiple to scale the x components by.
 * @param y The multiple to scale the y components by.
 * @param z The multiple to scale the z components by.
 */
extern void __attribute__((overloadable))
rsMatrixScale(rs_matrix4x4 *m, float x, float y, float z);

/**
 * Multiply the matrix \e m with a translation matrix.
 *
 * This function modifies a transformation matrix to first
 * do a translation.  When translating, a number is added
 * to each component of a vector.
 *
 * To apply this combined transformation to a vector, multiply
 * the vector by the created matrix using \ref rsMatrixMultiply.
 *
 * @param m The matrix to modify.
 * @param x The number to add to each x component.
 * @param y The number to add to each y component.
 * @param z The number to add to each z component.
 */
extern void __attribute__((overloadable))
rsMatrixTranslate(rs_matrix4x4 *m, float x, float y, float z);

/**
 * Load an orthographic projection matrix.
 *
 * Constructs an orthographic projection matrix, transforming the box
 * identified by the six clipping planes <em>left, right, bottom, top,
 * near, far</em> into a unit cube with a corner at
 * <em>(-1, -1, -1)</em> and the opposite at <em>(1, 1, 1)</em>.
 *
 * To apply this projection to a vector, multiply the vector by the
 * created matrix using \ref rsMatrixMultiply.
 *
 * See https://en.wikipedia.org/wiki/Orthographic_projection .
 *
 * @param m The matrix to set.
 * @param left
 * @param right
 * @param bottom
 * @param top
 * @param near
 * @param far
 */
extern void __attribute__((overloadable))
rsMatrixLoadOrtho(rs_matrix4x4 *m, float left, float right, float bottom, float top, float near, float far);

/**
 * Load a frustum projection matrix.
 *
 * Constructs a frustum projection matrix, transforming the box
 * identified by the six clipping planes <em>left, right, bottom, top,
 * near, far</em>.
 *
 * To apply this projection to a vector, multiply the vector by the
 * created matrix using \ref rsMatrixMultiply.
 *
 * @param m The matrix to set.
 * @param left
 * @param right
 * @param bottom
 * @param top
 * @param near
 * @param far
 */
extern void __attribute__((overloadable))
rsMatrixLoadFrustum(rs_matrix4x4 *m, float left, float right, float bottom, float top, float near, float far);

/**
 * Load a perspective projection matrix.
 *
 * Constructs a perspective projection matrix, assuming a symmetrical field of view.
 *
 * To apply this projection to a vector, multiply the vector by the
 * created matrix using \ref rsMatrixMultiply.
 *
 * @param m The matrix to set.
 * @param fovy Field of view, in degrees along the Y axis.
 * @param aspect Ratio of x / y.
 * @param near The near clipping plane.
 * @param far The far clipping plane.
 */
extern void __attribute__((overloadable))
rsMatrixLoadPerspective(rs_matrix4x4* m, float fovy, float aspect, float near, float far);

#if !defined(RS_VERSION) || (RS_VERSION < 14)
/**
 * Multiply a vector by a matrix.
 *
 * Returns the post-multiplication of the vector by the matrix, ie. <em>m * in</em>.
 *
 * When multiplying a \e float3 to a \e rs_matrix4x4, the vector is expanded with (1).
 *
 * When multiplying a \e float2 to a \e rs_matrix4x4, the vector is expanded with (0, 1).
 *
 * When multiplying a \e float2 to a \e rs_matrix3x3, the vector is expanded with (0).
 *
 * This function is available in API version 10-13.  Starting with API 14,
 * the function takes a const matrix as the first argument.
 */
_RS_RUNTIME float4 __attribute__((overloadable))
rsMatrixMultiply(rs_matrix4x4 *m, float4 in);

/**
 * \overload
 */
_RS_RUNTIME float4 __attribute__((overloadable))
rsMatrixMultiply(rs_matrix4x4 *m, float3 in);

/**
 * \overload
 */
_RS_RUNTIME float4 __attribute__((overloadable))
rsMatrixMultiply(rs_matrix4x4 *m, float2 in);

/**
 * \overload
 */
_RS_RUNTIME float3 __attribute__((overloadable))
rsMatrixMultiply(rs_matrix3x3 *m, float3 in);

/**
 * \overload
 */
_RS_RUNTIME float3 __attribute__((overloadable))
rsMatrixMultiply(rs_matrix3x3 *m, float2 in);

/**
 * \overload
 */
_RS_RUNTIME float2 __attribute__((overloadable))
rsMatrixMultiply(rs_matrix2x2 *m, float2 in);
#else
/**
 * Multiply a vector by a matrix.
 *
 * Returns the post-multiplication of the vector of the matrix, i.e. <em>m * in</em>.
 *
 * When multiplying a \e float3 to a \e rs_matrix4x4, the vector is expanded with (1).
 *
 * When multiplying a \e float2 to a \e rs_matrix4x4, the vector is expanded with (0, 1).
 *
 * When multiplying a \e float2 to a \e rs_matrix3x3, the vector is expanded with (0).
 *
 * This function is available starting with API version 14.
 */
_RS_RUNTIME float4 __attribute__((overloadable))
rsMatrixMultiply(const rs_matrix4x4 *m, float4 in);

/**
 * \overload
 */
_RS_RUNTIME float4 __attribute__((overloadable))
rsMatrixMultiply(const rs_matrix4x4 *m, float3 in);

/**
 * \overload
 */
_RS_RUNTIME float4 __attribute__((overloadable))
rsMatrixMultiply(const rs_matrix4x4 *m, float2 in);

/**
 * \overload
 */
_RS_RUNTIME float3 __attribute__((overloadable))
rsMatrixMultiply(const rs_matrix3x3 *m, float3 in);

/**
 * \overload
 */
_RS_RUNTIME float3 __attribute__((overloadable))
rsMatrixMultiply(const rs_matrix3x3 *m, float2 in);

/**
 * \overload
 */
_RS_RUNTIME float2 __attribute__((overloadable))
rsMatrixMultiply(const rs_matrix2x2 *m, float2 in);
#endif


/**
 * Inverts a matrix in place.
 *
 * Returns true if the matrix was successfully inverted.
 *
 * @param m The matrix to invert.
 */
extern bool __attribute__((overloadable)) rsMatrixInverse(rs_matrix4x4 *m);

/**
 * Inverts and transpose a matrix in place.
 *
 * The matrix is first inverted then transposed.
 * Returns true if the matrix was successfully inverted.
 *
 * @param m The matrix to modify.
 */
extern bool __attribute__((overloadable)) rsMatrixInverseTranspose(rs_matrix4x4 *m);

/**
 * Transpose the matrix m in place.
 *
 * @param m The matrix to transpose.
 */
extern void __attribute__((overloadable)) rsMatrixTranspose(rs_matrix4x4 *m);
/**
 * \overload
 */
extern void __attribute__((overloadable)) rsMatrixTranspose(rs_matrix3x3 *m);
/**
 * \overload
 */
extern void __attribute__((overloadable)) rsMatrixTranspose(rs_matrix2x2 *m);


#endif