////////////////////////////////////////////////////////////////////////////////
// Note from the Scriptographer.org Team
//
// In Scriptographer 2.9, we switched to a top-down coordinate system and
// degrees for angle units as an easier alternative to radians.
// 
// For backward compatibility we offer the possibility to still use the old
// bottom-up coordinate system and radians for angle units, by setting the two
// values bellow. Read more about this transition on our website:
// http://scriptographer.org/news/version-2.9.064-arrived/

script.coordinateSystem = 'bottom-up';
script.angleUnits = 'radians';

/*
Code by Jonathan Puckey
Tile Tool - All Rights Reserved
http://www.jonathanpuckey.com

Thanks to Juerg Lehni for his continued work on Scriptographer
and for fixing all the things that needed to be fixed
to make Tile Tool possible!

changelog:
  2006-09-07  fixed dropdowns for pc -> create frames after creating buttons that are within them!
  2006-10-10  updated to work with latest version of Scriptographer (2.018)
  2007-18-03  updated to work with latest version of Scriptographer (2.023)
  2007-16-08  updated to work with latest version of Scriptographer (2.024)
  2010-11-05  updated to work with Palette code, saving +- 250 lines of code in the process
              added support for shift to move tiles below others while drawing, and start / end tiles
              (the code is a mess, but I was young and needed the money)
*/

var values = {
	order: 'Top Most Tiles',
	rotation: 'Straight'
}

var components = {
	tileSets: {
		label: 'Tilesets',
		type: 'list',
		options: [''],
		fullSize: true,
		onChange: function(value) {
			activateTileset(value);
		}
	},
	order: {
		label: 'Tile Order',
		type: 'list',
		fullSize: true,
		options: ['Top Most Tiles', 'Random Tiles', 'Sequential Tiles', 'Image Pixel Values']
	},
	rotation: {
		label: 'Tile Rotation',
		type: 'list',
		fullSize: true,
		options: ['Straight', 'Rotate', 'Rotate to Cursor']
	},
	newTileSet: {
		value: 'Create Tileset',
		type: 'button',
		onClick: function() {
			var values = Dialog.prompt('Create Tileset', {
				tileType: {
					label: 'Type',
					type: 'list',
					options: ['6 corners (centered)', '2 corners (centered)', '10 corners (not centered)', '3 corners (not centered)']
				},
				name: {
					label: 'Name',
					type: 'string',
					value: 'Tileset Name'
				}
			});
			if(values && values.name.length) {
				switch (values.tileType) {
					case '6 corners (centered)':
						var z = ['br', 'bl', 'tl', 'tr', 'hor', 'ver', 'str'];
						break;
					case '2 corners (centered)':
						var z = ['tl', 'ver', 'str'];
						break;
					case '10 corners (not centered)':
						var z = ['br2', 'bl2', 'tl2', 'tr2', 'br', 'bl', 'tl', 'tr', 'hor', 'ver', 'str'];
						break;
					case '3 corners (not centered)':
						var z = ['tl', 'tl2', 'ver', 'str'];
						break;
				}
				addEmptyTileset(z, values.name)
			}
		}
	},
	duplicate: {
		type: 'button',
		value: 'Duplicate Tileset',
		onClick: function() {
			if (hasOpenDocs()) {
				activeLayer = document.activeLayer;
				var selectError = !tilesetsLayer || !activeLayer.parent || activeLayer.parent.name != 'Tiles';
				if (!selectError) {
					var clonedLayer = activeLayer.clone();
					for (i = 0; i < clonedLayer.children.length; i++) {
						// take away 'copy' from their names.
						var child = clonedLayer.children[i];
						child.name = child.name.substring(0, child.name.length - 5);
					}
				} else {
					Error('Please select a Tileset by clicking on it in your layers window first');
				}
			}
		}
	}
}

var pallette = new Palette('Tile Tool', components, values);

var seq, timer, repLayer, curDoc;
var error = false;

var settings = {
	rotate: false
};

var oldLength = 0;
var newLength = 0;

var seq = 0;
var superTile = false;

var comb = {
	a2112: 'tl', a1001: 'tl2',
	a1021: 'tr', a0112: 'tr2',
	a0110: 'br', a1221: 'br2',
	a2110: 'bl2', a1201: 'bl',
	a2111: 'hor', a0111: 'hor',
	a0101: 'hor', a2121: 'hor',
	a0121: 'hor', a2101: 'hor',
	a1011: 'ver', a1211: 'ver',
	a1212: 'ver', a1010: 'ver',
	a1210: 'ver', a1012: 'ver',
	a1112: 'end', a1110: 'end',
	a1121: 'end', a1101: 'end',
	a1111: 'hor'
};

var endCornRot = {
	a1112: 1,
	a1110: 0,
	a1121: 0.5,
	a1101: -0.5
};

var radOffs = {
	tl: -0.5,
	tr: 0,
	br: 0.5,
	bl: 1,
	tl2: 0,
	tr2: 0.5,
	br2: 1,
	bl2: -0.5,
	hor: -0.5,
	ver: -1
};

