var kMoveSteps = 15;
var kGapSteps = 40;
var kExpandSteps = 10;
var kInterval = 50;
var kTileSize = 200;
var kSmallSize = 80;
var kGridSize = kTileSize * 3;
var kExpandedSize = 400;
var kImagesBase = 'http://fngcdn.appspot.com/fn/'

var sBlankDex = 8;
var sCurrMover = -1;
var sCurrProgress = kGapSteps * 2 / 3;
var sLastBlank = 100;		// to prevent tile from moving right back to previous square on next move
var sBaseLeft, sBaseTop;

var kModeMoving = 0;
var kModeExpanding = 1;
var kModeCollapsing = 2;
var kModeNull = 3
var sMode = kModeMoving;

function Init()
{
	window.onresize = DrawGrid;
	var base = document.getElementById('base');
	base.style.width = 602;
	base.style.height = 602;
	DrawGrid();
	setInterval('Animate();', kInterval);
}

function DrawGrid()
{
	var pos = GetAbsPos(base);
	sBaseLeft = pos.left;
	sBaseTop = pos.top;
	
	// position all our tiles
	for(var i = 0; i < sTiles.length; i++)
	{
		var tile = sTiles[i];
		tile.div = document.getElementById(tile.id);
		tile.div.style.position = "absolute";
		tile.div.style.overflow = "hidden";
		var tilePos = GetPosFor(tile.currdex);
		MoveTile(tile, tilePos.left, tilePos.top, kTileSize, kTileSize);
		tile.div.innerHTML = GetTileHtml(i, 100);
		
		// make dummy tile invisible
		if(i == kTiles)
		{
			tile.div.style.border = "0";
			tile.div.style.visibility = "hidden";
		}
		else
		{
			tile.div.style.background = "white";
		}
	}
}

function MoveTile(tile, left, top, width, height)
{
	tile.left = left;
	tile.top = top;
	tile.width = width;
	tile.height = height;
	tile.div.style.left = "" + left + "px";
	tile.div.style.top = "" + top + "px";
	tile.div.style.width = "" + width + "px";
	tile.div.style.height = "" + height + "px";
}

function Tile(id, currdex, div)
{
	this.id = id;
	this.currdex = currdex;
	this.div = div;
}

function Pos(left, top)
{
	this.left = left;
	this.top = top;
}

function GetPosFor(index)
{
	var left = (index % 3) * (kTileSize + 1);
	var top = Math.floor(index / 3) * (kTileSize + 1);
	return new Pos(left + sBaseLeft, top + sBaseTop);
}

function GetEdgePosFor(index, expandingDex)
{
	var smallpos = kSmallPositions[kSmallPositionsFor[expandingDex][index]];
	return new Pos(smallpos.left + sBaseLeft, smallpos.top + sBaseTop);
}

function Animate()
{
	sCurrProgress++;
	switch(sMode)
	{
		case kModeMoving:
			AnimateMove();
			break;
			
		case kModeExpanding:
			AnimateExpand();
			break;
			
		case kModeCollapsing:
			AnimateCollapse();
			break;
			
		case kModeNull:
			sCurrProgress = 0;
			break;
	}
}

function AnimateMove()
{
	if(sCurrMover >= 0)
	{
		if(sCurrProgress <= kMoveSteps)
		{
			var endPos = GetPosFor(sBlankDex);
			MoveStep(sCurrMover, endPos.left, endPos.top, kTileSize, kTileSize, kMoveSteps - sCurrProgress);
		}
		else
		{
			sLastBlank = sBlankDex;
			sBlankDex = sTiles[sCurrMover].currdex;
			sTiles[sCurrMover].currdex = sLastBlank;
			sCurrMover = -1;
			sCurrProgress = 0;
		}
	}
	else if(sCurrProgress >= kGapSteps)
	{
		var neighbors = GetNeighbors(sBlankDex);
		var choice = Math.floor(Math.random() * neighbors.length);
		// slightly favor moves from center tile because it opens up more future possibilities
		if(neighbors[choice] == 4)
			choice = Math.floor(Math.random() * neighbors.length);
		for(sCurrMover = 0; sTiles[sCurrMover].currdex != neighbors[choice]; sCurrMover++);
		sCurrProgress = 0;
	}
}

