/*  JavaScript SudokuTime, version 1.0.0
 *  (c) 2007-2008 Zone Projects Ltd
 *--------------------------------------------------------------------------*/

var g_mask = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536];
var g_not_mask = [65534, 65533, 65531, 65527, 65519, 65503, 65471, 65407, 65279, 65023, 64511, 63487, 61439, 57343, 49151, 32767];

 // properties are directly passed to `create` method
var Cell = Class.create({
  initialize: function(candidates, uservalue, titularvalue, current_value_selection) {
	this.candidates = candidates;
	this.user_value = uservalue;
    this.titular_value = titularvalue;
	this.current_value_selection = current_value_selection; // 0 - candidates, 1 - user_value, 2 - titular_value
  }

});

function cellClickedHandler(event){
	var el = Event.element(event);
	if(el.tagName != "DIV" || el.up(".user_value") || el.up(".titular_value"))
	{
		el=el.up('div');
	}

	if (active_cell) active_cell.removeClassName('active_cell');
	el.addClassName('active_cell');
	active_cell = el;
	ShowHintTooltip(false);
	
	var tableIndex = bb.GetActiveCell_TableIndex();
	update_keypad_buttons(tableIndex);
}

function update_keypad_buttons(cell_tableIndex)
{
	var cell_obj = bb.Cells[cell_tableIndex];
	if (cell_obj.titular_value)
	{
		disable_candidate_buttons();
		turn_titular_buttons_OnOff(false);
		$('cb0').className = 'cb-disabled';
		$('tb0').className = 'tb-disabled';
	}
	else
	{
		turn_titular_buttons_OnOff(true);
		
		if (cell_obj.user_value)
		{
			disable_candidate_buttons();
			$('tb0').className = 'tb-up';
		}
		else
		{
			update_keypad_candidate_buttons(cell_tableIndex);
			$('cb0').className = 'cb-up';
			$('tb0').className = 'tb-disabled';
		}
	}
}

function update_keypad_candidate_buttons(tableIndex)
{
	var cell_candidates = bb.Cells[tableIndex].candidates;
	var ci = 0;
	for (ci = 0; ci < bb.Tbn*bb.Tbn; ++ci) {
		ToggleCandidateBtn(ci + 1,  (cell_candidates & g_mask[ci]));
	}
}


function disable_candidate_buttons(){
	var cb_index = 0;
	for (cb_index = 0; cb_index <= bb.Tbn*bb.Tbn; ++cb_index) {
		if ($('cb' + cb_index).className != 'cb-disabled')
		{
				$('cb' + cb_index).className = 'cb-disabled';
		}
	}
}

function turn_titular_buttons_OnOff(turn_on){
	var tb_index = 0;
	var new_className = 'tb-disabled';
	if (turn_on) new_className = 'tb-up';
	for (tb_index = 1; tb_index <= bb.Tbn*bb.Tbn; ++tb_index) {
		if ($('tb' + tb_index).className != new_className)
		{
			$('tb' + tb_index).className = new_className;
		}
	}
}

function hintBorderClickedHandler(event){
	return true;
}


function KeycodeToSudokuValue(key){
	if(key - 48 < 10)
	{
		key -=48;
	}
	else
	{
		key -=96;
	}
	return key;
}

