/* Web Paint-by-Number - (c) 2009, Jan Wolter - all rights reserved */

//  Draw.js library.
//
//    $brd= new Board($cols, $rows, [$cellsize])
//
//       Define a new board object, $brd.  Give numbers of rows and columns.
//       Cellsize defaults to 10, and looks best at values 10, 12, 14 but
//       can be any value.
//
//    $brd.set($x, $y, $color)
//
//       Define the color for one cell.  $x is the column number (0 based,
//       left to right), and $y is the row number (0 based, top to bottom).
//       $color is:
//
//             'x'    unknown
//             '0'    white
//             '1'    black
//             '2'    red
//             '3'    green
//             '4'    blue
//             'A'    purple letter A
//             'B'    purple letter B
//
//    $brd.row($y, $colorstring)
//
//       Set the colors of a whole row of cells.  $y is the row number and
//       $colorstring is a string of color characters whose length is equal
//       to the row length.
//
//    $brd.topclue($x, $num1,$num2,...)
//
//       Define the clue values for a column.  $x is the column number, and
//       $num1,$num2,... are the clue values, in top to bottom order.  Clues
//       can be <number> or '<number>.<color>', so '3.4' is a blue 3.  Note
//       that the '<number>.<color>' case needs to be enclosed in quotes, but
//       the <number> case does not need to be.
//
//    $brd.sideclue($y, $num1,$num2,...)
//
//       Define the clue values for a row.  $y is the row number, and
//       $num1,$num2,... are the clue values, in left to right order.
//
//    $board.draw($id)
//
//       Draw the board.  $id is the ID attribute of a div to which the
//       board is to be attached.  This should be defined before calling
//       the draw function.

var colchar= "x01234";	// color characters passed into functions
var colpre= " wgrnu";	// prefixes used to get numbers of various colors
var colsrc= new Array('img/w.gif', '1/dot.gif',
	'img/g.gif', 'img/r.gif', 'img/n.gif', 'img/u.gif');

// clues can be like NUMBER or 'NUMBER.COLOR'.  Return format like 'g/31'.
function clueval(n)
{
    if (typeof(n) == "string")
    {
	var r= n.match(/^(\d+)\.(.)$/)
	if (r != null)
	{
	    var col= colchar.indexOf(r[2]);
	    return colpre.charAt(col)+'/'+r[1];
	}
	else
	    return null;
    }
    else
    {
	return 'g/'+n;
    }
}

// image for a color/prefix
function imagestr(pref,col)
{
    if (col == 'x') return 'img/w.gif';
    if (col == '0') return pref+'dot.gif';
    if (col >= '1' && col <= '9') return colsrc[col - 0 + 1];
    return pref+col+'.gif';
}

// board.set(x,y,color) - where color is 'x'=unknown, '0'=white,
//   '1'=black, '2'=red, '3'=green, '4'=blue
function Board_Set(x,y,color)
{
    this.grid[y][x]= color;
}

// board.row(y,string) - set string of colors as in board.set().
function Board_Row(y,string)
{
    var color;
    for (var i= 0; i < this.cols; i++)
    {
	color= string.charAt(i);
	if (color.length == 0) break;
	this.set(i,y,color);
    }
}

// board.topclue(x,num,num,...) - set a top clue for given column.
function Board_TopClue()
{
    var x= arguments[0];
    this.top[x]= new Array();
    for (var i= 1; i < arguments.length; i++)
	this.top[x][i-1]= clueval(arguments[i]);
}

// board.top(y,num,num,...) - set a side clue for given row.
function Board_SideClue()
{
    var y= arguments[0];
    this.side[y]= new Array();
    for (var i= 0; i < arguments.length; i++)
	this.side[y][i-1]= clueval(arguments[i]);
}