function MoveStep(index, endLeft, endTop, endWidth, endHeight, remainingSteps)
{
	var tile = sTiles[index];
	if(remainingSteps > 0)
	{
		endLeft = tile.left + (endLeft - tile.left) / remainingSteps;
		endTop = tile.top + (endTop - tile.top) / remainingSteps;
		endWidth = tile.width + (endWidth - tile.width) / remainingSteps;
		endHeight = tile.height + (endHeight - tile.height) / remainingSteps;
	}
	var opacity = 100;
	if(endWidth > kTileSize && endWidth > tile.width)	// if getting bigger
		opacity = Math.floor(remainingSteps * 100 / kExpandSteps);
	else if(endWidth > kTileSize)
		opacity = 100 - Math.floor(remainingSteps * 100 / kExpandSteps);
	
	tile.div.innerHTML = GetTileHtml(index, opacity);
	MoveTile(tile, endLeft, endTop, endWidth, endHeight);
}

function AnimateExpand()
{
	if(sCurrProgress >= kExpandSteps)
	{
		sTiles[sCurrMover].div.innerHTML = GetExpandedHtml(sCurrMover);
		sMode = kModeNull;
		return;
	}
	
	var remainingSteps = kExpandSteps - sCurrProgress;
	var mover = sTiles[sCurrMover];
	for(var i = 0; i < kTiles; i++)
	{
		var tile = sTiles[i];
		if(i == sCurrMover)
		{
			var expos = kExpandedPos[tile.currdex];
			MoveStep(i, expos.left + sBaseLeft, expos.top + sBaseTop, kExpandedSize, kExpandedSize, remainingSteps);
		}
		else
		{
			var edgepos = GetEdgePosFor(tile.currdex, mover.currdex);
			MoveStep(i, edgepos.left, edgepos.top, kSmallSize, kSmallSize, remainingSteps);
		}
	}
}

function AnimateCollapse()
{
	if(sCurrProgress >= kExpandSteps)
	{
		sMode = kModeMoving;
		sCurrMover = -1;
		sCurrProgress = kMoveSteps * 3 / 4;
		return;
	}
	
	var remainingSteps = kExpandSteps - sCurrProgress;
	for(var i = 0; i < kTiles; i++)
	{
		var tile = sTiles[i];
		var tilepos = GetPosFor(tile.currdex);
		MoveStep(i, tilepos.left, tilepos.top, kTileSize, kTileSize, remainingSteps);
	}
}

function Expand(index)
{
	sMode = kModeExpanding;
	sCurrMover = index;
	sCurrProgress = 0;
	for(var i = 0; i < kTiles; i++)
	{
		sTiles[i].div.style.zIndex = (i == index ? "100" : "10");
	}
}

function Collapse()
{
	sMode = kModeCollapsing;
	sCurrProgress = 0;
}

function GetNeighbors(index)
{
	var list;
	switch(index)
	{
		case 0:	list = [1, 3]; break;
		case 1:	list = [0, 2, 4]; break;
		case 2:	list = [1, 5]; break;
		case 3:	list = [0, 4, 6]; break;
		case 4: list = [1, 3, 5, 7]; break;
		case 5: list = [2, 4, 8]; break;
		case 6: list = [3, 7]; break;
		case 7:	list = [4, 6, 8]; break;
		case 8:	list = [5, 7]; break;
	}
	
	// most of the time, prevent move back by same tile
	if(Math.random() * 10 >= 1)
	{
		for(var i = 0; i < list.length; i++)
		{
			if(list[i] == sLastBlank)
			{
				var listA = list.slice(0, i);
				var listB = list.slice(i + 1);
				return listA.concat(listB);
			}
		}
	}
	return list;
}

