module jg {
	export class ChipSet {
		image:any;
		sep:number;
		tile:Tile;
		tileHeight:number;
		chipOffset:number;

		constructor(tile:Tile, image:any) {
			this.image = image;
			this.tile = tile;
			this.sep = Math.floor(this.image.width / this.tile.tileWidth);
		}

		count():number {
			return Math.round((this.image.width * this.image.height) / (this.tile.tileWidth * this.tile.tileHeight));
		}

		draw(c:CanvasRenderingContext2D, x:number, y:number, chip:number) {
			var tw = this.tile.tileWidth;
			var th = this.tile.tileHeight;
			c.drawImage(
				this.image,
				(chip % this.sep) * tw,
				Math.floor(chip / this.sep) * th,
				tw,
				th,
				x * tw,
				y * th,
				tw,
				th
			);
		}
	}

	export class AutoTileChipSet extends ChipSet {
		map(x:number, y:number) {
			if (x < 0 || y < 0 || x >= this.tile.size.width || y >= this.tile.size.height)
				return -1;
			return this.tile.data[x][y];
		}

		draw(c:CanvasRenderingContext2D, x:number, y:number, chip:number) {
			var tw = this.tile.tileWidth;
			var th = this.tile.tileHeight;
			var tw2 = Math.floor(this.tile.tileWidth/2);
			var th2 = Math.floor(this.tile.tileHeight/2);
			for (var i=0; i < 4; i++) {
				for (var j=0; j < 4; j++) {
					var ox = x + (Math.floor((i + 1) / 2) - 1);
					var oy = y + (Math.floor((j + 1) / 2) - 1);
					var n = this.map(ox, oy);
					if (n == -1)
						continue;

					var tx = ox + (i % 2 == 0 ? 1 : -1);
					var ty = oy + (j % 2 == 0 ? 1 : -1);
					var sel;
					var v = this.map(tx, oy);
					var h = this.map(ox, ty);
					var vh = this.map(tx, ty);
					sel = 0;
					if (h == n)
						sel++;
					if (v == n)
						sel+=2;
					if (sel == 3 && vh == n)
						sel++;

					c.drawImage(
						this.image,
						(sel % this.sep) * tw + tw2 * (i % 2 == 0 ? 1 : 0),
						Math.floor(sel / this.sep) * th + th2 * (j % 2 == 0 ? 1 : 0),
						tw2,
						th2,
						x * tw + tw2 * (i-1),
						y * th + th2 * (j-1),
						tw2,
						th2
					);
				}
			}
		}
	}

	export class Tile extends E {
		tileWidth:number;
		tileHeight:number;
		chips:ChipSet[];
		chipMap: ChipSet[];
		chipCount:number;
		canvas: HTMLCanvasElement;
		data: number[][];
		size:CommonSize;

		constructor(image:any, tileWidth:number, tileHeight:number) {
			super();
			this.tileWidth = tileWidth;
			this.tileHeight = tileHeight;
			this.chips = new ChipSet[];
			this.chipMap = new ChipSet[];
			this.chipCount = 0;
			if (image)
				this.addChipSet(image);
			this.x = 0;
			this.y = 0;
			this.disableTransform = true;
		}

		addChipSet(image:HTMLImageElement, opt?:any) {
			var chipset;
			if (opt) {
				if (opt.autoTile) {
					chipset =new AutoTileChipSet(this, image);
				}
			}

			if (! chipset)
				chipset = new ChipSet(this, image);			

			chipset.chipOffset = this.chipCount;
			this.chips.push(chipset);
			var cnt = chipset.count();
			var cnt2 = this.chipCount + cnt;
			for (var i=this.chipCount; i<cnt2; i++)
				this.chipMap[i] = chipset;
			this.chipCount = cnt2;
		}

		generate(data:number[][], width?:number, height?:number) {
			this.data = data;
			if (! width)
				width = this.data.length;
			if (! height)
				height = this.data[0].length;
			this.size = {
				width: width,
				height: height
			}
			this.width = this.tileWidth * width;
			this.height = this.tileHeight * height;
			this.refresh();
		}

		refresh() {
			this.canvas = window.createCanvas(this.width, this.height);
			var c = this.canvas.getContext("2d");

			for (var x=0; x<this.size.width; x++) {
				for (var y=0; y<this.size.height; y++) {
					if (this.data[x][y] < 0)
						continue;
					var cs = this.chipMap[this.data[x][y]];
					cs.draw(c, x, y, this.data[x][y]-cs.chipOffset);
				}
			}
		}

		draw(context:CanvasRenderingContext2D) {
			var parent = this.parent ? this.parent : this;
			var scroll = parent.scroll ? parent.scroll : {x: 0, y: 0};
			var src:CommonArea = {
				x: -scroll.x,
				y: -scroll.y,
				width: parent.width,
				height: parent.height
			};
			var dist:CommonArea = {
				x: -scroll.x,
				y: -scroll.y,
				width: parent.width,
				height: parent.height
			};
			if (src.x < 0) {
				src.width += src.x;
				if (src.width <= 0)
					return;
				dist.x -= src.x;
				dist.width += src.x;
				src.x = 0;
			} else if ((src.x+src.width) > this.width) {
				var p = ((src.x+src.width) - this.width);
				src.width -= p;
				if (src.width <= 0)
					return;
				dist.width -= p;
			}
			if (src.y < 0) {
				src.height += src.y;
				if (src.height <= 0)
					return;
				dist.y -= src.y;
				dist.height += src.y;
				src.y = 0;
			} else if ((src.y+src.height) > this.height) {
				var p = ((src.y+src.height) - this.height);
				src.height -= p;
				if (src.height <= 0)
					return;
				dist.height -= p;
			}

			context.drawImage(
				this.canvas,
				src.x,
				src.y,
				src.width,
				src.height,
				dist.x,
				dist.y,
				dist.width,
				dist.height
			);
		}
	}
}