//
// Matrix4 class corresponds to sandy Matrix4
//

class Matrix4 {
  static public function fromString( s:String ):Matrix4 {
    var sa:Array< String > = s.split( " " );
    var ret:Matrix4 = new Matrix4();
    if ( sa.length == 0 ) return( ret );
    ret.m11 = Std.parseFloat( sa[0] );
    if ( sa.length == 1 ) return( ret );
    ret.m12 = Std.parseFloat( sa[1] );
    if ( sa.length == 2 ) return( ret );
    ret.m13 = Std.parseFloat( sa[2] );
    if ( sa.length == 3 ) return( ret );
    ret.m14 = Std.parseFloat( sa[3] );
    if ( sa.length == 4 ) return( ret );
    ret.m21 = Std.parseFloat( sa[4] );
    if ( sa.length == 5 ) return( ret );
    ret.m22 = Std.parseFloat( sa[5] );
    if ( sa.length == 6 ) return( ret );
    ret.m23 = Std.parseFloat( sa[6] );
    if ( sa.length == 7 ) return( ret );
    ret.m24 = Std.parseFloat( sa[7] );
    if ( sa.length == 8 ) return( ret );
    ret.m31 = Std.parseFloat( sa[8] );
    if ( sa.length == 9 ) return( ret );
    ret.m32 = Std.parseFloat( sa[9] );
    if ( sa.length == 10 ) return( ret );
    ret.m33 = Std.parseFloat( sa[10] );
    if ( sa.length == 11 ) return( ret );
    ret.m34 = Std.parseFloat( sa[11] );
    if ( sa.length == 12 ) return( ret );
    ret.m41 = Std.parseFloat( sa[12] );
    if ( sa.length == 13 ) return( ret );
    ret.m42 = Std.parseFloat( sa[13] );
    if ( sa.length == 14 ) return( ret );
    ret.m43 = Std.parseFloat( sa[14] );
    if ( sa.length == 15 ) return( ret );
    ret.m44 = Std.parseFloat( sa[15] );
    return( ret );
  }

  // ###################################################################

  public var m11( __get11, __set11 ):Float;
    public function __get11():Float { return( m11 ); }
    public function __set11( v:Float ):Float { m11 = v; return( m11 ); }
  public var m12( __get12, __set12 ):Float;
    public function __get12():Float { return( m12 ); }
    public function __set12( v:Float ):Float { m12 = v; return( m12 ); }
  public var m13( __get13, __set13 ):Float;
    public function __get13():Float { return( m13 ); }
    public function __set13( v:Float ):Float { m13 = v; return( m13 ); }
  public var m14( __get14, __set14 ):Float;
    public function __get14():Float { return( m14 ); }
    public function __set14( v:Float ):Float { m14 = v; return( m14 ); }
  public var m21( __get21, __set21 ):Float;
    public function __get21():Float { return( m21 ); }
    public function __set21( v:Float ):Float { m21 = v; return( m21 ); }
  public var m22( __get22, __set22 ):Float;
    public function __get22():Float { return( m22 ); }
    public function __set22( v:Float ):Float { m22 = v; return( m22 ); }
  public var m23( __get23, __set23 ):Float;
    public function __get23():Float { return( m23 ); }
    public function __set23( v:Float ):Float { m23 = v; return( m23 ); }
  public var m24( __get24, __set24 ):Float;
    public function __get24():Float { return( m24 ); }
    public function __set24( v:Float ):Float { m24 = v; return( m24 ); }
  public var m31( __get31, __set31 ):Float;
    public function __get31():Float { return( m31 ); }
    public function __set31( v:Float ):Float { m31 = v; return( m31 ); }
  public var m32( __get32, __set32 ):Float;
    public function __get32():Float { return( m32 ); }
    public function __set32( v:Float ):Float { m32 = v; return( m32 ); }
  public var m33( __get33, __set33 ):Float;
    public function __get33():Float { return( m33 ); }
    public function __set33( v:Float ):Float { m33 = v; return( m33 ); }
  public var m34( __get34, __set34 ):Float;
    public function __get34():Float { return( m34 ); }
    public function __set34( v:Float ):Float { m34 = v; return( m34 ); }
  public var m41( __get41, __set41 ):Float;
    public function __get41():Float { return( m41 ); }
    public function __set41( v:Float ):Float { m41 = v; return( m41 ); }
  public var m42( __get42, __set42 ):Float;
    public function __get42():Float { return( m42 ); }
    public function __set42( v:Float ):Float { m42 = v; return( m42 ); }
  public var m43( __get43, __set43 ):Float;
    public function __get43():Float { return( m43 ); }
    public function __set43( v:Float ):Float { m43 = v; return( m43 ); }
  public var m44( __get44, __set44 ):Float;
    public function __get44():Float { return( m44 ); }
    public function __set44( v:Float ):Float { m44 = v; return( m44 ); }

