
var PicWidth = 256;
var PicHeight = 256;

var ValueMap = new Array( PicWidth * PicHeight );

var DoCalc = 1;

//globals
var canvas, c, canvastemp, ctemp;

var Zooming = 0;
var ZoomStart = null;

//co-ords of the view
var ViewCoords = new Rect( -1.5, -1, 2, 2 );

var maxIterations = 25;

var setBounds = 4;

//list of rect's to work on
var WorkList = new Array();

var MaxCol = 16777215;

var ResolutionColLimit = 1;

//-------------------------------------------
//rect object
function Rect(x,y,width,height) {
	this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;

  this.top = y;
  this.bottom = y + height;
  this.left = x;
  this.right = x + width;

  this.area = function()
  {
    return this.height * this.width;
  }
};

//utility
function max( x, y )
{
  var ret;
  if( x > y )
  {
    ret = x;
  }
  else
  {
    ret = y;
  }

  return ret;
}

function min( x, y )
{
  var ret;
  if( x < y )
  {
    ret = x;
  }
  else
  {
    ret = y;
  }

  return ret;
}


//return colour for given x y
function fractalFunc( x0, y0 )
{
  //lookup
  var index = y0 * PicWidth + x0;
  if( ValueMap[ index ] )
  {
    return ValueMap[ index ]
  }

  var x = x0;
  var y = y0;

  var x2 = x*x;
  var y2 = y*y;

  var iter = 0;

  while( x2 + y2 < setBounds && iter < maxIterations )
  {
    y = 2*x*y + y0;
    x = x2 - y2 + x0;

    x2 = x * x;
    y2 = y * y;

    iter = iter + 1;
  }

  var col = MaxCol * iter / maxIterations;

  ValueMap[ index ] = col;

  return col;
}

//user set coords
function newCoords()
{
  var newx = parseFloat( document.controls.newXMin.value );
  var newy = parseFloat( document.controls.newYMin.value );
  var newwidth = parseFloat( document.controls.newXMax.value ) - newx;
  var newheight = parseFloat( document.controls.newYMax.value ) - newy;

  ViewCoords = new Rect( newx, newy, newwidth, newheight );
  resetCoords();
}

//draw a rect on the canvas
function drawRect( rect, col )
{
  c.strokeStyle = col;
  c.fillStyle = col;

  var hratio = PicHeight / ViewCoords.height;
  var wratio = PicWidth / ViewCoords.width;

  var left = Math.round( ( rect.left - ViewCoords.left ) * wratio );
  var width = Math.round( ( rect.right - rect.left) * wratio );
  var top = Math.round( ( rect.top - ViewCoords.top ) *  hratio );
  var height = Math.round( ( rect.bottom - rect.top ) * hratio );

  c.fillRect( left, top, width, height );

  //return width height
  return { w: width, h: height };
}

function SplitRectAddToWork( rect )
{
  var halfwidth = rect.width / 2;
  var halfheight = rect.height / 2;

  WorkList.splice( 0, 0, new Rect( rect.left, rect.top, halfwidth, halfheight ) );
  WorkList.splice( 0, 0, new Rect( rect.left + halfwidth, rect.top, halfwidth, halfheight ) );
  WorkList.splice( 0, 0, new Rect( rect.left, rect.top + halfheight, halfwidth, halfheight ) );
  WorkList.splice( 0, 0, new Rect( rect.left + halfwidth, rect.top + halfheight, halfwidth, halfheight ) );
}

function colFromInt( col )
{
  var colstring = col.toString( 16 );

  while( colstring.length < 6 )
  {
    colstring = '0' + colstring;
  }

  return "#" + colstring;
}
//do some work
function processWork()
{
  if( WorkList.length != 0  && DoCalc )
  {
    rect = WorkList[ WorkList.length - 1 ];

    //do points
    var topleft = fractalFunc( rect.left, rect.top );
    var topright = fractalFunc( rect.right, rect.top );
    var botleft = fractalFunc( rect.left, rect.bottom );
    var botright = fractalFunc( rect.right, rect.bottom );
    var middle = fractalFunc( rect.left + rect.width / 2, rect.top + rect.height /2 );

    //average colour
    var avg =  ( topleft + topright + middle + botleft + botright ) / 5.0;

    //draw for now
    size = drawRect( rect, colFromInt( Math.floor( avg ) ) );

    //remove rect from list
    WorkList.splice( WorkList.length - 1, 1 );

    //is this okay?
    var maxDiff = Math.abs( topleft - avg );
    maxDiff = max( Math.abs( topright - avg ), maxDiff );
    maxDiff = max( Math.abs( botleft - avg ), maxDiff );
    maxDiff = max( Math.abs( botright - avg ), maxDiff );
    maxDiff = max( Math.abs( middle - avg ), maxDiff );


    if( maxDiff > ResolutionColLimit && ! ( size.w <= 1 && size.h <= 1 ) )
    {
      //split it up and add to work queue
      SplitRectAddToWork( rect );
    }

    //check for adjancents


    if( WorkList.length > 0 )
    {
      setTimeout( "processWork()", 1 );
    }
  }
}