function cellKeydownHandler(e){
	ShowHintTooltip(false);
	var e=window.event || e
	var key = e.which || e.keyCode;

	if (key == 46) // delete
	{
		var tableIndex = bb.GetActiveCell_TableIndex();
		if (bb.Cells[tableIndex].titular_value) return;
		if (bb.Cells[tableIndex].user_value)
		{	
			var Command_clearUserValue = new DeleteUserValue_Command( tableIndex, bb.Cells[tableIndex].user_value);
			CommandProcessor.execute( Command_clearUserValue );
		}
		else
		{
			var Command_clearCandidates = new DeleteCandidates_Command( tableIndex, bb.Cells[tableIndex].candidates);
			CommandProcessor.execute( Command_clearCandidates );
		}
	}
	
	if (key == 48 || key == 45 || key ==96) // 0
	{
		togglePencilMarkTypeMode();
		return;
	}
	
	if (key == 37 || key == 38 || key == 39 || key == 40 )
	{
	//	e.cancelBubble = false;
	//	e.returnValue=false;
	//	window.scroll(0,0);
		
	
		if (window.event) {
		   window.event.cancelBubble = true;
		   window.event.returnValue = false;
		 }
		
		if (e && e.preventDefault && e.stopPropagation) {
		   e.preventDefault();
		   e.stopPropagation();
		 }
		
		var tableIndexActiveCell = bb.GetActiveCell_TableIndex();
		var tableIndex = tableIndexActiveCell;
		// right
		var el;
		
		switch(key)
		{
			case 39:
			{
				if( Math.floor(tableIndex % (bb.Tbn*bb.Tbn) ) < bb.Tbn*bb.Tbn -1 )
				{
					tableIndex ++;
					el =  bb.GetCellElement(tableIndex);
				}
				else
				{
					return ;
				}
			}
			break;
			case 37:
			{
				if( Math.floor(tableIndex % (bb.Tbn*bb.Tbn) ) > 0 )
				{
					tableIndex --;
					el =  bb.GetCellElement(tableIndex);
				}
				else
				{
					return ;
				}
			}
			break;
			case 38:
			{
				if( Math.floor(tableIndex / (bb.Tbn*bb.Tbn) ) > 0 )
				{
					tableIndex -= bb.Tbn*bb.Tbn;
					el =  bb.GetCellElement(tableIndex);
				}
				else
				{
					return ;
				}
			}
			break;
			case 40:
			{
				if( Math.floor(tableIndex / (bb.Tbn*bb.Tbn) ) < bb.Tbn*bb.Tbn*bb.Tbn*bb.Tbn - bb.Tbn*bb.Tbn )
				{
					tableIndex += bb.Tbn*bb.Tbn;
					el =  bb.GetCellElement(tableIndex);
				}
				else
				{
					return ;
				}
			}			
			break;			
		}		
		
		
		if(el)
		{
			active_cell = bb.GetCellElement(tableIndexActiveCell);
			
			if (active_cell==null){
			return ;
			}
			active_cell.removeClassName('active_cell');
		
			el.addClassName('active_cell');
			active_cell = el;
			ShowHintTooltip(false);
			
			var tableIndex = bb.GetActiveCell_TableIndex();
			update_keypad_buttons(tableIndex);
		}
		return ;
	}
	

	if (!(((key - 49) <= bb.Tbn*bb.Tbn &&
		(key - 49) >= 0) || ((key - 97) <= bb.Tbn*bb.Tbn &&
		(key - 97) >= 0) )) return;

	if (bb.TypeMode == 'user_values')
	{
		var user_value_to_set = KeycodeToSudokuValue(key);
		var tableIndex = bb.GetActiveCell_TableIndex();

		if (!active_cell.hasClassName('titular_value'))
		{
			var Command_SetUserValue = new SetUserValue_Command( tableIndex, user_value_to_set);
			CommandProcessor.execute( Command_SetUserValue );
		}
	}
	else
	{
		var candidateValue = KeycodeToSudokuValue(key);
		var tableIndex = bb.GetActiveCell_TableIndex();
		if (bb.Cells[tableIndex].user_value == 0)
		{
			var TurnOnOffValue = bb.IsCandidateOn( tableIndex, candidateValue );
			var Command_SetCandidateValue = new SetCandidateValue_Command( tableIndex,  candidateValue, !TurnOnOffValue);
			CommandProcessor.execute( Command_SetCandidateValue );
			if (!active_cell.hasClassName('candidates'))
			{
				//['user_value', 'titular_value', 'empty_value'].each( function(class_name) { this.removeClassName(class_name); }, active_cell);
				//active_cell.addClassName('candidates');
			}
		}

		return true;
	}
}
function togglePencilMarkTypeMode()
{
	if (bb.TypeMode == 'user_values') // 0
	{
		bb.TypeMode = 'candidate_values';
		document.getElementById("switch_candidates_titular_btn").className = 'pencilmark-on';
	}
	else
	{
		bb.TypeMode = 'user_values';
		document.getElementById("switch_candidates_titular_btn").className = 'pencilmark-off';
	}
}

function candidatesHtml(from, to, Tbn){
	return $A($R(from,to)).zip( function(s) {
				return '<b class="none">' + s + '</b>' + ( s % Tbn?'':'<br />');
				}
	).join(' ');
}