function GetAbsPos(div)
{
	var left = div.offsetLeft;
	var top = div.offsetTop;

	while (div.offsetParent)
	{
		div = div.offsetParent;
		left += div.offsetLeft;
		top += div.offsetTop;
	}
	return new Pos(left, top);
}

function getObjectClass(obj) {
    if (obj && obj.constructor && obj.constructor.toString) {
        var arr = obj.constructor.toString().match(/function\s*(\w+)/);

        if (arr && arr.length == 2) {
            return arr[1];
        }
    }
    return "undefined";
}

function alertx(msg)
{
//	alert(msg);
}

var sTiles = [
	new Tile("div0", 0, null),
	new Tile("div1", 1, null),
	new Tile("div2", 2, null),
	new Tile("div3", 3, null),
	new Tile("div4", 4, null),
	new Tile("div5", 5, null),
	new Tile("div6", 6, null),
	new Tile("div7", 7, null),
	new Tile("div8", 8, null)	// dummy tile to form complete square; can be ignored after initialization
];
var kTiles = sTiles.length - 1;	// ignore dummy tile

function TileInfo(pos, image, caption, url, desc, expandedHtml)
{
	this.image = image;
	this.caption = caption;
	this.url = url;
	this.desc = desc;
	this.expandedHtml = expandedHtml;
	this.currpos = this.dest = pos;
}

function GetTileHtml(index, opacity)
{
	if(index >= kTileInfo.length)
		return "";
	var tile = kTileInfo[index];
	var html = "";
	html += "<a href='#' onclick='Expand(" + index + "); return false;'>";
	html += "<img src='" + kImagesBase + tile.image + ".jpg' style='border: 0; opacity: " + opacity / 100.0 + "; filter:alpha(opacity=" + opacity + ");' title='" + tile.caption + "' width='100%' /></a>";
	
	return html;
}

function GetExpandedHtml(index)
{
	if(kTileInfo[index].expandedHtml != null)
		return kExpandedFrameStart + kTileInfo[index].expandedHtml + kExpandedFrameEnd;
		
	var tile = kTileInfo[index];
	return kExpandedFrameStart
		+ "<table><tr><td><table><tr><td><div style='border: 2px solid #808080'><a href='" + tile.url + "' target='_blank'>"
		+ "<img src='" + kImagesBase + tile.image + ".png' style='border:0;' /></a></div></td><td valign='center'>"
		+ "<a href='" + tile.url + "' target='_blank'><img src='go.gif' style='border:0'/></a></td></tr></table>"
		+ "<tr><td><table height='2px'><tr><td></td></tr></table>" 
		+ (tile.desc != null ? tile.desc : tile.caption) + "</td></tr></table>"
		+ kExpandedFrameEnd;
}

var kExpandedFrameStart = "<center><br><table width='85%'><tr><td valign='top' align='right'><a href='#' onclick='Collapse(); return false;'><img src='close.gif' style='border:0;' /></a></td></tr><tr><td><div style='font-family: sans-serif; font-size: 13sp;'><hr />"; 
var kExpandedFrameEnd = "</div></td></tr></table></center>";

var kTilePositions = [
	new Pos(kTileSize * 0, kTileSize * 0),
	new Pos(kTileSize * 1, kTileSize * 0),
	new Pos(kTileSize * 2, kTileSize * 0),
	new Pos(kTileSize * 0, kTileSize * 1),
	new Pos(kTileSize * 1, kTileSize * 1),
	new Pos(kTileSize * 2, kTileSize * 1),
	new Pos(kTileSize * 0, kTileSize * 2),
	new Pos(kTileSize * 1, kTileSize * 2),
	new Pos(kTileSize * 2, kTileSize * 2)
]