var cornerRot = {
	tl: 0,
	tr: -0.5,
	br: 1,
	bl: 0.5,
	tl2: 0,
	tr2: -0.5,
	br2: 1,
	bl2: 0.5,
	hor: -0.5,
	ver: 0,
	end: 0,
	active: false
};

function onMouseDrag(event) {
	if (!error)
		moveTile(event.point, false);
}

var activeLayer, refPoint, start;
var tileSets = [];

function onMouseDown(event) {
	error = false;
	start = true;
	activeLayer = document.activeLayer;
	aView = document.activeView;
	if (activeLayer.locked || activeLayer.hidden)
		Error('Please unlock and show the active layer.');
	if (activeLayer.name == 'Tiles' || (activeLayer.parent && (activeLayer.parent.name == 'Tiles' || (activeLayer.parent.parent && activeLayer.parent.parent.name == 'Tiles'))))
		Error('Please select a layer that doesn\'t contain Tilesets');

	if (document.layers.first.locked || document.layers.first.hidden)
		Error('please unlock and show the first layer');

	refPoint = event.point;

	initialize();
}

function onMouseUp(event) {
	if (!error) {
		phBounds = placeholder.bounds;
		if (corn.end && !error && event.delta.length > 0) {
			placeTile();
		}
		placeholder.remove();
	}
}

// ------------------------  The functions  ------------------------ 
var tilesetsLayer, phBounds, placeholder, oldp, p, dir, maxDis, corn;
function initialize() {
	tilesetsLayer = document.layers['Tiles'];
	document.rulerOrigin = new Point(0, 0);
	
	var emptyError = true;
	if (cornerRot.active && corn && corn.tl && corn.ver)
		emptyError = false;
	
	if(corn && corn.str && corn.bl && corn.br && corn.tr && corn.tl && corn.hor && corn.ver)
		emptyError = false;
	if (!error) {
		error = emptyError;
		settings.rotate = (values.rotation == 'Rotate' || superTile) ? 1 : 0;
		//clone the placeholder and move it to the correct position
		placeholder = corn.str.children[0].clone();
		phBounds = placeholder.bounds;
		activeLayer.appendTop(placeholder);
		var offset = new Point();
		if (refPoint.x < 0)
			offset.x = phBounds.width;

		if (refPoint.y < 0)
			offset.y = phBounds.height;
		placeholder.position += -phBounds.point + refPoint - (refPoint % phBounds.size) - offset;
		p = placeholder.position.clone();
		oldp = p;
		dir = new Point(1, 1);
		maxDis = new Point(phBounds.size) / 1.5;
	} else {
		if (tilesetsLayer && tilesetsLayer.children.length > 0) {
			Error('One or more of the layers of the active Tileset are empty');
		} else {
			Error('No Tilesets found');
		}
	}
}

function moveTile(rP, trace) {
	if(!error) {
		refPoint = rP;
		var dis = refPoint - placeholder.position;
		var absDis = dis.abs();
		if (absDis.x >= maxDis.x || absDis.y >= maxDis.y) {
			phBounds = placeholder.bounds.clone();
			//Determine where the placeholder should be placed
			var offset = dis / absDis * phBounds.size;
			placeholder.position += (absDis.x > absDis.y) ? [offset.x, 0] : [0, offset.y];

			//place a corner tile
			placeTile();

			//Tile until we can Tile no more.
			if (!trace)
				moveTile(aView.mousePoint, false);
			else if (trace == 2) moveTile(rP, 2);
		}
	}
}

function placeTile() {
	oldp = p;
	p = placeholder.position.clone();

	oDir = dir;
	dir = p - oldp;

	if (dir.x)
		dir.x = dir.x / Math.abs(dir.x);
	if (dir.y)
		dir.y = dir.y / Math.abs(dir.y);
	dir += new Point(1, 1);

	//converts the relative positions of the current placeholder and the last one
	//to a string that points to the corner that needs to be cloned
	var cName = comb['a' + dir.x + dir.y + oDir.x + oDir.y];
	var repLayer = corn[cName];

	if (start) {
		if (corn.start)
			repLayer = corn.start;
		start = false;
	}

	var repC = repLayer.children;
	if (values.order == 'Random Tiles') {
		newRep = repC[Math.round(Math.random() * (repC.length - 1))].clone();
	} else {
		if (values.order == 'Sequential Tiles') {
			seq--;
			if (seq < 0)
				seq = repC.length - 1;
			if (seq < repC.length - 1) {
				newRep = repC[seq].clone();
			} else {
				seq = repC.length - 1;
				newRep = repC[repC.length - 1].clone();
			}
		} else {
			if (values.order == 'Image Pixel Values') {
				var rep = Math.round((repC.length - 1) * getGray());
				newRep = repC[repC.length - rep - 1].clone();
			} else {
				newRep = repC[0].clone();
			}
		}
	}
	if(Key.isDown('shift')) {
		activeLayer.appendBottom(newRep);
	} else {
		activeLayer.appendTop(newRep);
	}
	newRep.position = phBounds.center;
	if (cornerRot.active)
		newRep.rotate(cornerRot[cName] * Math.PI);
	if (values.rotation == 'Rotate to Cursor') {
		var radians = Math.atan2(newRep.position.x - refPoint.x, newRep.position.y - refPoint.y) * -1;
		newRep.rotate(radians + radOffs[cName] * Math.PI);
	} else if (settings.rotate | cName == 'end') {
		if (cName == 'hor' && dir.x == 0)
			newRep.rotate(Math.PI);
		if (cName == 'ver' && dir.y == 0)
			newRep.rotate(Math.PI * -1);
		if (cName == 'end') {
			var rot = endCornRot['a' + dir.x + dir.y + oDir.x + oDir.y];
			if (rot)
				newRep.rotate(Math.PI * rot);
		}
	}

	//if the option key is down, remove the placed Tile again.
	if (Tracker.currentModifiers == 68 || error)
		newRep.remove();

	repLayer = null;
}

