module Mittsu
  class Matrix3
    attr_accessor :elements

    DIMENSIONS = 3

    def initialize()
      @elements = [
        1.0, 0.0, 0.0,
        0.0, 1.0, 0.0,
        0.0, 0.0, 1.0
      ]
    end

    def set(n11, n12, n13, n21, n22, n23, n31, n32, n33)
      te = self.elements
      te[0] = n11.to_f; te[3] = n12.to_f; te[6] = n13.to_f
      te[1] = n21.to_f; te[4] = n22.to_f; te[7] = n23.to_f
      te[2] = n31.to_f; te[5] = n32.to_f; te[8] = n33.to_f
      self
    end

    def identity
      self.set(
        1.0, 0.0, 0.0,
        0.0, 1.0, 0.0,
        0.0, 0.0, 1.0
      )
      self
    end

    def copy(m)
      me = m.elements
      self.set(
        me[0], me[3], me[6],
        me[1], me[4], me[7],
        me[2], me[5], me[8]
      )
      self
    end

    def apply_to_vector3_array(array, offset = 0, length = array.length)
      v1 = Mittsu::Vector3.new
      i, j = 0, offset
      while i < length
        v1.x = array[j]
        v1.y = array[j + 1]
        v1.z = array[j + 2]
        v1.apply_matrix3(self)
        array[j]     = v1.x
        array[j + 1] = v1.y
        array[j + 2] = v1.z
        i += 3
        j += 3
      end
      array
    end

    def multiply_scalar(s)
      te = self.elements
      te[0] *= s; te[3] *= s; te[6] *= s
      te[1] *= s; te[4] *= s; te[7] *= s
      te[2] *= s; te[5] *= s; te[8] *= s
      self
    end

    def determinant
      a, b, c, d, e, f, g, h, i = *self.elements
      a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g
    end

    def inverse(matrix, throw_on_invertible = false)
      # input: Mittsu::Matrix4
      # (based on http:#code.google.com/p/webgl-mjs/)
      me = matrix.elements
      te = self.elements
      te[0] =   me[10] * me[5] - me[6] * me[9]
      te[1] = - me[10] * me[1] + me[2] * me[9]
      te[2] =   me[6] * me[1] - me[2] * me[5]
      te[3] = - me[10] * me[4] + me[6] * me[8]
      te[4] =   me[10] * me[0] - me[2] * me[8]
      te[5] = - me[6] * me[0] + me[2] * me[4]
      te[6] =   me[9] * me[4] - me[5] * me[8]
      te[7] = - me[9] * me[0] + me[1] * me[8]
      te[8] =   me[5] * me[0] - me[1] * me[4]
      det = me[0] * te[0] + me[1] * te[3] + me[2] * te[6]
      # no inverse
      if det.zero?
        msg = "Mittsu::Matrix3#inverse: can't invert matrix, determinant is 0"
        if throw_on_invertible
          raise Error.new(msg)
        else
          puts "WARNING: #{msg}"
          # THREE.warn(msg)
        end
        self.identity
        return self
      end
      self.multiply_scalar(1.0 / det)
      self
    end

    def transpose
      m = self.elements
      tmp = m[1]; m[1] = m[3]; m[3] = tmp
      tmp = m[2]; m[2] = m[6]; m[6] = tmp
      tmp = m[5]; m[5] = m[7]; m[7] = tmp
      self
    end

    def flatten_to_array_offset(array, offset)
      te = self.elements
      array[offset    ] = te[0]
      array[offset + 1] = te[1]
      array[offset + 2] = te[2]
      array[offset + 3] = te[3]
      array[offset + 4] = te[4]
      array[offset + 5] = te[5]
      array[offset + 6] = te[6]
      array[offset + 7] = te[7]
      array[offset + 8]  = te[8]
      array
    end

    def normal_matrix(m)
      # input: THREE.Matrix4
      self.inverse(m).transpose
      self
    end

    def transpose_into_array(r)
      m = self.elements
      r[0] = m[0]
      r[1] = m[3]
      r[2] = m[6]
      r[3] = m[1]
      r[4] = m[4]
      r[5] = m[7]
      r[6] = m[2]
      r[7] = m[5]
      r[8] = m[8]
      self
    end

    def ==(other)
      other.elements == @elements
    end

    def from_array(array)
      self.elements[0..array.length] = (array)
      self
    end

    def to_a
      te = self.elements
      [
        te[0], te[1], te[2],
        te[3], te[4], te[5],
        te[6], te[7], te[8]
      ]
    end

    def clone
      Mittsu::Matrix3.new.from_array(self.elements)
    end

  end
end
