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;
  this.eventReciver       = Matrix.buildEventReceiver(this.container);


  // 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.buildEventReceiver = function (container)
{
  var d = document.createElement('div');
  var dim = MochiKit.Style.getElementDimensions(container);
  var borderL = parseInt(MochiKit.Style.getStyle(container, 'border-left-width'));
  var borderR = parseInt(MochiKit.Style.getStyle(container, 'border-right-width'));
  var borderT = parseInt(MochiKit.Style.getStyle(container, 'border-top-width'));
  var borderB = parseInt(MochiKit.Style.getStyle(container, 'border-bottom-width'));
  var zIndex  = parseInt(MochiKit.Style.getStyle(container, 'z-index'));
  var style   = d.style;
  style.position  = 'absolute';
  style.width     = (dim.w - borderL - borderR) + 'px';
  style.height    = (dim.h -　borderT - borderB) + 'px';
  style.left      = '0px';
  style.top       = '0px';
  style.zIndex    = zIndex + 100;

  style.backgroundColor = '#FFFFFF';
  MochiKit.Style.setOpacity(d, 0);
  //style.opacity = '.0';

  container.appendChild(d);
  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
// _______________________________________________________________________


Matrix.prototype.setOnClick = function (func)
{
  MochiKit.Signal.connect(this.eventReciver, 'onclick', func);
}



















// 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)
{
  c = c || [255, 255, 255, 1];
  canvas = canvas || this.getCurrentCanvas();
  var context = canvas.getContext('2d');
  context.fillStyle = 'rgba(' + c.join(',') + ')';
  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;
}