// board.draw() - Generate a board image
function Board_Draw(parent)
{
    var a, x, y, i, j;

    // Maximum number of side clues
    var sidenum= 0;
    for (j= 0; j < this.side.length; j++)
    {
	if (this.side[j].length > sidenum)
	    sidenum= this.side[j].length;
    }

    // Maximum number of top clues
    var topnum= 0;
    for (i= 0; i < this.top.length; i++)
	if (this.top[i].length > topnum)
	    topnum= this.top[i].length;

    // number of fat vertical internal lines
    var nvfat= Math.floor((this.cols-1)/5);
    // length of horizontal line spanning grid
    var w1= (this.block + 1) * this.cols + nvfat + 4;
    // length of horizontal side spanning side clues;
    var w2= (sidenum == 0) ? 0 : this.block * sidenum + 2;
    // total width of board
    var w3= w1 + w2;

    // number of fat horizontal internal lines
    var nhfat= Math.floor((this.rows-1)/5);
    // length of vertical line spanning grid
    var h1= (this.block + 1) * this.rows + nhfat + 4;
    // length of vertical side spanning side clues;
    var h2= (topnum == 0) ? 0 : this.block * topnum + 2;
    // total height of board
    var h3= h1 + h2;

    // container for puzzle
    var puz= document.createElement("DIV")
    puz.style.position= "relative";
    puz.width= w3+'';
    puz.height= h3+'';
    puz.style.width= w3+'px';
    puz.style.height= h3+'px';

    // Attach to the named parent object
    var pardiv= document.getElementById(parent);
    pardiv.appendChild(puz);

    // Black background for top clues
    if (topnum > 0)
    {
	a= document.createElement("IMG");
	a.src= "img/b.gif";
	a.width= w1;
	a.style.width= w1+'px';
	a.height= h2+2;
	a.style.height= (h2+2)+'px';
	a.style.position= 'absolute';
	a.style.left= w2+'px';
	a.style.top= '0px';
	puz.appendChild(a);
    }

    // Black background for side clues and puzzle grid
    a= document.createElement("IMG");
    a.src= "img/b.gif";
    a.width= w3;
    a.style.width= w3+'px';
    a.height= h1;
    a.style.height= h1+'px';
    a.style.position= 'absolute';
    a.style.left= '0px';
    a.style.top= h2+'px';
    puz.appendChild(a);

    // Draw top clues
    if (topnum > 0)
    {
	x= w2 + 2;
	for (j= 0; j < this.cols; j++)
	{
	    y= 2;
	    if ((j % 5) == 0) x++;
	    ci= this.top[j].length - topnum;
	    for (var k= 0; k < topnum; k++, ci++)
	    {
		a= document.createElement("IMG");
		if (ci < 0)
		{
		    a.src= "img/w.gif";
		    a.width= a.height= this.block;
		    a.style.width= a.style.height= this.block+"px";
		}
		else
		    a.src= this.prefix+this.top[j][ci]+'.gif';
		a.style.width= a.style.height= this.block+"px";
		a.style.position= "absolute";
		a.style.left= x+"px";
		a.style.top= y+"px";
		puz.appendChild(a);
		y+= this.block;
	    }
	    x+= this.block + 1;
	}
    }

    // Draw side clues
    if (sidenum > 0)
    {
	y= h2 + 2;
	for (i= 0; i < this.rows; i++)
	{
	    x= 2;
	    if ((i % 5) == 0) y++;
	    ci= this.side[i].length - sidenum;
	    for (var k= 0; k < sidenum; k++, ci++)
	    {
		a= document.createElement("IMG");
		if (ci < 0)
		{
		    a.src= "img/w.gif";
		    a.width= a.height= this.block;
		    a.style.width= a.style.height= this.block+"px";
		}
		else
		    a.src= this.prefix+this.side[i][ci]+'.gif';
		a.style.width= a.style.height= this.block+"px";
		a.style.position= "absolute";
		a.style.left= x+"px";
		a.style.top= y+"px";
		puz.appendChild(a);
		x+= this.block;
	    }
	    y+= this.block + 1;
	}
    }

    // Draw puzzle cells
    y= h2 + 2;
    for (i= 0; i < this.rows; i++)
    {
	if ((i % 5) == 0) y++;
	x= w2 + 2;
	for (j= 0; j < this.cols; j++)
	{
	    if ((j % 5) == 0) x++;
	    a= document.createElement("IMG");
	    a.width= a.height= this.block;
	    a.src= imagestr(this.prefix,this.grid[i][j]);
	    a.style.position= "absolute";
	    a.style.left= x+"px";
	    a.style.top= y+"px";
	    a.style.width= a.style.height= this.block+"px";
	    puz.appendChild(a);
	    x+= this.block + 1;
	}
	y+= this.block + 1;
    }
}

// Constructor for a board
function Board(cols,rows,blocksize)
{
    this.rows= rows;
    this.cols= cols;
    this.grid= new Array(rows);
    for (var i= 0; i < rows; i++)
    	this.grid[i]= new Array(cols);
    this.top= new Array();
    this.side= new Array();
    this.block= blocksize > 0 ? blocksize : 10;
    if (this.block < 12)
    	this.prefix= '1/';
    else if (this.block < 14)
    	this.prefix= '2/';
    else
    	this.prefix= '3/';

    this.set= Board_Set;
    this.row= Board_Row;
    this.topclue= Board_TopClue;
    this.sideclue= Board_SideClue;
    this.draw= Board_Draw;
}