function ShowHintTooltip(b)
{
	if (b)
	{
		$('menu_row').hide();
		$('hint_row').show();
	}
	else
	{
		$('menu_row').show();
		$('hint_row').hide();
	}
}

var Board = Class.create({
  initialize: function(Tbn) {
	this.TypeMode = 'Xuser_values'; // 'candidate_values';
    this.Tbn = Tbn;
	this.Cells = new Array();
	this.TableIndexToGridIndex_Map = new Array();

	var i = 0;
	for(i = 0; i < (this.Tbn*this.Tbn)*(this.Tbn*this.Tbn); ++i)
	{
		this.Cells.push( new Cell(0, 0, 0, 0));
	}

},

  Tbn: function() {
    return this.Tbn;
  },
  Build: function() {
	var boardElement = $('board');
	var colors = ["red","green","blue","darkred","darkgreen","darkblue","lightred","lightgreen","lightblue"];
	this.BuildTableIndexToGridIndex();
	
	//document.addEventListener('keydown', cellKeydownHandler, false); 
	document.observe('keydown', cellKeydownHandler); // for FF
	
	if (boardElement.childNodes.length > 1)
	{
		var newBoard = boardElement.cloneNode(false); // no child nodes;
		boardElement.parentNode.replaceChild(newBoard,boardElement);
	}

	var i = 0;
	for (i = 0; i < this.Tbn*this.Tbn; ++i)
	{
		var box = new Element( "div", { 'id':'box' + i});
		if (i < this.Tbn)
		{
			box.addClassName('padding2top');
		}
		if (i % this.Tbn == 0)
		{
			box.addClassName('padding2left');
		}

		if (i % 2 == 1)
		{
			box.addClassName('alt');
		}



		var ci = 0;
		for (ci = 0; ci < this.Tbn*this.Tbn; ++ci)
		{
			var cell = new Element("div", {'id':'cell_' + i + '_' + ci})
			cell.update(candidatesHtml(1,this.Tbn*this.Tbn, this.Tbn));
			
			//document.observe('keydown', cellKeydownHandler); // for FF
			cell.observe('click', cellClickedHandler);

			cell.insert(new Element("div").update(ci));
			cell.addClassName('candidates');

			box.insert( cell );
			if (((ci + 1) % this.Tbn) == 0)
			{
				box.insert( new Element( "br") );
			}
		}

		boardElement.insert( box );
		var add_newline = (((i +1 ) % this.Tbn) == 0);
        if (add_newline)
        {
            var BR =  new Element( "br");
            boardElement.insert( BR);
            BR.setStyle({  clear: 'both'});
        }
     }

     var BR1 =  new Element( "br");
     boardElement.insert( BR1);
     BR1.setStyle({  clear: 'both' , height: '0px', fontSize: '1px'});

	 active_cell = this.GetCellElement(20);
	 if (active_cell!=null)
	 {
	 	active_cell.addClassName('active_cell');
	 }

  },

	NewGame: function( titularvalues ){
		var i = 0; var len = 0;
		for( i = 0, len = titularvalues.length; i < len; ++i)
		{
			this.ClearCell( null, i);

			if (titularvalues.charAt(i) != '0')
			{
				this.Cells[i].titular_value = titularvalues.charAt(i) - '0';
				this.Cells[i].user_value = this.Cells[i].titular_value;
				this.SetTitularValue(null, i, titularvalues.charAt(i));
			}
			else
			{
				this.Cells[i].titular_value = 0;
				this.Cells[i].user_value = 0;
			}
		}
	},

	GetActiveCell_TableIndex: function(){
		var Tbn = this.Tbn;
		var box_and_cell_index = active_cell.id.split("_");
		box_and_cell_index.shift();
		var box_index = box_and_cell_index[0];
		var cell_index = box_and_cell_index[1];
		var r1 = Math.floor(box_index / Tbn);
		var r2 = Math.floor(cell_index / Tbn);
		return r1*Tbn*Tbn*Tbn + r2*Tbn*Tbn + (box_index % Tbn)*Tbn + cell_index % Tbn;
	},

	SetCandidateValue: function( cell_element, cell_table_index, candidateToSet, candidateValue){
		var cell_element = $('board').getElementsByTagName('b')[this.TableIndexToGridIndex_Map[cell_table_index]*this.Tbn*this.Tbn].parentNode;

		if (candidateToSet == 0) // clear all candidates 
		{
			this.Cells[cell_table_index].candidates = 0;
			return cell_element;
		}
		
		var candidate_element = cell_element.down('b', candidateToSet - 1);

		if(candidateValue)
		{
			if (cell_element.hasClassName('empty_value'))
			{
				var i = 0; var len = 0;
				for( i = 0, len = this.Tbn*this.Tbn; i < len; ++i)
				{
					cell_element.down('b', i).className = 'none';
				}
			}
		
			this.Cells[cell_table_index].candidates |= g_mask[candidateToSet - 1];
			if (candidate_element.className != "n")
			{
				candidate_element.className = "n";
				this.Cells[cell_table_index].current_value_selection = 0;
			}
		}
		else
		{
			this.Cells[cell_table_index].candidates &= g_not_mask[candidateToSet - 1];
			if (candidate_element.className != "none")
			{
				candidate_element.className = "none";
				this.Cells[cell_table_index].current_value_selection = 1;
			}
		}

		['user_value', 'titular_value', 'candidates', 'empty_value'].each( function(class_name) { this.removeClassName(class_name); }, cell_element);
		cell_element.addClassName('candidates');

		return cell_element;
	},

	BuildTableIndexToGridIndex: function(){
		if (this.TableIndexToGridIndex_Map.length == 0)
		{
			var i = 0; var len = 0;
			for( i = 0, len = this.Tbn*this.Tbn*this.Tbn*this.Tbn; i < len; ++i)
			{
				this.TableIndexToGridIndex_Map[i] = this.TableIndexToGridIndex(i);
			}
		}
	},

	SetGridCandidates: function( candidates_array ){
		var candidates_b_array = $('board').getElementsByTagName('b');
		var i = 0; var len = 0;
		for( i = 0, len = candidates_array.length; i < len; ++i)
		{
			var cell_element;
			if (this.Cells[i].candidates != candidates_array[i])
			{
				//this.Cells[ this.TableIndexToGridIndex_Map[i] ].candidates = candidates_array[i];

				var xor_candidates = this.Cells[i].candidates ^ candidates_array[i];
				for (candidate_index = 0; candidate_index < this.Tbn*this.Tbn; ++candidate_index) {
					if (xor_candidates & g_mask[candidate_index]) // changed
					{
						int_setit = candidates_array[i] & g_mask[candidate_index];
						candidate_element = candidates_b_array[this.TableIndexToGridIndex_Map[i]*this.Tbn*this.Tbn + candidate_index]
						if(int_setit)
						{
							if (candidate_element.className != "n")
							{
								candidate_element.className = "n";
							}
						}
						else
						{
							if (candidate_element.className != "none")
							{
								candidate_element.className = "none";
							}
						}
					}
				}

				this.Cells[i].candidates = candidates_array[i];

				cell_element = candidate_element.parentNode;
			}
			else
			{
				cell_element = $(candidates_b_array[this.TableIndexToGridIndex_Map[i]*this.Tbn*this.Tbn]).parentNode;
			}

			if (cell_element.hasClassName("empty_value"))
			{
				cell_element.className =  'candidates';
			}


		}

	},


	SetGridUserValues: function( uservalues_int_array ){
		var i = 0; var len = 0;
		for( i = 0, len = uservalues_int_array.length; i < len; ++i)
		{
			if (this.Cells[ i ].user_value != uservalues_int_array[i])
			{
				this.SetUserValue( null, i, uservalues_int_array[i]);
				if ( uservalues_int_array[i] == 0 )
				{
					this.ClearCell(null, i);
				}
			}
		}
	},

	IsCandidateOn: function( tableIndex, candidateValue ){
		return (this.Cells[tableIndex].candidates & g_mask[candidateValue-1]);
	},
	
	GetCellElement: function( cell_table_index){
		var box_index;
		box_index = this.GetBoxIndexFromTableIndex(cell_table_index);
		cell_element = $('cell_' + box_index + '_' + this.GetCellIndexFromTableIndex(cell_table_index));

		return cell_element;
	},
	
	ClearCell: function( cell_element, cell_table_index){
		var box_index;

		if (!cell_element)
		{
			box_index = this.GetBoxIndexFromTableIndex(cell_table_index);
			cell_element = $('cell_' + box_index + '_' + this.GetCellIndexFromTableIndex(cell_table_index));
		}
		//var cell_element = $('board').getElementsByTagName('b')[this.TableIndexToGridIndex_Map[cell_table_index]*this.Tbn*this.Tbn].parentNode;

		cell_element.className = 'empty_value';

		return cell_element;
	},

	SetUserValue: function( cell_element, cell_table_index, user_value_to_set){
		var box_index;

		if (!cell_element)
		{
			box_index = this.GetBoxIndexFromTableIndex(cell_table_index);
			cell_element = $('cell_' + box_index + '_' + this.GetCellIndexFromTableIndex(cell_table_index));
		}

		//var cell_element = $('board').getElementsByTagName('b')[this.TableIndexToGridIndex_Map[cell_table_index]*this.Tbn*this.Tbn].up();

		var user_value_element = cell_element.down('div');
		user_value_element.update(user_value_to_set);

		this.Cells[cell_table_index].user_value = user_value_to_set;

		if (user_value_to_set == 0)
		{
			cell_element.className = cell_element.className.replace('user_value', 'candidates');
			cell_element.className = cell_element.className.replace('empty_value', 'candidates');
		}
		else if (!cell_element.hasClassName('user_value'))
		{
			--this.EmptyCellsLeft;
			['candidates', 'empty_value'].each( function(class_name) { this.removeClassName(class_name); }, cell_element);
			cell_element.addClassName('user_value');
		}

		if ( this.EmptyCellsLeft == 0 ) alert("End of the Game");

		return cell_element;
	},

	SetTitularValue: function( cell_element, cell_table_index, user_value_to_set){
		var ui_cell = this.SetUserValue(cell_element, cell_table_index, user_value_to_set);
		ui_cell.className = ui_cell.className.replace('user_value','titular_value');
	},

	// private
	TableIndexToGridIndex: function ( tableIndex )
	{
		var box_index = bb.GetBoxIndexFromTableIndex(tableIndex);

		var rc = bb.TableIndexToRowColumn(tableIndex);
		var index_in_box = (rc.row % this.Tbn)*this.Tbn + rc.col % this.Tbn;
		return box_index*(this.Tbn*this.Tbn) + index_in_box;
	},

	TableIndexToRowColumn: function (tableIndex){
		var pos = new Object;
		pos.row = Math.floor((tableIndex/(this.Tbn*this.Tbn)));
		pos.col = (tableIndex%(this.Tbn*this.Tbn));
		return pos;
	},
	///////////////////////////////
	GetBoxIndexFromTableIndex: function(tableIndex)	{
		return ( Math.floor( tableIndex/( this.Tbn*this.Tbn*this.Tbn ) ) ) * this.Tbn  +
				 Math.floor((tableIndex%( this.Tbn*this.Tbn ) ) / this.Tbn) % this.Tbn;
	},
	Box_CellGroupIndexToCellTableIndex: function(cellGroupIndex, GroupIndex)
	{
		return ( Math.floor((GroupIndex/this.Tbn))*this.Tbn + Math.floor((cellGroupIndex/this.Tbn)))*(this.Tbn*this.Tbn) + 
				 Math.floor((GroupIndex%this.Tbn))*this.Tbn + Math.floor(cellGroupIndex%this.Tbn);
	},
	
	GetRowIndexFromTableIndex: function(tableIndex)
	{
		return  Math.floor( tableIndex/( this.Tbn*this.Tbn ) );
	},
	Row_CellGroupIndexToCellTableIndex: function(cellGroupIndex, GroupIndex)
	{
		return GroupIndex*(this.Tbn*this.Tbn) + cellGroupIndex;
	},
	
	GetColumnIndexFromTableIndex: function(tableIndex)
	{
		return Math.floor(tableIndex%( this.Tbn*this.Tbn ) );
	},
	
	Col_CellGroupIndexToCellTableIndex: function(cellGroupIndex, GroupIndex)
	{
		return cellGroupIndex*(this.Tbn*this.Tbn) + GroupIndex;
	},

	/////////////////////////////
	
	GetCellIndexFromTableIndex: function(tableIndex)	{
		var pos = this.TableIndexToRowColumn(tableIndex);
		var boxRow = (pos.row % this.Tbn);
		return boxRow*this.Tbn + (pos.col % this.Tbn);
	},

	SerializeGrid: function()	{
		var serialized_grid = '';
		for (var index = 0, len = this.Cells.length; index < len; ++index) {
			serialized_grid += this.Cells[index].user_value;
		}

		return serialized_grid;
	},

	SerializeGridTitulars: function()	{
		var serialized_GridTitulars = '';
		for (var index = 0, len = this.Cells.length; index < len; ++index) {
			serialized_GridTitulars += this.Cells[index].titular_value;
		}

		return serialized_GridTitulars;
	},

	SerializeCandidates: function()	{
		var serialized_candidates = new Array();
		for (var index = 0, len = this.Cells.length; index < len; ++index) {
			serialized_candidates.push( this.Cells[index].candidates );
		}

		return serialized_candidates;
	},

	ClearHint: function(data) {
		var i = 0; var len = 0;
		for( i = 0, len = data.Records.length; i < len; ++i)
		{
			if (data.Records[i].patternMakers)
			{
				this.HighlightCell( data.Records[i].cellTableIndex,  data.Records[i].candidates, false, 'patternmaker-cell', 'g' );
			}
			else
			{
				this.HighlightCell( data.Records[i].cellTableIndex,  data.Records[i].candidates, false, 'no-patternmaker-cell', 'r'  );
			}
		}
		var board_element = $('board');
		for( i = 0, len = data.hintBorders.length; i < len; ++i)
		{
			board_element.removeChild(data.hintBorders[i]);
		}
		
		ShowHintTooltip(false);

		this.currentHintData = null;
	},

	HighlightHint: function(data) {
		this.currentHintData = data;
		data.hintBorders = new Array();
		var alreadyCreatedHintBorders = new Object;
		alreadyCreatedHintBorders.rows = new Array();
		alreadyCreatedHintBorders.cols = new Array();
		alreadyCreatedHintBorders.boxes = new Array();
		
		var candidates_b_array = $('board').getElementsByTagName('b');
		
		var i = 0; var len = 0;
		var rowLength = this.Tbn*this.Tbn;
		for( i = 0, len = data.Records.length; i < len; ++i)
		{
			if (data.Records[i].patternMakers)
			{
				this.HighlightCell( data.Records[i].cellTableIndex,  data.Records[i].candidates, true, 'patternmaker-cell', 'g' );
			}
			else
			{
				this.HighlightCell( data.Records[i].cellTableIndex,  data.Records[i].candidates, true, 'no-patternmaker-cell', 'r'  );
			}
			
			var hintBorder = null;
			switch (data.Records[i].groupType)
			{
				case 1: // row
					var startRowCellIndex = this.TableIndexToGridIndex_Map[data.Records[i].cellTableIndex - (data.Records[i].cellTableIndex % rowLength)];
					var endRowCellIndex = this.TableIndexToGridIndex_Map[data.Records[i].cellTableIndex + (rowLength - (data.Records[i].cellTableIndex % rowLength)) - 1];
					var startRowCell = candidates_b_array[startRowCellIndex*rowLength].parentNode;
					var endRowCell = candidates_b_array[endRowCellIndex*rowLength].parentNode;

					hintBorder = this.CreateHintBorder(	i,
															startRowCell.offsetLeft - 1,
															startRowCell.offsetTop - 1,
															endRowCell.offsetLeft + endRowCell.getWidth(), 
															startRowCell.offsetTop + startRowCell.getHeight(),
															(data.Records[i].patternMakers?'hint-border-patternmaker':'hint-border-no-patternmaker'));
					break;
				case 2: // column
					var startColCellIndex = this.TableIndexToGridIndex_Map[data.Records[i].cellTableIndex % rowLength];
					var endColCellIndex = this.TableIndexToGridIndex_Map[rowLength*rowLength - (rowLength - (data.Records[i].cellTableIndex % rowLength))];
					var startColCell = candidates_b_array[startColCellIndex*rowLength].parentNode;
					var endColCell = candidates_b_array[endColCellIndex*rowLength].parentNode;

					hintBorder = this.CreateHintBorder(	i,
															startColCell.offsetLeft - 1,
															startColCell.offsetTop - 1,
															endColCell.offsetLeft + endColCell.getWidth(), 
															endColCell.offsetTop + endColCell.getHeight(),
															(data.Records[i].patternMakers?'hint-border-patternmaker':'hint-border-no-patternmaker'));
					break;
				case 3: // box
					var box_element = candidates_b_array[ this.TableIndexToGridIndex_Map[data.Records[i].cellTableIndex]*rowLength ].parentNode.parentNode;
					var startBoxCell = box_element.down("DIV");
					var endBoxCell = startBoxCell.next("DIV", rowLength - 2);

					hintBorder = this.CreateHintBorder(	i,
															startBoxCell.offsetLeft - 1,
															startBoxCell.offsetTop - 1,
															endBoxCell.offsetLeft + endBoxCell.getWidth(), 
															endBoxCell.offsetTop + endBoxCell.getHeight(),
															(data.Records[i].patternMakers?'hint-border-patternmaker':'hint-border-no-patternmaker'));
					break;
			}
			
			if (hintBorder)
			{
				$('board').insert(hintBorder.hintBorderLeft);
				$('board').insert(hintBorder.hintBorderTop);
				$('board').insert(hintBorder.hintBorderRight);
				$('board').insert(hintBorder.hintBorderBottom);
			}
		}
	},
	
	//  private
	CreateHintBorder: function( id, left, top, right, bottom, class_name) {
		var borderWidthHeight = '1px';
		var hintBorder = new Object;
	
		hintBorder.hintBorderLeft = new Element("div", {'id':'hintBorder_left_' + id, 'class':class_name});
		hintBorder.hintBorderLeft.setStyle({ 'top': top + "px", 
							  'left': left + "px", 
							  'width': borderWidthHeight, 
							  'height': bottom - top + 'px'});
		this.currentHintData.hintBorders.push( hintBorder.hintBorderLeft );

		hintBorder.hintBorderTop = new Element("div", {'id':'hintBorder_top_' + id, 'class':class_name});
		hintBorder.hintBorderTop.setStyle({ 'top': top + "px", 
							  'left': left + "px", 
							  'width': right - left + 'px', 
							  'height': borderWidthHeight});
		this.currentHintData.hintBorders.push( hintBorder.hintBorderTop );
							  
		hintBorder.hintBorderRight = new Element("div", {'id':'hintBorder_right_' + id, 'class':class_name});
		hintBorder.hintBorderRight.setStyle({ 'top': top + "px", 
							  'left': right + "px", 
							  'width':  borderWidthHeight, 
							  'height': bottom - top + 'px'});
		this.currentHintData.hintBorders.push( hintBorder.hintBorderRight );
							  
		hintBorder.hintBorderBottom = new Element("div", {'id':'hintBorder_bottom_' + id, 'class':class_name});
		hintBorder.hintBorderBottom.setStyle({ 'top': bottom + "px", 
							  'left': left + "px", 
							  'width': right - left + 'px', 
							  'height': borderWidthHeight });
		this.currentHintData.hintBorders.push( hintBorder.hintBorderBottom );
		
		return hintBorder;
	},

	HighlightCell: function( tableIndex, candidatesToHighlight, turnonLights, cellClassName, candidateClassName){
		var candidates_b_array = $('board').getElementsByTagName('b');
		var cell_element = $(candidates_b_array[this.TableIndexToGridIndex_Map[tableIndex]*this.Tbn*this.Tbn]).parentNode;

		if (turnonLights)
		{
			cell_element.addClassName(cellClassName);
		}
		else
		{
			cell_element.removeClassName(cellClassName);
		}

		var i = 0; var len = 0;
		for( i = 0, len = this.Tbn*this.Tbn; i < len; ++i)
		{
			if ((candidatesToHighlight & g_mask[i]) &&
				(this.Cells[tableIndex].candidates & g_mask[i]))
			{
				var b = cell_element.down('b', i);
				if (turnonLights)
				{
					b.addClassName(candidateClassName);
				}
				else
				{
					b.removeClassName(candidateClassName);
				}
			}
		}

		return cell_element;
	}
	
});















