var Matrix = function (w, h, scale, container, bullpen)
{
  // parameter normalize
  w = w || Matrix.DEFAULT_WIDTH;
  h = h || Matrix.DEFAULT_HEIGHT;
  scale = scale || Matrix.DEFAULT_SCALE;

  // instance varibales
  this.buffer             = [];
  this.bullpen            = bullpen;
  this.container          = container;
  this.isContainerShowing = false;
  this.pointer            = 0;
  this.pixScale           = scale;

  // initialize buffer
  var c;
  for (var i = 0; i < Matrix.MAX_BUFFERS; ++i)
  {
    c = Matrix.buildCanvas(this.bullpen, w, h);
    this.buffer[i] = c;
    this.container.appendChild(Matrix.buildCanvasWrapper(c));
  }
}


// STATICs
// ________________________________________________________________________


Matrix.MAX_BUFFERS    = 2;
Matrix.DEFAULT_WIDTH  = 300;
Matrix.DEFAULT_HEIGHT = 300;
Matrix.DEFAULT_SCALE  = 30;

Matrix.buildCanvasWrapper = function (canvas)
{
  var d = document.createElement('div');
  d.appendChild(canvas);
  return d;
}


Matrix.buildCanvas = function (bullpen, w, h)
{
  var c = document.createElement('CANVAS');
  if (document.all && !window.opera)
  {
    // this process is needed only by Internet Explorer
    // with using excanvas.js
    bullpen.appendChild(c);
    c = G_vmlCanvasManager.initElement(c);
    bullpen.removeChild(c);
  }
  c.width             = w;
  c.height            = h;
  c.style.position    = 'absolute';
  c.style.width       = w + 'px';
  c.style.height      = h + 'px';
  c.style.top         = 0;
  c.style.left        = 0;
  c.style.overflow    = 'hidden';
  c.style.visibility  = 'hidden';
  return c;
}


// SCRATCH PADS
// _______________________________________________________________________






















// INITIALIZE METHODS
// _______________________________________________________________________


Matrix.prototype.initCanvas = function (index)
{
  var oldNode = this.buffer[index];
  var newNode = Matrix.buildCanvas(this.bullpen, oldNode.width, oldNode.height);
  oldNode.parentNode.replaceChild(newNode, oldNode);
  this.buffer[index] = newNode;
}


// VIEW METHODS
// _______________________________________________________________________


Matrix.prototype.erase = function (x, y, c, canvas)
{
  canvas = canvas || this.getCurrentCanvas();
  var context = canvas.getContext('2d');
  context.fillStyle = 'rgba(255, 255, 255, 1)';
  context.fillRect(x*this.pixScale, y*this.pixScale, this.pixScale, this.pixScale);
}


Matrix.prototype.plot = function (x, y, c, canvas)
{
  canvas = canvas || this.getCurrentCanvas();
  var context = canvas.getContext('2d');
  context.fillStyle = 'rgba(255, 0, 0, 1)';
  context.fillRect(x*this.pixScale, y*this.pixScale, this.pixScale, this.pixScale);
}


Matrix.prototype.show = function ()
{
  this.container.style.display = 'block';
  this.getCurrentCanvas().style.visibility = 'visible';
}


Matrix.prototype.turn = function ()
{
  var old = this.pointer;
  this.getCurrentCanvas().style.visibility = 'hidden';
  //console.log('aging: ' + this.pointer);
  if (++this.pointer >= this.buffer.length)
  {
    this.pointer = 0;
  }
  this.getCurrentCanvas().style.visibility = 'visible';
  this.initCanvas(old);
}


// UTILITY METHODS
// _______________________________________________________________________


Matrix.prototype.getCurrentCanvas = function ()
{
  return this.buffer[this.pointer];
}


Matrix.prototype.getNextCanvas = function ()
{
  var result;
  if (this.pointer >= (this.buffer.length - 1))
  {
    result = this.buffer[0];
  }
  else
  {
    result = this.buffer[(this.pointer + 1)];
  }
  return result;
}