  // ###################################################################

  public function new() {
    identity();
  }

  public function clone():Matrix4 {
    var ret:Matrix4 = new Matrix4();
    ret.m11 = m11;
    ret.m12 = m12;
    ret.m13 = m13;
    ret.m14 = m14;
    ret.m21 = m21;
    ret.m22 = m22;
    ret.m23 = m23;
    ret.m24 = m24;
    ret.m31 = m31;
    ret.m32 = m32;
    ret.m33 = m33;
    ret.m34 = m34;
    ret.m41 = m41;
    ret.m42 = m42;
    ret.m43 = m43;
    ret.m44 = m44;
    return( ret );
  }

  public function clear():Void {
    m11 = m12 = m13 = m14 = 0.0;
    m21 = m22 = m23 = m24 = 0.0;
    m31 = m32 = m33 = m34 = 0.0;
    m41 = m42 = m43 = m44 = 0.0;
  }

  public function identity():Void {
    clear();
    m11 = 1.0;
    m22 = 1.0;
    m33 = 1.0;
    m44 = 1.0;
  }

  public function copy( m:Matrix4 ):Void {
    m11 = m.m11;
    m12 = m.m12;
    m13 = m.m13;
    m14 = m.m14;
    m21 = m.m21;
    m22 = m.m22;
    m23 = m.m23;
    m24 = m.m24;
    m31 = m.m31;
    m32 = m.m32;
    m33 = m.m33;
    m34 = m.m34;
    m41 = m.m41;
    m42 = m.m42;
    m43 = m.m43;
    m44 = m.m44;
  }

  //   1.0  0.0  0.0  0.0
  //   0.0  cos  sin  0.0
  //   0.0 -sin  cos  0.0
  //   0.0  0.0  0.0  1.0
  public function rotX( x:Float ):Void {
    var cos = Math.cos( x );
    var sin = Math.sin( x );
    m11 = m44 = 1.0;
    m12 = m13 = m14 = m21 = m24 = m31 = m34 = m41 = m42 = m43 = 0.0;
    m22 = m33 = cos;
    m23 = sin;
    m32 = -sin;
  }

  //   cos  0.0 -sin  0.0
  //   0.0  1.0  0.0  0.0
  //   sin  0.0  cos  0.0
  //   0.0  0.0  0.0  1.0
  public function rotY( y:Float ):Void {
    var cos = Math.cos( y );
    var sin = Math.sin( y );
    m22 = m44 = 1.0;
    m12 = m14 = m21 = m23 = m24 = m32 = m34 = m41 = m42 = m43 = 0.0;
    m11 = m33 = cos;
    m13 = -sin;
    m31 = sin;
  }

  //   cos  sin  0.0  0.0
  //  -sin  cos  0.0  0.0
  //   0.0  0.0  1.0  0.0
  //   0.0  0.0  0.0  1.0
  public function rotZ( z:Float ):Void {
    var cos = Math.cos( z );
    var sin = Math.sin( z );
    m33 = m44 = 1.0;
    m13 = m14 = m23 = m24 = m31 = m32 = m34 = m41 = m42 = m43 = 0.0;
    m11 = m22 = cos;
    m12 = sin;
    m21 = -sin;
  }

  //   1.0  0.0  0.0  0.0
  //   0.0  1.0  0.0  0.0
  //   0.0  0.0  1.0  0.0
  //    x    y    z   1.0
  public function trans( x:Float,
                         y:Float,
                         z:Float ):Void {
    m11 = m22 = m33 = m44 = 1.0;
    m12 = m13 = m14 = m21 = m23 = m24 = m31 = m32 = m34 = 0.0;
    m41 = x;
    m42 = y;
    m43 = z;
  }

  public function transByPoint3D( p:Point3D ):Void {
    m11 = m22 = m33 = m44 = 1.0;
    m12 = m13 = m14 = m21 = m23 = m24 = m31 = m32 = m34 = 0.0;
    m41 = p.x;
    m42 = p.y;
    m43 = p.z;
  }

  public function addTrans( x:Float,
                            y:Float,
                            z:Float ):Void {
    m41 += x;
    m42 += y;
    m43 += z;
  }

  public function addTransByPoint3D( p:Point3D ):Void {
    m41 += p.x;
    m42 += p.x;
    m43 += p.x;
  }

