/**
 * This file describes the tranformation matrix of 2-D graphics.
 * Classes described here are:
 * 		IGQuadrilateral and IGrafMatrix.
 *
 *   IBM Open Class Library
 *   (C) Copyright International Business Machines Corporation,  1997
 *   Licensed Material - Program-Property of IBM - All Rights Reserved.
 */

// Revision: 06 1.23.1.7 source/albert/graph2d/imatrix.hpp, 2d, ioc.v400, 980918 

#ifndef _IMATRIX_
#define _IMATRIX_

#include <igrtypes.hpp>
#include <igbase2d.hpp>
#include <ipt2darr.hpp>
#include <iprimtyp.hpp>

#if __IBMCPP__ >= 400
#pragma namemangling(compat)
#endif

class IGPoint2D;
class IGPolygon2D;
class IGrafMatrix;
class IGRect2D;
class IGRPoint2D;
class IGPoint2DArray;
class IGRPoint2DArray;

#pragma enum(4)
#pragma pack(push,4)

/** EMatrixKind for the rendering pipeline. */
enum EMatrixKind {
	kModelMatrix,
	kViewMatrix
};

/**
 * IGQuadrilateral is a four-sided polygon.
 * IGQuadrilateral is a four-sided polygon.
 * It specifies the quadrilateral used for specifying perspective and affine mapping.
 */

class IGQuadrilateral {
    public:
		/**
		 * Default constructor. Creates an IGQuadrilateral with all vertices at the origin.
		 */
        IGQuadrilateral();

        // Here's the order for the IGRect2D constructor:
        // p0 = TopLeft, p1 = TopRight, p2 = BottomRight, p3 = BottomLeft

		/**
		 * Creates a quadrilateral for a rectilinear mapping.
		 */
        IGQuadrilateral ( const IGRect2D& );

		/**
		 * Creates a quadrilateral for a general perspective mapping.
		 */
        IGQuadrilateral (
            const IGPoint2D& p0,
            const IGPoint2D& p1,
            const IGPoint2D& p2,
            const IGPoint2D& p3 );

		/**
		 * Copy constructor.
		 */
        IGQuadrilateral ( const IGQuadrilateral& );

        virtual ~IGQuadrilateral();

		/**
		 * Returns, in the specified parameters, the quadrilateral's four vertices.
		 */
        void points( IGPoint2D& p0, IGPoint2D& p1, IGPoint2D& p2, IGPoint2D& p3 ) const;

		/**
		 * Returns the specified vertex.
		 */
        IGPoint2D point( unsigned long index ) const;

		/**
		 * Sets the specified vertex.
		 */
        void    setPoint( unsigned long index, const IGPoint2D& p );

		/**
		 * Passes a quadrilateral through a linear transformation.
		 */
        void    transformBy( const IGrafMatrix& mat );

		/**
		 * Returns the geometry's bounding rectangle (the smallest axis-aligned rectangle that encloses the quadrilateral).
		 */
        IGRect2D  bounds () const;

		/**
		 * Checks to see if the IGQuadrilateral is a degenerate polygon.
		 */
        bool isDegenerate() const; // IsConcave & IsColinear check

		/**
		 * Assignment operator.
		 */
        IGQuadrilateral& operator= (const IGQuadrilateral& Src );

		/**
		 * Test the two quadrilaterals for equality.
		 */
        bool operator== (const IGQuadrilateral& Src ) const;

		/**
		 * Test the two quadrilaterals for inequality.
		 */
        bool operator!= (const IGQuadrilateral& Src ) const;

        IDataStream&    operator>>= (IDataStream&) const;
        IDataStream&    operator<<= (IDataStream&);
		void writeToStream(IDataStream& toWhere) const;
		void readFromStream(IDataStream& toWhere);

    private:
        IGPoint2DArray fPoints;

        enum EVertexOrientation{
            kClockWise,
            kCounterClockwise,
            kDegenerate             // Colinear? a line or a point
        };



   		EVertexOrientation GetVertexOrientation(const IGPoint2D& a,
                             const IGPoint2D& b,
                             const IGPoint2D& c) const;

};

/**
 * IGrafMatrix provides a transformation matrix that performs 2-D linear transformations for homogeneous spaces.
 * IGrafMatrix provides a transformation matrix that performs 2-D linear transformations for homogeneous spaces.
 * IGrafMatrix can be instantiated directly. All of the public methods of IGrafMatrix can be called
 * directly.
 */

class IGrafMatrix {
	public :

	/*
	 	Any Operation that causes the transform to change must also cause the transform's
     	time stamp t change.  timeStamp gets the current time stamp.  updateTimeStamp
   	 	allows subclasses to bump the time stamp when the transform's state changes.
	*/