var kMargin = 20;
var kExpandedPos = [
	new Pos(kMargin, kMargin),
	new Pos((kGridSize - kExpandedSize) / 2,  kMargin),
	new Pos(kGridSize - kExpandedSize - kMargin,  kMargin),
	new Pos(kMargin, (kGridSize - kExpandedSize) / 2),
	new Pos((kGridSize - kExpandedSize) / 2, (kGridSize - kExpandedSize) / 2),
	new Pos(kGridSize - kExpandedSize - kMargin, (kGridSize - kExpandedSize) / 2),
	new Pos(kMargin, kGridSize - kExpandedSize - kMargin),
	new Pos((kGridSize - kExpandedSize) / 2, kGridSize - kExpandedSize - kMargin),
	new Pos(kGridSize - kExpandedSize - kMargin, kGridSize - kExpandedSize - kMargin)
];

// all the possible small positions
// 0   1  2  3  4
// 5            6
// 7            8
// 9           10
// 11 12 13 14 15
var kStop0 = 0;
var kStop1 = (kGridSize - kSmallSize) / 4;
var kStop2 = (kGridSize - kSmallSize) / 2;
var kStop3 = (kGridSize - kSmallSize) * 3 / 4;
var kStop4 = kGridSize - kSmallSize;
var kSmallPositions = [
	new Pos(kStop0, kStop0), 
	new Pos(kStop1, kStop0),
	new Pos(kStop2, kStop0),
	new Pos(kStop3, kStop0),
	new Pos(kStop4, kStop0),
	new Pos(kStop0, kStop1),
	new Pos(kStop4, kStop1),
	new Pos(kStop0, kStop2),
	new Pos(kStop4, kStop2),
	new Pos(kStop0, kStop3),
	new Pos(kStop4, kStop3),
	new Pos(kStop0, kStop4),
	new Pos(kStop1, kStop4),
	new Pos(kStop2, kStop4),
	new Pos(kStop3, kStop4),
	new Pos(kStop4, kStop4)
];

// the small position each tile should move to when shrunk away from a given expander
// 0 1 2
// 3 4 5
// 6 7 8
var kSmallPositionsFor = [
	// 00      1
	//         2
	//         5
	//         8
	// 3 6 7 4
	[null, 4, 6, 11, 14, 8, 12, 13, 10],
	
	//    11  
	// 0       2
	// 3       5
	// 6       8
	//   4  7  
	[5, null, 6, 7, 12, 8, 9, 14, 10],
	
	// 1      22
	// 0 
	// 3   
	// 4   
	//   6 7 8 5
	[5, 0, null, 7, 9, 15, 12, 13, 14],

	//   0 1 2
	//         4 
	// 33
	//         5 
	//   6 7 8
	[1, 2, 3, null, 6, 10, 12, 13, 14],
	
	// 0   1   2
	//
	// 3  44   5
	//
	// 6   7   8
	[0, 2, 4, 7, null, 8, 11, 13, 15],
	
	//  0 1 2 
	// 3
	//        55
	// 4
	//  6 7 8
	[1, 2, 3, 5, 9, null, 12, 13, 14],
	
	// 3 0 1 2 
	//          4
	//          5
	//          8
	// 66       7
	[1, 2, 3, 0, 6, 8, null, 15, 10],

	//   1   4
	// 0       2
	// 3       5
	// 6       8
	//    77 
	[5, 1, 6, 7, 3, 8, 9, null, 10],
	
	//   0 1 2 5
	// 4
	// 3
	// 6
	// 7      88
	[1, 2, 3, 7, 5, 4, 9, 11, null]
];

