var TagMap = Class.create()
TagMap.prototype = {
    initialize: function(layer, tags, factory){
	this.layer = layer;
	this.element = $(factory.getTagMapID());
	this.scale = factory.getTagMapScale();
	var alltags = $("#all-tags");
	alltags.html("");
	for (var i = 0; i < tags.length; i++){
	    var link = document.createElement("a");
	    link.className = "tag-map-link";
	    link.innerHTML = tags[i].name;
	    link.style.left = Math.floor(tags[i].pos[0] * this.scale - tags[i].name.length * 1.5) + "px";
	    link.style.top  = Math.floor(tags[i].pos[1] * this.scale - 4) + "px";
	    link.href = "javascript:void(this)";
	    this.element[0].appendChild(link);
	    var handler = function(i_){
		return function(event){
		    event.stopPropagation();
		    event.preventDefault();
		    // ToDo: refine parameters 
		    layer.scroll(tags[i_].left - 
				 layer.getWidth() / 2,
				 tags[i_].top  - 
				 layer.getHeight() / 2
				);
		}
	    }(i);
	    $(link).click(handler);
	    
	    var link2 = document.createElement("a");
	    link2.innerHTML = tags[i].name;
	    link2.href = "javascript:void(this)";
	    alltags.append(link2).append(document.createTextNode(" "));
	    $(link2).click(handler);
	}
	this.area = $(document.createElement("div"));
	this.area.addClass("tag-map-area");
	this.element.append(this.area);
    },
    onMove: function(x, y){
	this.area[0].style.left = Math.floor(x / 75 * this.scale) + "px";
	this.area[0].style.top = Math.floor(y / 75 * this.scale) + "px";
    },
    onResize: function(){
	this.area[0].style.width  = Math.floor(this.layer.getWidth() / 75 * this.scale) + "px";
	this.area[0].style.height = Math.floor(this.layer.getHeight() / 75 * this.scale) + "px";
    }
}


var Loader = Class.create();
Loader.prototype = {
    initialize: function(factory){
	var self = this;
	this.loading = false;
	this.cache = {}
	this.path = factory.getLoaderPath();
	this.onLoadHandler = factory.getOnLoadHandler();
    },
    fetch: function(page){
	if (!this.loading && !this.cache[page]) {
	    this.loading = true;
	    var self = this;
	    //$.get("/flickr", {"page": page}).next(function(text){
	    $.get(this.path + page + "_json.html").next(function(text){
		var json = eval("(" + text + ")");
		self.loading = false;
		self.cache[page] = true;
		self.onLoadHandler(json);
	    }).
	    error(function(error){
		alert("Load error occured.");
		console.log(error);
	    });
	}
    }
}

var PhotoList = Class.create();
PhotoList.prototype = {
    initialize: function(loader){
	this.loader = loader
	this.cache = {};
    },
    getInfo: function(x, y){
	if (info = this.cache[x + "_" + y]){
	    return info;
	} else {
	    this.loader.fetch(this.getPage(x, y));
	    return null;
	}
    },
    getPage: function(x, y){
	return [Math.floor(x / 10), "_", Math.floor(y / 10)].join("");
    },
    aadPhotos: function(photos){
	for (var i = 0; i < photos.length; i++){
	    this.addPhoto(i);
	}
    },
    addPhoto: function(photo){
	var key = photo.x + "_" + photo.y;
	this.cache[key] = photo.info;
    }
    
}