        /**
         * Allows the client to efficiently specify an identity matrix.
		 */
        static const IGrafMatrix& identity ();

        /** EMatrixIndex defines the index values for the nine-element matrix array.*/
        enum EMatrixIndex {
            kScaleX = 0,
            kShearY = 1,
            kPerspectiveX = 2,
            kShearX = 3,
            kScaleY = 4,
            kPerspectiveY = 5,
            kTranslateX = 6,
            kTranslateY = 7,
            kHomogeneous = 8
        };

        /** EMatrixType defines the type of transformation matrix. */
        enum EMatrixType {
            kIdentity, kRotate, kTranslate, kScale, kAffine, kPerspective};

		/**
		 * Default constructor. Creates an identity matrix.
		 */
        IGrafMatrix ();

        /**
         * Copy constructor.
		 */
        IGrafMatrix ( const IGrafMatrix& matrix );

        /**
         * Creates a matrix from an array of matrix elements.
		 */
        IGrafMatrix ( const GCoordinate matrix [] );

        /**
         * Creates a matrix from a set of matrix elements.
		 */
        IGrafMatrix (
            const GCoordinate scaleX,
            const GCoordinate shearY,
            const GCoordinate perspectiveX,
            const GCoordinate shearX,
            const GCoordinate scaleY,
            const GCoordinate perspectiveY,
            const GCoordinate translateX,
            const GCoordinate translateY,
            const GCoordinate homogeneous );

        /**
         * Creates a translation matrix.
		 */
        IGrafMatrix (const IGPoint2D& delta);

        /**
         * Creates a matrix that scales around the specified point.
		 */
        IGrafMatrix (const IGPoint2D& scale, const IGPoint2D& centerOfScale);

        /**
         * Creates a matrix that rotates around the specified point.
		 */
        IGrafMatrix (const GDegrees& angle, const IGPoint2D& centerOfRotate);

        /**
         * Creates a matrix from the perspective mapping of the first parameter to the second one.
		 */
        IGrafMatrix (
            const IGQuadrilateral& fromQuadrilateral,
            const IGQuadrilateral& toQuadrilateral );

        virtual ~IGrafMatrix ();

        /**
         * Transforms a rational point to another rational point.
         * Transforms a rational point to another rational point.
	 	 * By default, transformPoint (singular) calls transformPoints (array).
	 	 * Subclasses can override transformPoint to improve performance.
		 */
        virtual IGRPoint2D transformPoint( const IGRPoint2D& source ) const;

        /**
         * Passes a rational point backwards through this transformation matrix.
         * Passes a rational point backwards through this transformation matrix.
	 	 * By default, untransformPoint Clones and Inverts the transform.
	 	 * Subclasses can override untransformPoint to improve performance. The matrix is not changed.
		 */
        virtual IGRPoint2D untransformPoint( const IGRPoint2D& source ) const;

        /**
         * Transforms a point to another point.
	 	 * By default, transformPoint (singular) calls transformPoints (array).
	 	 * Subclasses can override transformPoint to improve performance.
		 */
        virtual IGPoint2D transformPoint( const IGPoint2D& source ) const;

        /**
         * Passes a point backwards through this transformation matrix.
         * Passes a point backwards through this transformation matrix.
	 	 * By default, untransformPoint Clones and Inverts the transform.
	 	 * Subclasses can override untransformPoint to improve performance. The matrix is not changed.
		 */
        virtual IGPoint2D untransformPoint( const IGPoint2D& source ) const;

        // This may go away
		/**
         * Transforms an array of points efficiently. Returns the transformed points in the second parameter.
         * Transforms an array of points efficiently. Returns the transformed points in the second parameter.
	 	 * Subclasses must define transformPoints for points.
		 */
        virtual void    transformPoints (
                            const IGPoint2D points[],
                            IGPoint2D transformPoints[],
                            unsigned long count=1) const;

        /**
         * Transforms the specified array of points.
         * Transforms the specified array of points.
	 	 * Subclasses must define transformPoints for rational points.
		 */
        virtual void    transformPoints( IGPoint2DArray& points ) const;

        /**
         * Interprets IGPoint2D as a vector. It only transforms the magnitude and direction of the vector.
         * Interprets IGPoint2D as a vector. It only transforms the magnitude and direction of the vector.
	 	 * By default, transformVector calls transformPoint. Subclasses can override transformVector to improve performance.
		 */
        virtual IGPoint2D transformVector( const IGPoint2D& source ) const;

        /**
         * Passes a vector backwards through the transformation.
		 */
        virtual IGPoint2D untransformVector( const IGPoint2D& source ) const;

        /**
         * Transforms the corners of the specified IGRect2D, and finds the bounding box of these corners.
	 	 * Subclasses can override this to improve performance.
		 */
        virtual IGRect2D  transformBounds( const IGRect2D& r ) const;