// get the amount grey of the pixel beneath the placeholder
function getGray() {
	var rasters = document.getItems({
		type: Raster,
		selected: true
	});
	var color;
	if(rasters.length) {
		var raster = rasters.first;
		color = raster.getAverageColor(phBounds);
	} else {
		Error('Please select an image first!');
	}
	return color ? color.gray : 0;
}

function hasOpenDocs() {
	var open = documents.length > 0;
	if (!open)
		Error('Please open a document first');
	return open;
}

function activateTileset(layer) {
	corn = {};
	if (tilesetsLayer) {
		if(layer) {
			activeSet = layer;
			['bl', 'ver', 'hor', 'str', 'br', 'tl', 'tr', 'bl2', 'br2', 'tl2', 'tr2', 'start', 'end'].each(function(name) {
				var child = layer.children[name];
				if(child)
					corn[name] = child;
			});

			if (corn.bl2 && corn.br2 && corn.tr2 && corn.tl2) {
				cornerRot.active = false;
				superTile = true;
				settings.rotate = true;
			} else if (!corn.br && corn.tl && corn.ver) {
				cornerRot.active = true;
				['br', 'bl', 'tr'].each(function(name) {
					corn[name] = corn.tl;
				});
				if (!corn.tl2) {
					['tl2', 'br2', 'bl2', 'tr2'].each(function(name) {
						corn[name] = corn.tl;
					});
					corn.hor = corn.ver;
					superTile = false;
				} else {
					['br2', 'bl2', 'tr2'].each(function(name) {
						corn[name] = corn.tl2;
					});
					corn.hor = corn.ver;
					superTile = true;
					settings.rotate = true;
				}
			} else {
				cornerRot.active = false;
				superTile = false;
				['tr2', 'tl2', 'bl2', 'br2'].each(function(name) {
					if(!corn[name])
						corn[name] = corn['tl2'.substring(0, 2)];
				});
			}
		}
	}
}

function scanLayers() {
	var first = true;
	tileSets = [];
	if (tilesetsLayer) {
		tilesetsLayer.children.each(function(tSet) {
			if(tSet.children['str'])
				tileSets.push(tSet);
		});
	}
	components.tileSets.options = tileSets;
	components.tileSets.enabled = !!tileSets.length;
	if(components.tileSets.options.first)
		components.tileSets.onChange(components.tileSets.options.first);
}

function Error(message) {
	Dialog.alert(message);
	error = true;
}

function tracePath() {
	var selectedPaths = document.getSelectedItems(Path);
	if (selectedPaths.length) {
		var selectedItem = selectedPaths.first;
		refPoint = selectedItem.getPositionWithLength(0).point;
		activeLayer = document.activeLayer;
		initialize();
		var cornWidth = corn.str.children.first.bounds.width / 2;
		for (i = 0, l = selectedItem.length; i < l; i += cornWidth) {
			moveTile(selectedItem.getPositionWithLength(i).point, true);
		}

		//added to hopefully fill up the last tile that's otherwise left open
		for (i = 0; i < cornWidth; i += cornWidth) {
			moveTile(selectedItem.getPositionWithLength(i).point, true);
		}
		placeholder.remove();
	} else {
		Error('Please select a path.');
	}
}

function docChange() {
	if (documents.length > 0) {
		tilesetsLayer = document.layers['Tiles'];
		if (tilesetsLayer)
			newLength = tilesetsLayer.children.length;
		
		if (document != curDoc || newLength != oldLength) {
			curDoc = document;
			oldLength = newLength;
			corn = null;
			scanLayers();
		}
	} else {
		newLength = 0;
		if (newLength != oldLength) {
			oldLength = newLength;
		}
	}
}

function addEmptyTileset(layerNames, name) {
	if (hasOpenDocs()) {
		var tilesLayer;
		if (!tilesetsLayer) {
			tilesetsLayer = new Layer() {
				name: 'Tiles'
			};
		}
		var newTileset = new Layer() {
			name: name
		};
		tilesetsLayer.appendTop(newTileset);
		for (i = 0; i < layerNames.length; i++) {
			var p = new Layer() {
				name: layerNames[i]
			};
			newTileset.appendTop(p);
		}
	}
}

docChange();	
timer = setInterval(docChange, 600);