  public function multiply( a:Matrix4 ):Void {
    multiply3( a );
  }

  public function multiply3( a:Matrix4 ):Void {
    var _m11 = m11;
    var _m12 = m12;
    var _m13 = m13;

    m11 = _m11 * a.m11 + _m12 * a.m21 + _m13 * a.m31;
    m12 = _m11 * a.m12 + _m12 * a.m22 + _m13 * a.m32;
    m13 = _m11 * a.m13 + _m12 * a.m23 + _m13 * a.m33;

    var _m21 = m21;
    var _m22 = m22;
    var _m23 = m23;

    m21 = _m21 * a.m11 + _m22 * a.m21 + _m23 * a.m31;
    m22 = _m21 * a.m12 + _m22 * a.m22 + _m23 * a.m32;
    m23 = _m21 * a.m13 + _m22 * a.m23 + _m23 * a.m33;

    var _m31 = m31;
    var _m32 = m32;
    var _m33 = m33;

    m31 = _m31 * a.m11 + _m32 * a.m21 + _m33 * a.m31;
    m32 = _m31 * a.m12 + _m32 * a.m22 + _m33 * a.m32;
    m33 = _m31 * a.m13 + _m32 * a.m23 + _m33 * a.m33;

    m41 = m42 = m43 = 0.0;
    m44 = 1.0;
  }

  // may not be used in wm3d
  public function multiply4( a:Matrix4 ):Void {
    var _m11 = m11;
    var _m12 = m12;
    var _m13 = m13;
    var _m14 = m14;

    m11 = _m11 * a.m11 + _m12 * a.m21 + _m13 * a.m31 + _m14 * a.m41;
    m12 = _m11 * a.m12 + _m12 * a.m22 + _m13 * a.m32 + _m14 * a.m42;
    m13 = _m11 * a.m13 + _m12 * a.m23 + _m13 * a.m33 + _m14 * a.m43;
    m14 = _m11 * a.m14 + _m12 * a.m24 + _m13 * a.m34 + _m14 * a.m44;

    var _m21 = m21;
    var _m22 = m22;
    var _m23 = m23;
    var _m24 = m24;

    m21 = _m21 * a.m11 + _m22 * a.m21 + _m23 * a.m31 + _m24 * a.m41;
    m22 = _m21 * a.m12 + _m22 * a.m22 + _m23 * a.m32 + _m24 * a.m42;
    m23 = _m21 * a.m13 + _m22 * a.m23 + _m23 * a.m33 + _m24 * a.m43;
    m24 = _m21 * a.m14 + _m22 * a.m24 + _m23 * a.m34 + _m24 * a.m44;

    var _m31 = m31;
    var _m32 = m32;
    var _m33 = m33;
    var _m34 = m34;

    m31 = _m31 * a.m11 + _m32 * a.m21 + _m33 * a.m31 + _m34 * a.m41;
    m32 = _m31 * a.m12 + _m32 * a.m22 + _m33 * a.m32 + _m34 * a.m42;
    m33 = _m31 * a.m13 + _m32 * a.m23 + _m33 * a.m33 + _m34 * a.m43;
    m34 = _m31 * a.m14 + _m32 * a.m24 + _m33 * a.m34 + _m34 * a.m44;

    var _m41 = m41;
    var _m42 = m42;
    var _m43 = m43;
    var _m44 = m44;

    m41 = _m41 * a.m11 + _m42 * a.m21 + _m43 * a.m31 + _m44 * a.m41;
    m42 = _m41 * a.m12 + _m42 * a.m22 + _m43 * a.m32 + _m44 * a.m42;
    m43 = _m41 * a.m13 + _m42 * a.m23 + _m43 * a.m33 + _m44 * a.m43;
    m44 = _m41 * a.m14 + _m42 * a.m24 + _m43 * a.m34 + _m44 * a.m44;
  }

  public function toString():String {
    return( " [ " + m11 + " " + m12 + " " + m13 + " " + m14 + " ]\n" +
            " [ " + m21 + " " + m22 + " " + m23 + " " + m24 + " ]\n" +
            " [ " + m31 + " " + m32 + " " + m33 + " " + m34 + " ]\n" +
            " [ " + m41 + " " + m42 + " " + m43 + " " + m44 + " ]" );
  }

  public function toMatrix3D():flash.geom.Matrix3D {
    return( new flash.geom.Matrix3D(
               flash.Vector.ofArray([
                 m11, m12, m13, m14,
                 m21, m22, m23, m24,
                 m31, m32, m33, m34,
                 m41, m42, m43, m44,
               ])));
  }
}
