/*
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You 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.
 *
 */

package org.apache.flex.compiler.internal.fxg.types;

import org.apache.flex.compiler.internal.fxg.dom.transforms.MatrixNode;

import org.apache.flex.swf.ISWFConstants;
import org.apache.flex.swf.types.Matrix;

/**
 * Utility class to help with matrix transformation for coordinate transformation.
 */
public class FXGMatrix
{

	public double a; //x-axis scaling
    public double b; //x-axis skew
    public double c; //y-axis skew
    public double d; //y-axis scaling
    public double tx; //x-axis translation
    public double ty; //y-axis translation
    
    //constructor
    public FXGMatrix(double a, double b, double c, double d, double tx, double ty)
    {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
        this.tx = tx;
        this.ty = ty;
    }
    
    //constructor - intializes matrix to identity matrix
    public FXGMatrix()
    {
        this.identity();
    }

    public FXGMatrix(MatrixNode m)
    {
        this.a = m.a;
        this.b = m.b;
        this.c = m.c;
        this.d = m.d;
        this.tx = m.tx;
        this.ty = m.ty;
    }

    public void identity() 
    {
        this.a = 1;
        this.b = 0;
        this.c = 0;
        this.d = 1;
        this.tx = 0;
        this.ty = 0;        
    }
    
    //concatenates matrix m to the current matrix
    public void concat(FXGMatrix m)
    {
        // Matrix multiplication 
        double new_a = a * m.a + b * m.c;
        double new_b = a * m.b + b * m.d;
        double new_c = c * m.a + d * m.c;
        double new_d = c * m.b + d * m.d;
        double new_tx = tx * m.a + ty * m.c + m.tx;
        double new_ty = tx * m.b + ty * m.d + m.ty;

        a  = new_a;
        b  = new_b;
        c  = new_c;
        d  = new_d;
        tx = new_tx;
        ty = new_ty;        
    }
    
    //concatenates a rotation matrix with rotation angle to the current matrix
    public void rotate(double angle)
    {
        double cos = Math.cos(angle*Math.PI/180.0);
        double sin = Math.sin(angle*Math.PI/180.0);
        FXGMatrix newM = new FXGMatrix(cos, sin, -sin, cos, 0, 0);
        this.concat (newM);
    }
    
    //concatenates a scaling matrix with scale factors scaleX and scaleY to the current matrix
    public void scale(double scaleX, double scaleY)
    {
        FXGMatrix newM = new FXGMatrix(scaleX, 0, 0, scaleY, 0, 0);
        this.concat (newM);     
    }
    
    //concatenates a transaltion matrix with translations (dx, dy) to the current matrix
    public void translate(double dx, double dy)
    {
        tx += dx;
        ty += dy;
    }
    
    //creates a matrix from the discrete transform parameters
    public static FXGMatrix convertToMatrix(double scaleX, double scaleY, double rotation, double tx, double ty)
    {
        FXGMatrix m = new FXGMatrix();
        m.scale (scaleX, scaleY);
        m.rotate (rotation);
        m.translate(tx, ty);        
        return m;
    }

    //returns a SWF Matrix data type that is equivalent to the current matrix
    public Matrix toSWFMatrix()
    {
        
        /*SWF matrices need to be invertible - check if it is invertible
         * disabled it for now - other apps seem to allow it
        FXGMatrix newm = new FXGMatrix(a, b, c, d, tx, ty);
        if (!newm.invert())
            throw new FXGException("MatrixNotInvertible");
        */
        
        Matrix sm = new Matrix();
        if (b != 0 || c != 0)
            sm.setRotate(b, c);        
        if (a != 0 || d != 0)
            sm.setScale(a, d);
        
        sm.setTranslate((int)(tx*ISWFConstants.TWIPS_PER_PIXEL), (int)(ty*ISWFConstants.TWIPS_PER_PIXEL));
        
        return sm;        
    }
    
    /**
     * Set matrix attribute values with values in this FXGMatrix object.
     * @param node - the matrix node whose attribute values will be updated.
     */
    public void setMatrixNodeValue(MatrixNode node)
    {
        node.a = this.a;
        node.b = this.b;
        node.c = this.c;
        node.d = this.d;
        node.tx = this.tx;
        node.ty = this.ty;
    }
    
}