var Layer = Class.create();
Layer.prototype = {
    initialize: function(element, factory){
	this.element = $(element);
	this.factory = factory;
	this.x = 0;
	this.y = 0;
	this.lastScrollTop = this.element[0].scrollTop;
	this.lastScrollLeft = this.element[0].scrollLeft
	this.interval = 500;
	this.checkHandler = this.scrollCheck.bind(this);
	this.timer = setInterval(this.checkHandler, this.interval);
	$(window).resize(this.onResize.bind(this));
    },
    scrollCheck: function(){
	if (this.lastScrollTop  != this.element[0].scrollTop ||
	    this.lastScrollLeft != this.element[0].scrollLeft){
	    this.lastScrollTop = this.element[0].scrollTop;
	    this.lastScrollLeft = this.element[0].scrollLeft
	    this.onScroll();
	    clearInterval(this.timer);
	    this.timer = setInterval(this.checkHandler, this.interval);
	}
    },
    scroll: function(x, y){
	this.element[0].scrollLeft = x;
	this.element[0].scrollTop = y;
	this.lastScrollTop = this.element[0].scrollTop;
	this.lastScrollLeft = this.element[0].scrollLeft
	this.onScroll();
    },
    moveReal: function(x, y){
	this.x = x;
	this.y = y;
	if (this.x < this.minX){
	    this.x = this.minX;
	}
	if (this.y < this.minY){
	    this.y = this.minY;
	}
	if (this.x > this.maxX){
	    this.x = this.maxX;
	}
	if (this.y > this.maxY){
	    this.y = this.maxY;
	}
	this.factory.getTagMap().onMove(this.x, this.y);
    },
    move: function(x, y){
	this.moveReal(x, y);
	this.tileLayout().update(this.x, this.y);
    },
    getHeight: function(){
	return this.layerHeight;
    },
    getWidth: function(){
	return this.layerWidth;
    },
    tileLayout: function(){
	return this.factory.getTileLayout();
    },
    moveByScroll: function(diffX, diffY){
	this.element[0].scrollLeft += diffX;
	this.element[0].scrollTop  += diffY;
    },
    onScroll: function(){
	this.move(this.element[0].scrollLeft, this.element[0].scrollTop);
    },
    onResize: function(){
	var height = $(window).height() - this.factory.getHeightMargin();
	this.layerHeight = height;
	this.layerWidth = this.element.width();
	this.element.css("height", this.layerHeight);
	this.tileLayout().onResize(this);
	$("#sidebar").height(this.layerHeight);
	this.maxX = this.tileLayout().maxX;
	this.minX = this.tileLayout().minX;
	this.maxY = this.tileLayout().maxY;
	this.minY = this.tileLayout().minY;
	this.tileLayout().update(this.x, this.y);
	// tag-map
	this.factory.getTagMap().onResize(this);
    }
}

var Tile = Class.create();
Tile.prototype = {
    initialize: function(layer, photoList){
	this.element = document.createElement("div");
	this.aTag = document.createElement("a");
	this.imgTag = document.createElement("img")
	this.spanTag = document.createElement("span");
	this.element.appendChild(this.aTag);
	this.element.appendChild(this.spanTag);
	this.aTag.appendChild(this.imgTag);
	$(this.aTag).lightBox();
	this.element.onmouseover = this.onMouseOver.bindAsEventListener(this);
	this.nox = this.noy = null;
	this.state = 0;
	this.photoList = photoList;
	this.element.className = "tile";
	layer.element[0].appendChild(this.element);
	this.element_style = this.element.style;
    },
    update: function(nox, noy, top, left){
	if (this.nox != nox || this.noy != noy){
	    this.nox = nox;
	    this.noy = noy;
	    this.updateInfo();
	    this.element_style.top = top + "px";
	    this.element_style.left = left + "px";
	}
    },
    checkLoaded: function(){
	if (this.state == 0){
	    this.updateInfo();
	}
    },
    // private
    updateInfo: function(){
	if (info = this.photoList.getInfo(this.nox, this.noy)){
	    this.info = info;
	    if (this.info.loaded){
		this.state = 2;
		this.element_style.background = 
		    "#fff url(/flickr/images/loader2.gif) no-repeat"
		this.showImage();
	    } else {
		this.state = 1;
		this.element_style.background = this.info.color;
		this.showText(info.tags.join(" "));
		//this.showText([this.nox, this.noy].join(" "));
		this.imgTag.src = "/flickr/images/loader2.gif";
	    }
	} else {
	    this.state = 0;
	    this.element_style.background = "#fff";
	    // this.imgTag.src = "images/loader2.gif";
	    this.showText("");
	}
    },
    onMouseOver: function(event){
	if (this.state == 1) {
	    this.element_style.background = 
		"#fff url(/flickr/images/loader2.gif) no-repeat";
	    this.showImage();
	    this.state = 2;
	}
    },
    showText: function(text){
	this.aTag.style.display = "none";
	this.spanTag.innerHTML = text;
	this.spanTag.style.display = "inline";
    },
    showImage: function(){
	var base = ['http://static.flickr.com/',
	    this.info.server,'/', this.info.id, '_', this.info.secret].join("")
	var click_url =  base +  '.jpg';
	var url = base + '_s.jpg';
	this.aTag.href = click_url;
	this.aTag.title =  this.info.title;
	this.aTag.title_url = ["http://www.flickr.com/photos/",
				  this.info.owner, "/", this.info.id].join("");
	this.imgTag.src = url;
	this.info.loaded = true;
	this.spanTag.style.display = "none";
	this.aTag.style.display = "inline";
    }

}