        /**
         * Passes the specified IGRect2D backwards through the transformation.
		 */
        virtual IGRect2D  untransformBounds( const IGRect2D& r ) const;

        /**
         * Multiplies this matrix by another matrix. Concatenation is not commutative.
		 */
        virtual void    concatWith ( const IGrafMatrix& matrix );

        /**
         * Multiplies this matrix by delta translation. Concatenation is not commutative.
		 */
        virtual void    translateBy ( const IGPoint2D& delta );

        /**
         * Concatenates this matrix by a scale. The scaling is about the specified center of scale.
		 */
       virtual void    scaleBy (
            const IGPoint2D& scale,
            const IGPoint2D& centerOfScale=IGPoint2D::origin() );

        /**
         * Concatenates this matrix with a rotate matrix.
	 	 * The rotation is about the specified center of rotation. The angle is measured in degrees from the positive x axis to the positive y axis.
		 */
        virtual void    rotateBy (
            GDegrees degrees,
            const IGPoint2D& centerOfRotate=IGPoint2D::origin() );

        /**
         * Sets this matrix to another matrix, multiplied by this matrix. Concatenation is not commutative.
		 */
        virtual void    preConcatWith ( const IGrafMatrix& matrix );

        /**
         * Constructs a translation matrix out of the specified delta values and preconcatenates with this matrix.
		 */
        virtual void    preTranslateBy ( const IGPoint2D& delta );

        /**
         * Constructs a scale matrix out of the specified scaling factor and preconcatenates with this matrix.
         * Constructs a scale matrix out of the specified scaling factor and preconcatenates with this matrix.
	 	 * The scaling is centered around the specified point.
		 */
        virtual void    preScaleBy (
            const IGPoint2D& scale,
            const IGPoint2D& centerOfScale=IGPoint2D::origin() );

        /**
         * Constructs a rotation matrix out of the specified rotation factor and preconcatenates with this matrix.
         * Constructs a rotation matrix out of the specified rotation factor and preconcatenates with this matrix.
	 	 * The rotation is about the specified center of rotation. The angle is measured in degrees from the positive x axis to the positive y axis.
		 */
        virtual void    preRotateBy (
            GDegrees degrees,
            const IGPoint2D& centerOfRotate=IGPoint2D::origin() );

		/**
		 * Sets this IGrafMatrix to an identity matrix (that is, one that does nothing to a point).
		 */
        virtual void setToIdentity ();

		/**
		 * Sets this IGrafMatrix so that its translation components (kTranslateX and kTranslateY) are set to the coordinates of the specified IGPoint2D.
		 */
        virtual void setToTranslate ( const IGPoint2D& delta );

		/**
		 * Sets this IGrafMatrix so that its scaling components (kScaleX and kScaleY) are equal to the coordinates of the specified IGPoint2D.
		 */
        virtual void setToScale (
            const IGPoint2D& scale,
            const IGPoint2D& centerOfScale=IGPoint2D::origin() );

		/**
		 * Resets this IGrafMatrix to one that rotates around the specified point.
		 */
        virtual void setToRotate (
            GDegrees degrees,
            const IGPoint2D& centerOfRotate=IGPoint2D::origin() );

		/**
		 * Sets this matrix to one that transforms the first polygon into the second one.
		 * Sets this matrix to one that transforms the first polygon into the second one.
		 * The source and destination polygons must be convex, and they must have four
		 * vertices (quadrilateral). It creates the following perspective mapping:
         *	fromQuadrilateral [0] --> toQuadrilateral [0]
         *	fromQuadrilateral [1] --> toQuadrilateral [1]
         *	fromQuadrilateral [2] --> toQuadrilateral [2]
         *	fromQuadrilateral [3] --> toQuadrilateral [3]
		 */
        virtual void setToPerspectiveMap (
            const IGQuadrilateral& fromQuadrilateral,
            const IGQuadrilateral& toQuadrilateral );

		/**
		 * Fills in a nine-element array with matrix element values. You must preallocate the array before making this call.
		 */
        virtual GCoordinate*    matrix ( GCoordinate matrix [] ) const;

		/**
		 * Returns the angle of rotation (between 0 and 360 degrees) and the center of rotation in the respective parameters.
		 */
        virtual bool            rotate ( GDegrees& degrees, IGPoint2D& centerOfRotate ) const;

		/**
		 * Determines what type of matrix this is (that is, either kIdentity, kTranslate, kScale, kRotate, kAffine, or kPerspective).
		 */
        virtual EMatrixType     type() const;

		/**
		 * Determines whether this IGrafMatrix is an identity matrix (that is, does not transform the matrix).		
		 */
        virtual bool            isIdentity () const;