var kTileInfo = [

	new TileInfo(kTilePositions[0], "freeman", "about me", null, null, "<div style='text-align:justify;'><p><div style='float: left; margin-right:10px;'><img src='me.jpg' /></div><b>About me</b></p><p>I'm a Google software engineer by profession and a writer by obsession.</p><p>I've been writing all my life: poetry in high school and college, and then short fiction as well, and starting in 2002, young adult fiction and picture book texts.</p><p>My biggest current writing projects are my <a href='http://www.PleasePublishJoan.com' target='_blank'>novelization of the life of Joan of Arc</a> and my ongoing attempt (since July, 2010!) to write <a href='http://www.HaikuDiem.com?src=fng' target='_blank'>one haiku a day</a>.</p></div>"),

/*
	new TileInfo(kTilePositions[1], "pearlcards", "Pearl Cards: a creative, collaborative storytelling game", "http://www.PearlCards.com?src=fn", "<b>Pearl Cards</b><br><br>Pearl Cards is a creative collaborative storytelling game that you can play for free online on Facebook or through email. You can also buy physical decks of Pearl Cards to play in person with your friends or family.", null),
*/

	new TileInfo(kTilePositions[1], "politipedia", "Politipedia: essential knowledge for understanding today&apos;s politics", "http://www.Politipedia.us?src=fn", "<b>Politipedia</b><br><br>Politipedia is my weekly feed of political definitions that I hope will be both entertaining and enlightening.", null),

	new TileInfo(kTilePositions[2], "haikustory", "Haiku Story: a touching tale of father and son, conflict and reconciliation, told entirely in haiku form!", "http://www.haikudiem.com/blog/2011/02/16/haiku-story", "<b>Haiku Story</b><br><br>A touching tale of father and son, conflict and reconciliation, told entirely in haiku form!", null),

	new TileInfo(kTilePositions[3], "joan", "My novelization of the life of Joan of Arc", "http://www.PleasePublishJoan.com?src=fng", "<b>Please Publish Joan!</b><br><br>Help me get my novelization of the life of Joan of Arc published by reading excerpts from the book on this site and following the page on Facebook if you like what you read. If the page's fan count gets high enough, it could convince editors that a book written <i>entirely in the second person</i> can still sell!", null),

	new TileInfo(kTilePositions[4], "haikudiem", "Haiku Diem: follow my attempt to write one new haiku every day!", "http://www.HaikuDiem.com?src=fng", "<b>Haiku Diem</b><br><br>I've been writing one new haiku a day since July, 2010! You can read them all here, and also arrange to receive each day's haiku through Facebook or Twitter or by subscribing to an RSS feed or email list.", null),

	new TileInfo(kTilePositions[5], "santa", "Santa: a dark transcendent take on Old Saint Nick", "http://www.FreemanNg.net/Santa", "<b>Santa</b><br><br>This is a dark, yet ultimately transcendent fantasy story that got rejected by all the science fiction and fantasy magazines I submitted it to. I think they were all wrong to reject it, and I hope you'll agree!", null),

	new TileInfo(kTilePositions[6], "d3", "The Daughters Three: An Impossible Picture Book", "http://www.TheDaughtersThree.com", "<b>The Daughters Three</b><br>An Impossible Picture Book<br><br>An illustrator friend and I are working on a picture book based on this poem I wrote many years ago. We'll be self-publishing it, as publishers don't produce picture books over 32 pages these days, while we believe it will take at least 60 to do full justice to the poetry!", null),

	new TileInfo(kTilePositions[7], "follow", "Facebook - Twitter - LinkedIn - email - blog - mailing list", null, null, "Please follow me on your social network of choice or subscribe to my blog or mailing list to get all the news about my various endeavors.<hr/><br><center><table><tr><td valign='top'><img src='facebook.jpg' style='margin-top:2;'/></td><td><iframe src='http://www.facebook.com/plugins/like.php?app_id=1522968770&href=http://www.FreemanNg.net&send=false&layout=standard&width=360&show_faces=false&action=like&colorscheme=light&font&height=35' scrolling='no' frameborder='0' style='border:none; overflow:hidden; width:285px; height:35px;' allowTransparency='true'></iframe> </td></tr></table> </p> <p> <a href='https://plus.google.com/114058914080826728768' target='_blank'><img src='googleplus.gif' style='border:0;' /></a> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href='http://www.twitter.com/authorfreeman' target='_blank'><img src='twitter.gif' style='border:0;'/></a> </p><p><a href='http://www.FreemanNg.net/blog' target='_blank'><img src='blog.gif' style='border:0;'/></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href='http://open.salon.com/blog/authorfreeman' target='_blank'><img src='opensalon.jpg' style='border:0;'></a></p><p><a href='mailto:Freeman@FreemanNg.net'> Freeman@FreemanNg.net</a></div></td></tr></table></center>")
];
 