var TagTile = Class.create();
TagTile.prototype = {
    initialize: function(layer, info){
	var element = document.createElement("div");
	element.className = "tag-tile";
	element.innerHTML = info.name;
	element.style.top =  (info.top  -  4)+ "px";
	element.style.left = (info.left - info.name.length * 4) + "px";	
	layer.element[0].appendChild(element);
    }
}

var TileLayout = Class.create();
TileLayout.prototype = {
    initialize: function(layer, tags, factory){
	this.factory = factory;
	this.tileHeight = factory.getTileHeight();
	this.tileWidth  = factory.getTileWidth();
	this.numTotalRows = factory.getNumTotalRow();
	this.numTotalCols = factory.getNumTotalCol();
	this.lastDiv = $(document.createElement("div"));
	var top  = this.numTotalRows * this.tileHeight;
	var left = this.numTotalCols * this.tileWidth;
	this.tiles = [];
	this.numTiles = 0;
	this.pageCache = {};
	this.lastDiv.css({height: "1px", width: "1px", position: "absolute",
			  top: top + "px", left: left +  "px", zIndex: -1});
	layer.element.append(this.lastDiv);
	this.minX = 0;
	this.minY = 0;
	this.maxX = left;
	this.maxY = top;
	this.maxPageX = Math.floor(this.numTotalCols / 10);
	this.maxPageY = Math.floor(this.numTotalRows / 10);
	this.tags = tags;
	for (var i = 0; i < this.tags.length; i++){
	    this.tags[i].top = Math.floor(this.tags[i].pos[1] * this.tileHeight);
	    this.tags[i].left = Math.floor(this.tags[i].pos[0] * this.tileWidth);
	    this.factory.createTagTile(this.tags[i], 
				       this.tags[i].top, 
				       this.tags[i].left);
	}
    },
    onResize: function(layer){
	var oldNumTiles = this.numTiles;
	this.layerWidth = layer.getWidth();
	this.layerHeight = layer.getHeight();
	this.numRows = Math.floor(this.layerHeight / this.tileHeight) + 6;
	this.numCols = Math.floor(this.layerWidth / this.tileWidth) + 6;
	this.numTiles = this.numCols * this.numRows;
	this.pageHeight = this.tileHeight * this.numRows;
	this.pageWidth  = this.tileWidth * this.numCols;
	for (var i = oldNumTiles; i < this.numTiles; i++) {
	    this.tiles[i] = this.factory.createTile();
	}
    },
    checkLoaded: function(){
	for (var i = 0; i < this.tiles.length; i++){
	    this.tiles[i].checkLoaded();
	}
    },
    update: function(x, y){
	var xd = x % this.pageWidth;
	var yd = y % this.pageHeight;
	var xm = Math.floor(x / this.pageWidth);
	var ym = Math.floor(y / this.pageHeight)
	for(var i = 0; i < this.numTiles; i++){
	    this.updateTile(i, i % this.numCols, 
			    Math.floor( i / this.numCols),
			    xd, yd, xm,ym);
	}
    },
    updateTile: function(i, ix, iy, xd, yd, xm, ym){
	var left = this.tileWidth * ix;
	var nox = ix + xm * this.numCols;
	if (left < xd - this.tileWidth * 3){
	    left += this.pageWidth;
	    nox += this.numCols;
	} 
	var top = this.tileHeight * iy;
	var noy = iy + ym * this.numRows;
	if (top < yd - this.tileHeight * 3){
	    top += this.pageHeight;
	    noy += this.numRows;
	} 
	left += this.pageWidth * xm;
	top += this.pageHeight * ym;
	
	if (nox < this.numTotalCols && noy < this.numTotalRows){
	    this.tiles[i].update(nox, noy, 
				 top,
				 left)
	} 
    }
}