		/**
		 * Determines whether this IGrafMatrix is a translate matrix.		
		 */
        virtual bool            isTranslate () const;

		/**
		 * Determines whether the matrix is affine (that is, perspectiveX =0, perspectiveY =0, and homogeneous =1.0).		
		 */
        virtual bool            isAffine () const;

		/**
		 * Determines whether a rectangle passed through the matrix is still a rectangle.	
		 */
        virtual bool            isRectilinear () const;

		/**
		 * Inverts this IGrafMatrix. 	
		 */
        virtual void invert ();     // exception is used

		/**
		 * Gets the determinant of this IGrafMatrix. If the determinant is 0, a matrix is not invertible.  	
		 */
        virtual GCoordinate determinant () const;

		/**
		 * Transposes this IGrafMatrix (that is, swaps the rows and columns of the matrix).  	
		 */
        virtual void transpose ();

		/**
		 * Converts this IGrafMatrix to its Classical Adjoint.   	
		 */
        virtual void makeAdjoint ();

		/**
		 * Divides the IGrafMatrix by the homogeneous value.    	
		 */
        virtual bool    normalize ();

		/**
		 * Uses EMatrixIndex to get a particular element out of the IGrafMatrix.     	
		 */
        virtual GCoordinate element ( EMatrixIndex ) const;

		/**
		 * Sets the specified element in the IGrafMatrix.     	
		 */
        virtual void        setElement ( EMatrixIndex, GCoordinate element );

		/**
		 * Assignment operator.     	
		 */
        IGrafMatrix&    operator= ( const IGrafMatrix& source );

		/**
		 * Concatenates the left operand by the right operand and sets the left operand to the new value.      	
		 */
        IGrafMatrix&    operator*= ( const IGrafMatrix& matrix );

		/**
		 * Returns the time stamp for this IGrafMatrix.      	
		 */
        unsigned long timeStamp () const;

		/**
		 * Tests the two IGrafMatrix objects for equality.      	
		 */
		virtual bool operator==(const IGrafMatrix&) const;

		/**
		 * Tests the two IGrafMatrice obiects for inequality.      	
		 */
		virtual bool operator!=(const IGrafMatrix&) const;
		
        IDataStream&    operator>>= (IDataStream&) const;
        IDataStream&    operator<<= (IDataStream&);
		void writeToStream(IDataStream& toWhere) const;
		void readFromStream(IDataStream& toWhere);
		
    protected:
        void updateTimeStamp();
        void resetTimeStamp();
        void setTimeStamp ( unsigned long );

    private:
        // This may go away...it's used by the rendering code
friend class IGCurve2D;
friend class ISamplingExtractor;
friend class IGraph2DTestHelper;
        virtual void transformPoints(
                            const IGRPoint2D points[],
                            IGRPoint2D transformPoints[],
                            unsigned long count = 1) const;

        virtual void transformPoints(IGRPoint2DArray& points) const;

    private:
        enum EPrMatrixType {
            kPrIdentity, kPrRotate, kPrTranslate, kPrScale, kPrAffine, kPrGeneric};
        enum ERotateType {kRotate0, kRotate90, kRotate180, kRotate270, kSlowRotate};

        EPrMatrixType fAccelerator;
        ERotateType fRotateAccelerator;
        GCoordinate fTranslateX, fTranslateY;
        GCoordinate *fMatrix;

        EPrMatrixType GetAccelerator ( const GCoordinate matrix [] ) const;
        ERotateType GetFastRotate (GCoordinate degrees) const;
        void RotateToAffine (
            ERotateType rotateAccelerator,
            const GCoordinate rotateMatrix [],
            GCoordinate affineMatrix [] ) const;
        void ComputeCosAndSin ();

        unsigned long fTimeStamp;
friend class IGrafMatrixTimeStamp;
	static const unsigned long& staticTimeStamp();

        void ComputePerspectiveMap(
                const IGQuadrilateral& fromQuadrilateral,
                const IGQuadrilateral& toQuadrilateral );
        void UnitToQuad (
            const IGQuadrilateral& unitRectToQuad,
            GCoordinate dest [] );
        void RectToQuad (
            const IGRect2D& fromRect, const IGQuadrilateral& toQuad,
            GCoordinate dest [] );

        void PreMatrixMult (
            long matrixType,
            const GCoordinate matrix1 []);
        void PostMatrixMult (
            long matType,
            const GCoordinate matrix1 []);

        void SetInternalMatrixToIdentity();
};

#pragma pack(pop)
#pragma enum(pop)

#if __IBMCPP__ >= 400
#pragma namemangling()
#endif

#include <imatrix.inl>

#endif // _IMATRIX_
