  /**
   * http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html
   *
   * A great resource: http://www.senocular.com/flash/tutorials/transformmatrix/
   *
   * (a:Number = 1, b:Number = 0, c:Number = 0, d:Number = 1, tx:Number = 0, ty:Number = 0)
   *
   * @param int a
   * @param int b
   * @param int c
   * @param int d
   * @param int tx
   * @param int ty
   */

  Matrix = function(a, b, c, d, tx, ty){


    /**
     * @var int a The value that affects the positioning of pixels along the x
     * axis when scaling or rotating an image.
     */
    this.a = a || 1;


    /**
     * @var int b The value that affects the positioning of pixels
     * along the y axis when rotating or skewing an image.
     */
    this.b = b || 0;


    /**
     * @var int c The value that affects the positioning of pixels
     * along the x axis when rotating or skewing an image.
     */
    this.c = c || 0;

    /**
     * @var int d The value that affects the positioning of
     * pixels along the x axis when scaling or rotating an image.
     */
    this.d = d || 1;

    /**
     * @var int tx The distance by which to translate each point
     * long the x axis.
     */
    this.tx = tx || 0;

    /**
     * @var int ty The distance by which to translate each point
     * along the y axis.
     */
    this.ty = ty || 0;

  };

  /**
   * @return {Matrix} A clone of this Matrix.
   */
  Matrix.prototype.clone = function(){

    return new Matrix(this.a, this.b, this.c, this.d, this.tx, this.ty);

  };

  /**
   * Concatenates a matrix with the current matrix, effectively combining the
   * geometric effects of the two.
   *
   * http://en.wikipedia.org/wiki/Matrix_multiplication
   *
   * @link {http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html#concat()}
   *
   * @param {Matrix} m The matrix to contat with this matrix.
   *
   * @return void
   */
  Matrix.prototype.concat = function(m) {

    var r = new Matrix();

    // 1,1
    // (a,c,tx) * (a,b,0)
    r.a = (this.a * m.a) + (this.c * m.b);

    // 1,2
    // (a,c,tx) * (c,d,0)
    r.c = (this.a * m.c) + (this.c * m.d);

    // 1,3
    // (a,c,tx) * (tx,ty,1)
    r.tx = (this.a * m.tx) + (this.c * m.ty) + this.tx;

    // 2,1
    // (b,d,ty) * (a,b,0)
    r.b = (this.b * m.a) + (this.d * m.b);

    // 2,2
    //(b,d,ty) * (c,d,0)
    r.d = (this.b * m.c) + (this.d * m.d);

    // 2,3
    // (b,d,ty) * (tx,ty,1);
    r.ty = (this.b * m.tx) + (this.d * m.ty) + this.ty;

    this.a = r.a;
    this.b = r.b;
    this.c = r.c;
    this.d = r.d;
    this.tx = r.tx;
    this.ty = r.ty;

    return this;

  };

  /**
   * Includes parameters for scaling, rotation, and translation.
   * createBox(scaleX:Number, scaleY:Number, rotation:Number = 0, tx:Number = 0,
   * ty:Number = 0):void
   *
   * @param float scaleX
   * @param float scaleY
   * @param float rotation
   * @param float tx
   * @param float ty
   *
   * @return void
   */
  Matrix.prototype.createBox = function(scaleX, scaleY, rotation, tx, ty){
    this.identity();
    this.rotate(rotation);
    this.scale(scaleX, scaleY);
    this.translate(tx, ty);
  };

  /**
   * Creates the specific style of matrix expected by the beginGradientFill() and
   * lineGradientStyle() methods of the Graphics class.
   * createGradientBox(width:Number, height:Number, rotation:Number = 0, tx:Number =
   * 0, ty:Number = 0):void
   *
   * @link {http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html#createGradientBox()}
   *
   * @param float width
   * @param float height
   * @param float rotation
   * @param float tx
   * @param float ty
   *
   * @return void
   */
  Matrix.prototype.createGradientBox = function(width, height,
                                                           rotation, tx, ty){

    // does the graphics class have to be finished first?  thinking yes.

  };

  /**
   * Given a point in the pretransform coordinate space, returns the coordinates
   * of that point after the transformation occurs.
   * deltaTransformPoint(point:Point):Point
   *
   * @link {http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html#deltaTransformPoint()}
   *
   * @param {Point} point
   *
   * @return {Point}
   */
  Matrix.prototype.deltaTransformPoint = function(point){

    var point = {
      x : ( (this.a * point.x) +
              (this.c * point.y) ),
        y : ( (this.b * point.x) +
        (this.d * point.y) )
    };

    return new Point(point.x, point.y);


  };

  /**
   * Sets each matrix property to a value that causes a null transformation.
   * identity():void
   *
   * @link {http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html#identity()}
   *
   * @return void
   */
  Matrix.prototype.identity = function(){

    // 1 0 0
    // 0 1 0
    // 0 0 1

    this.a = 1;
    this.b = 0;
    this.c = 0;
    this.d = 1;
    this.tx = 0;
    this.ty = 0;

  };


  /**
   * Performs the opposite transformation of the original matrix. invert():void
   *
   * Math below taken straight from Wikipedia:
   * http://en.wikipedia.org/wiki/Matrix_inversion#Methods_of_matrix_inversion
   *
   * @link {http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html#invert()}
   *
   * @return void
   */
  Matrix.prototype.invert = function(){

    var determinant = this.a * this.d - this.b * this.c;
      return new Matrix(
        this.d / determinant,
        -this.b / determinant,
        -this.c / determinant,
        this.a / determinant,
        (this.c * this.ty - this.d * this.tx) / determinant,
        (this.b * this.tx - this.a * this.ty) / determinant
      );

    // AA-1 = A-1A = I
    // Flash Matrix -> Wikipedia Matrix -> Inverted Matrix
    // a c tx       -> a b c            -> A D G
    // b d ty       -> d e f            -> B E H
    // 0 0 1        -> g h k            -> C F K

    // Notice we do not need F or K
    // C is necessary to test for invertability
    var A = (this.d * 1) - (this.ty * 0),
        B = (this.ty * 0) - (1 * this.b),
        C = (this.b * 0) - (this.d_ * 0),
        D = (this.tx * 0) - (this.c * 1),
        E = (this.a * 1) - (this.tx * 0),
        G = (this.c * this.ty) - (this.tx * this.d),
        H = (this.tx * this.b) - (this.a * this.ty);

    // Z = a(ek âˆ’ fh) + b(fg âˆ’ kd) + c(dh âˆ’ eg)
    // Test to see if this Matrix is invertable
    if(((this.a * A) + (this.c * B) + (this.tx * C)) <= 0) { // not invertable
      return null;
    }

    return new Matrix(A, B, D, E, G, H);

  };

  /**
   * Applies a rotation transformation to the Matrix object.
   * rotate(angle:Number):void
   *
   * http://en.wikipedia.org/wiki/Rotation_matrix
   *
   * @link {http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html#rotate()}
   *
   * @return void
   *
   */
  Matrix.prototype.rotate = function(r){

    var m = new Matrix(Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0);
    this.concat(m);

  };

  Matrix.prototype.getRotation = function(degrees) {
    var scale = Math.sqrt(this.a*this.a + this.b*this.b);

    // arc sin, convert from radians to degrees, round
    // DO NOT USE: see update below
    var sin = this.b/scale;

    var radians = Math.asin(sin);

    if(degrees) {
      return Math.round(radians * (180/Math.PI));
    }
    return radians;
  };

  Matrix.prototype.degToRad = function(deg) {
    return deg * (Math.PI / 180);
  };

  Matrix.prototype.radToDeg = function(rad) {
    return rad * (180/Math.PI);
  };

  /**
   * Applies a scaling transformation to the matrix. scale(sx:Number,
   * sy:Number):void
   *
   * @link http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html#scale()}
   *
   * @param float sx
   * @param float sy
   *
   * @return void
   *
   */
  Matrix.prototype.scale = function(sx, sy){

    var m = new Matrix(sx, 0, 0, sy, 0, 0);
    this.concat(m);

  };

  /**
   * Returns a text value listing the properties of the Matrix object.
   * toString():String
   *
   * @link {http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html#toString()}
   *
   * @return string
   *
   * TODO: need to test this override...see if it works.
   */
  Matrix.prototype.toString = function(){

    return this.a + ' ' + this.c + ' ' + this.tx + '\n' +
           this.b + ' ' + this.d + ' ' + this.ty;

  };


  /**
   * Returns the result of applying the geometric transformation represented by
   * the Matrix object to the specified point. transformPoint(point:Point):Point
   *
   * Converts a local point to a world (matrix) point.
   *
   * @link {http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html#transformPoint()}
   *
   * @param {Point} point
   *
   * @return {Point}
   *
   */
  Matrix.prototype.transformPoint = function(point){

    // WARNING: DOUBLE CHECK MATH!!  been awhile since knocking these out
    // in my linear algebra class
    var point = {
      x : ( (this.a * point.x) +
            (this.c * point.y) +
            (this.tx) ),
      y : ( (this.b * point.x) +
            (this.d * point.y) +
            (this.ty) )
    };

    return new Point(point.x, point.y);

  };

  /**
   * Translates the matrix along the x and y axes, as specified by the dx and dy
   * parameters. translate(dx:Number, dy:Number):void
   *
   * @link {http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/Matrix.html#translate()}
   *
   * @param float dx
   * @param float dy
   *
   * @return void
   *
   */
  Matrix.prototype.translate = function(dx, dy){

    this.tx += dx;
    this.ty += dy;

  };

  Matrix.prototype.toMatrixString = function () {
    return 'matrix(' + this.a.toFixed(20) + ', ' +
      this.b.toFixed(20) + ', ' +
      this.c.toFixed(20) + ', ' +
      this.d.toFixed(20) + ', ' +
      this.tx.toFixed(20) + ', ' +
      this.ty.toFixed(20) + ')';
  };

  Matrix.initWithElem = function ($elem) {
    var tr = $elem.css('-webkit-transform');
    var m = new Matrix();

    try {
      var v = tr.split('(')[1].split(')')[0].split(',');
      m = new Matrix(Number(v[0]),
      Number(v[1]),
      Number(v[2]),
      Number(v[3]),
      Number(v[4]),
      Number(v[5]));
    } catch (e) {}

    return m;
  };