/*
 * Copyright (c) Joshua John Lawrence 2012, all rights reserved.
 *
 * This file is hardly complete for a graphics library, but overkill for the
 * task this demo performs. I've written it in the style I would use in a real
 * product, expecting it to grow with reuse and needing it to be maintainable.
 */

/*
 * A color value with alpha defaulting to fully opaque. Immutable.
 *
 * All arguments should be numeric. If they have fractional parts, they will
 * be truncated.
 */
function Color( red, green, blue, alpha ) {
  red = Math.floor( red );
  green = Math.floor( green );
  blue = Math.floor( blue );
  if( alpha === undefined ) { alpha = 255; }
  alpha = Math.floor( alpha );

  this.getRed = function() { return red; };
  this.getGreen = function() { return green; };
  this.getBlue = function() { return blue; };
  this.getAlpha = function() { return alpha; };
}

Color.BLACK = new Color( 0, 0, 0 );

/*
 * A simple wrapper around a 2d canvas context, to allow easy pixel manipulation.
 */
function PixelCanvas( context ) {

  /*
   * Set the pixel at position p to the color.
   *
   * v should be a Vector whose ordinates will be truncated, and color a Color.
   */
  this.drawPixel = function( v, color ) {
    var x = Math.floor( v.getX() );
    var y = Math.floor( v.getY() );
  	var offset = 4 * ( y * this.getWidth() + x );
  	imageData.data[ offset + 0 ] = color.getRed();
  	imageData.data[ offset + 1 ] = color.getGreen();
  	imageData.data[ offset + 2 ] = color.getBlue();
  	imageData.data[ offset + 3 ] = color.getAlpha();
  };

  /*
   * Redraw the canvas the next time the browser updates the screen.
   */
  this.paint = function() {
    context.putImageData( imageData, 0, 0 );
  };

  /*
   * The width of this canvas.
   */
  this.getWidth = function() {
    return context.canvas.width;
  };

  /*
   * The height of this canvas.
   */
  this.getHeight = function() {
    return context.canvas.height;
  };

  // After getWidth and getHeight are defined.
  var imageData = context.getImageData( 0, 0, this.getWidth(), this.getHeight() );
}

/*
 * To transform between world and screen co-ordinates. All arguments are Vectors. Immutable.
 *
 * Screen left and screen right cannot be equal. Likewise screen top and screen bottom.
 */
function Transform( worldBottomLeft, worldTopRight, screenBottomLeft, screenTopRight ) {
  var scaleX = ( worldTopRight.getX() - worldBottomLeft.getX() ) /
               ( screenTopRight.getX() - screenBottomLeft.getX() );
  var scaleY = ( worldTopRight.getY() - worldBottomLeft.getY() ) /
               ( screenTopRight.getY() - screenBottomLeft.getY() );
  var m = new Matrix( scaleX, 0, 0, scaleY );
  var offset = new Vector(
    worldBottomLeft.getX() - scaleX * screenBottomLeft.getX(),
    worldBottomLeft.getY() - scaleY * screenBottomLeft.getY()
  );

  /*
   * Takes a Vector in screen co-ordinates, returns a Vector in world co-ordinates.
   */
  this.screenToWorld = function( v ) {
    return m.times( v ).plus( offset );
  };
}