function displayCoords()
{
  var xmin = min( ViewCoords.x, ViewCoords.x + ViewCoords.width );
  var xmax = max( ViewCoords.x, ViewCoords.x + ViewCoords.width );
  var ymin = min( ViewCoords.y, ViewCoords.y + ViewCoords.height );
  var ymax = max( ViewCoords.y, ViewCoords.y + ViewCoords.height );

  document.controls.newXMin.value = xmin.toString();
  document.controls.newXMax.value = xmax.toString();
  document.controls.newYMin.value = ymin.toString();
  document.controls.newYMax.value = ymax.toString();
}

function resetCoords()
{
  //clear worklist, value map
  WorkList = new Array();
  ValueMap = new Object();

  //start worklist again
  SplitRectAddToWork( ViewCoords );

  //display co-ords
  displayCoords();

  processWork();
}

function onLoad()
{
  canvas = document.getElementById('canvas');
  canvastemp = document.getElementById('canvastemp');

 //repos
 if(canvas.getContext)
 {
   c = canvas.getContext("2d");
   ctemp = canvastemp.getContext("2d");

   //set up defaults
   ctemp.lineWidth = c.lineWidth = 1;
   ctemp.strokeStyle = c.strokeStyle = '#000';
   ctemp.fillStyle = c.fillStyle = '#FFF';

   //c.tertStyle = '#DDD';
   //c.strokeFill = 1; //outline shapes

   //set up events
   window.onmousemove = bodyMove;
   window.onmouseup = c_up;

   canvas.onmousedown = canvastemp.onmousedown = c_down;
   canvas.onmousemove = canvastemp.onmousemove = c_move;
   canvas.onmouseout = canvastemp.onmouseout = c_out;
   canvas.onmouseup = canvastemp.onmouseup = c_up;

   SplitRectAddToWork( ViewCoords );
   processWork();
 }

  displayCoords();
}

function getxy(e, o) {
//gets mouse position relative to object o

 if(c) {
 //IE madness
 if (!e) var e = window.event;
 var bo = getpos(o);

 var x = e.clientX - bo.x; //correct for canvas position, workspace scroll offset
 var y = e.clientY - bo.y;
 if( ! o.getBoundingClientRect ) //only correct if not IE...
 {
  x += document.documentElement.scrollLeft; //correct for window scroll offset
  y += document.documentElement.scrollTop;
 }
 x = (c.zoom) ? x/c.zoom : x; //correct for zoom
 y = (c.zoom) ? y/c.zoom : y;
 return { x: x-.5, y: y-.5 }; //-.5 prevents antialiasing of stroke lines
 }
}

function getpos(o)
{
//gets position of object o
 var bo, x, y, b; x = y = 0;
 if(document.getBoxObjectFor) { //moz
 bo = document.getBoxObjectFor(o);
 x = bo.x; y = bo.y;
 } else if (o.getBoundingClientRect) { //ie (??)
 bo = o.getBoundingClientRect(); //this is relative to screen position not including vertical scroll bar effect in y and presumably x
 x = bo.left; y = bo.top;
 } else { //opera, safari etc
 while(o && o.nodeName != 'BODY') {
 x += o.offsetLeft;
 y += o.offsetTop;
 b = parseInt(document.defaultView.getComputedStyle(o,null).getPropertyValue('border-width'));
 if(b > 0) { x += b; y +=b; }
 o = o.offsetParent;
 }
 }
 return { x:x, y:y }
}

function restore()
{
  ctemp.clearRect(0,0,canvastemp.width, canvastemp.height);
}

function save()
{
}

//store these for undo on explorer
var zleft, zwidth, ztop, zheight;

function DrawZoomRect( m )
{
  c.strokeStyle = "#ffffff";

  zleft = min( m.x, ZoomStart.x );
  zwidth = max( m.x, ZoomStart.x ) - zleft;
  ztop = min( m.y, ZoomStart.y );
  zheight = max( m.y, ZoomStart.y ) - ztop;

  ctemp.strokeRect( zleft, ztop, zwidth, zheight );
}

function c_down(e)
{
  ZoomStart = getxy(e, canvas);

  Zooming = 1;
  DoCalc = 0;

  save();

  processWork();

  return false;
}


//handles mouseup on the canvas depending on tool selected
function c_up(e)
{
  if( Zooming )
  {
    m = getxy(e, canvas);

    if( e !=null )
    {
      e.stopPropagation();
    }

    restore();

    Zooming = 0;
    DoCalc =1;

      //change view coords
    var pxleft = min( m.x, ZoomStart.x );
    var pxright = max( m.x, ZoomStart.x );
    var pxtop = min( m.y, ZoomStart.y );
    var pxbot = max( m.y, ZoomStart.y );

    var left = pxleft * ViewCoords.width / PicWidth + ViewCoords.left;
    var right = pxright * ViewCoords.width / PicWidth + ViewCoords.left;
    var top = pxtop * ViewCoords.height / PicHeight + ViewCoords.top;
    var bot = pxbot * ViewCoords.height / PicHeight + ViewCoords.top;
    ViewCoords = new Rect( left, top, right - left, bot - top );

    resetCoords();
  }

  return false;
}

function c_move(e) {
  //IE madness
  m = getxy(e, canvas);

  if( e !=null )
  {
    e.stopPropagation();
  }

  if( Zooming )
  {
    restore();
    DrawZoomRect( m );

  }

  return false;
}

function c_out(e) {
 //var source = e.currentTarget;

}


function bodyMove(e) {

  if( Zooming )
  {
    c_move(e);
  }
}


