var actionkit = new ActionKit.UnicastDispatcher();
var remoteaction = new ActionKit.UnicastDispatcher();
var renderkit = new RenderKit();

function flatten(obj,includePrototype,into,prefix) {
	// from http://stackoverflow.com/questions/963607
	into = into||{};
	prefix = prefix||'';
	for(var k in obj) {
		if((includePrototype)||(obj.hasOwnProperty(k))) {
			var prop = obj[k];
			if((prop)&&(typeof prop === 'object')&&(!((prop instanceof Date)||(prop instanceof RegExp))))
				arguments.callee(prop,includePrototype,into,prefix+k+'.');
			else into[prefix+k] = prop;
		}
	}
	return into;
}

actionkit.registerAll(flatten({
	render: {
		demo: {
			load: function(data,path) {
				renderkit.clear();
				renderkit.ins('fetch',actionkit.delegate('render.file.fetch',{position:0,url:'http://www.google.com/intl/en_ALL/images/logo.gif'}),true,0);
				renderkit.ins('gaussian blur',actionkit.delegate('render.filter.gaussianblur',{position:1}),true,1);
				renderkit.ins('edge detect',actionkit.delegate('render.filter.edgedetect',{position:2}),true,2);
				renderkit.ins('negate',actionkit.delegate('render.filter.negate',{position:3}),true,3);
			}
		},
		file: {
			open: function(data,path) {
				switch(data.method) {
					case 'setup':	// label,state,position
						var cookie = $.cookie('history'), history = [];
						if(cookie) {
							cookie = cookie.split(':');
							var shown = {};
							for(var i = 0; i < cookie.length; i++) if((cookie[i])&&(!(cookie[i] in shown))) {
								shown[cookie[i]] = true;
								history.push('<img style="border: 1px dashed gray;" src="thumbnail.php?sz=100&rsrc='+cookie[i]+'" onclick="$(\'#file-open\').val(\''+cookie[i]+'\');$(\'#jqi_state0_buttonOK\').click()" />');
							}
						}
						$.prompt((history.length>0?('load image from history:<div style="width: 100%; white-space: nowrap; overflow: auto;">'+history.join('&nbsp;')+'</div>'):'')+'fetch image from url:<br /><input id="file-open" type="text" name="rsrc" value='+JSON.stringify(data.rsrc?data.rsrc:'http://www.google.com/intl/en_ALL/images/logo.gif')+' style="width: 100%;" />',{
							overlayspeed: 'fast',
							buttons: { OK: true, Cancel: false },
							callback: function(v,m,f) {
								if(!v) return;
								if(!f.rsrc) return;
								var insert = data.position<0;
								if(insert) data.position = renderkit.len();
								data.rsrc = f.rsrc;
								var desc;
								try {
									desc = data.rsrc.match(/^(?:f|ht)tp(?:s)?\:\/\/([^\/]+)/im)[1].toString().split('.').slice(-2).join('.')
								} catch(e) {
									if(!(e instanceof TypeError)) throw e;
									desc = data.rsrc.substr(0,4)+'&middot;&middot;&middot;'+data.rsrc.substr(data.rsrc.length-4);
								}
								renderkit[insert?'ins':'set']('open '+desc,actionkit.delegate(path,data),true,data.position);
							}
						});
						break;
					case 'render':
						return ['file_open',['quote',data.rsrc]];
				}
			},
			save: function(data,path) {	// url
				switch(data.method) {
					case 'setup':	// label,state,position
						var url = location.href.split('/').slice(0,-1).join('/')+'/download.php?rsrc='+$('#image').attr('src').split('/')[1];
						var stringified = JSON.stringify(url), escaped = escape(url);
						$.prompt('save image:<br />'
							+'&raquo;<a href='+JSON.stringify($('#image').attr('src'))+'>download</a> '
							+'&raquo;<a target="_blank" href="http://imageshack.us/transload.php?rembar=1&url='+escaped+'">ImageShack</a> '
							+'&raquo;<a target="_blank" href="http://kimag.es/download/do.php?url='+escaped+'">kimages</a> ',{
							overlayspeed: 'fast'
						});
						break;
					case 'render':
						return ['fetch',['quote',data.url]];
				}
			},
			close: function(data,path) {
				switch(data.method) {
					case 'setup':
						renderkit.clear();
				}
			},
			bookmarklet: function(data,path) {
				switch(data.method) {
					case 'setup':
						$.prompt('<a id="bookmarklet">Edit Image</a><br />Bookmark the above link for easy access to Imagine. To edit an image on any Web page, simply activate this bookmarklet and click the image you want to edit.',{overlayspeed:'fast'});
						$('#bookmarklet').attr('href',decodeURIComponent("javascript%3A(function()%7Bi%3Ddocument.images%2Cm%3D%7B%7D%2Cd%3D''%3Bfor(c%3D0%3Bc%3Ci.length%3Bc%2B%2B)%7Bif(i%5Bc%5D.src%20in%20m)continue%3Bm%5Bi%5Bc%5D.src%5D%3D1%3Bd%2B%3D'%3Cimg%20src%3D%22'%2Bi%5Bc%5D.src%2B'%22onclick%3D%22location.replace(%5C'http%3A%2F%2Finportb.com%2Fimagine%2F%23open%2F'%2BencodeURIComponent(i%5Bc%5D.src)%2B'%5C')%22%3E%3Cbr%2F%3E'%7D%3Bif(d!%3D'')%7Bw%3Dwindow.open()%3Bw.document.write('%3Ccenter%3E'%2Bd%2B'%3C%2Fcenter%3E')%3Bw.document.close()%7Delse%7Balert('No%20images!')%7D%7D)()"));
				}
			}
		},
		geometry: {
			scale: function(data,path) {
				switch(data.method) {
					case 'setup':
						$.prompt('scale image (percent values accepted):<br /><input type="text" name="sx" value="'+(data.sx?data.sx:$('#image').attr('width'))+'" style="width: 4em;" />W<br /><input type="text" name="sy" value="'+(data.sy?data.sy:$('#image').attr('height'))+'" style="width: 4em;" />H',{
							overlayspeed: 'fast',
							buttons: { OK: true, Cancel: false },
							callback: function(v,m,f) {
								if(!v) return;
								var insert = data.position<0;
								if(insert) data.position = renderkit.len();
								data.sx = f.sx;
								data.sy = f.sy;
								renderkit[insert?'ins':'set']('scale '+data.sx+' '+data.sy,actionkit.delegate(path,data),true,data.position);
							}
						});
						break;
					case 'render':
						var sx = data.sx, sy = data.sy;
						sx = sx.charAt(sx.length-1)=='%'?['mul',['width'],sx.substr(0,sx.length-1)/100.0]:Math.round(sx);
						sy = sy.charAt(sy.length-1)=='%'?['mul',['height'],sy.substr(0,sy.length-1)/100.0]:Math.round(sy);
						return ['geometry_resize',sx,sy,true];
				}
			},
			crop: function(data,path) {
				switch(data.method) {
					case 'setup':
						var select = $('#image')[0].select, px = 0, py = 0, sx = 0, sy = 0;
						if((select)&&(select.w > 0)&&(select.h > 0)) {
							px = select.x;
							py = select.y;
							sx = select.w;
							sy = select.h;
						} else {
							sx = $('#image').attr('width');
							sy = $('#image').attr('height');
						}
						$.prompt('crop image:<br /><input type="text" name="px" value="'+(data.px?data.px:px)+'" style="width: 4em;" />X<br /><input type="text" name="py" value="'+(data.py?data.py:py)+'" style="width: 4em;" />Y<br /><input type="text" name="sx" value="'+(data.sx?data.sx:sx)+'" style="width: 4em;" />W<br /><input type="text" name="sy" value="'+(data.sy?data.sy:sy)+'" style="width: 4em;" />H',{
							overlayspeed: 'fast',
							buttons: { OK: true, Cancel: false },
							callback: function(v,m,f) {
								if(!v) return;
								var insert = data.position<0;
								if(insert) data.position = renderkit.len();
								data.px = Math.round(f.px);
								data.py = Math.round(f.py);
								data.sx = Math.round(f.sx);
								data.sy = Math.round(f.sy);
								renderkit[insert?'ins':'set']('crop '+data.px+' '+data.py+' '+data.sx+' '+data.sy,actionkit.delegate(path,data),true,data.position);
							}
						});
						break;
					case 'render':
						return ['geometry_crop',data.px,data.py,data.sx,data.sy];
				}
			}
		},
		filter: {
			negate: function(data,path) {
				switch(data.method) {
					case 'setup':
						if(data.position >= 0) return;
						data.position = renderkit.len();
						renderkit.ins('negate',actionkit.delegate(path,data),true,data.position);
						break;
					case 'render':
						return ['filter_negate'];
				}
			},
			grayscale: function(data,path) {
				switch(data.method) {
					case 'setup':
						if(data.position >= 0) return;
						data.position = renderkit.len();
						renderkit.ins('grayscale',actionkit.delegate(path,data),true,data.position);
						break;
					case 'render':
						return ['filter_grayscale'];
				}
			},
			brightness: function(data,path) {
				switch(data.method) {
					case 'setup':
						$.prompt('brightness:<br /><input type="text" name="value" value='+JSON.stringify(data.value?data.value:0)+' style="width: 4em;" /> integer from -255 to 255',{
							overlayspeed: 'fast',
							buttons: { OK: true, Cancel: false },
							callback: function(v,m,f) {
								if(!v) return;
								var insert = data.position<0;
								if(insert) data.position = renderkit.len();
								data.value = f.value;
								renderkit[insert?'ins':'set']('brightness '+data.value,actionkit.delegate(path,data),true,data.position);
							}
						});
						break;
					case 'render':
						return ['filter_brightness',Math.round(data.value)];
				}
			},
			contrast: function(data,path) {
				switch(data.method) {
					case 'setup':
						$.prompt('contrast:<br /><input type="text" name="value" value='+JSON.stringify(data.value?data.value:0)+' style="width: 4em;" /> integer from -100 to 100',{
							overlayspeed: 'fast',
							buttons: { OK: true, Cancel: false },
							callback: function(v,m,f) {
								if(!v) return;
								var insert = data.position<0;
								if(insert) data.position = renderkit.len();
								data.value = f.value;
								renderkit[insert?'ins':'set']('contrast '+data.value,actionkit.delegate(path,data),true,data.position);
							}
						});
						break;
					case 'render':
						return ['filter_contrast',Math.round(data.value)];
				}
			},
			colorize: function(data,path) {
				switch(data.method) {
					case 'setup':
						$.prompt('colorize:<br /><input type="text" name="valueR" value='+JSON.stringify(data.valueR?data.valueR:0)+' style="width: 4em;" /> red: integer from -255 to 255<br /><input type="text" name="valueG" value='+JSON.stringify(data.valueG?data.valueG:0)+' style="width: 4em;" /> green: integer from -255 to 255<br /><input type="text" name="valueB" value='+JSON.stringify(data.valueB?data.valueB:0)+' style="width: 4em;" /> blue: integer from -255 to 255',{
							overlayspeed: 'fast',
							buttons: { OK: true, Cancel: false },
							callback: function(v,m,f) {
								if(!v) return;
								var insert = data.position<0;
								if(insert) data.position = renderkit.len();
								data.valueR = f.valueR, data.valueG = f.valueG, data.valueB = f.valueB;
								renderkit[insert?'ins':'set']('colorize '+data.valueR+' '+data.valueG+' '+data.valueB,actionkit.delegate(path,data),true,data.position);
							}
						});
						break;
					case 'render':
						return ['filter_colorize',Math.round(data.valueR),Math.round(data.valueG),Math.round(data.valueB)];
				}
			},
			hue: function(data,path) {
				switch(data.method) {
					case 'setup':
						$.prompt('hue:<br /><input type="text" name="valueR" value='+JSON.stringify(data.valueR?data.valueR:0)+' style="width: 4em;" /> red: integer from 0 to 255<br /><input type="text" name="valueG" value='+JSON.stringify(data.valueG?data.valueG:0)+' style="width: 4em;" /> green: integer from 0 to 255<br /><input type="text" name="valueB" value='+JSON.stringify(data.valueB?data.valueB:0)+' style="width: 4em;" /> blue: integer from 0 to 255',{
							overlayspeed: 'fast',
							buttons: { OK: true, Cancel: false },
							callback: function(v,m,f) {
								if(!v) return;
								var insert = data.position<0;
								if(insert) data.position = renderkit.len();
								data.valueR = f.valueR, data.valueG = f.valueG, data.valueB = f.valueB;
								renderkit[insert?'ins':'set']('hue '+data.valueR+' '+data.valueG+' '+data.valueB,actionkit.delegate(path,data),true,data.position);
							}
						});
						break;
					case 'render':
						return ['filter_hue',Math.round(data.valueR),Math.round(data.valueG),Math.round(data.valueB)];
				}
			},
			edgedetect: function(data,path) {
				switch(data.method) {
					case 'setup':
						if(data.position >= 0) return;
						data.position = renderkit.len();
						renderkit.ins('edge detect',actionkit.delegate(path,data),true,data.position);
						break;
					case 'render':
						return ['filter_edgedetect'];
				}
			},
			emboss: function(data,path) {
				switch(data.method) {
					case 'setup':
						if(data.position >= 0) return;
						data.position = renderkit.len();
						renderkit.ins('emboss',actionkit.delegate(path,data),true,data.position);
						break;
					case 'render':
						return ['filter_emboss'];
				}
			},
			gaussianblur: function(data,path) {
				switch(data.method) {
					case 'setup':
						if(data.position >= 0) return;
						data.position = renderkit.len();
						renderkit.ins('gaussian blur',actionkit.delegate(path,data),true,data.position);
						break;
					case 'render':
						return ['filter_gaussianblur'];
				}
			},
			selectiveblur: function(data,path) {
				switch(data.method) {
					case 'setup':
						if(data.position >= 0) return;
						data.position = renderkit.len();
						renderkit.ins('selective blur',actionkit.delegate(path,data),true,data.position);
						break;
					case 'render':
						return ['filter_selectiveblur'];
				}
			},
			meanremoval: function(data,path) {
				switch(data.method) {
					case 'setup':
						if(data.position >= 0) return;
						data.position = renderkit.len();
						renderkit.ins('mean removal',actionkit.delegate(path,data),true,data.position);
						break;
					case 'render':
						return ['filter_meanremoval'];
				}
			}
		},
		convolve: {
			smooth: function(data,path) {
				switch(data.method) {
					case 'setup':
						$.prompt('smooth:<br /><input type="text" name="value" value='+JSON.stringify(data.value?data.value:0)+' style="width: 4em;" /> float',{
							overlayspeed: 'fast',
							buttons: { OK: true, Cancel: false },
							callback: function(v,m,f) {
								if(!v) return;
								var insert = data.position<0;
								if(insert) data.position = renderkit.len();
								data.value = parseFloat(f.value);
								renderkit[insert?'ins':'set']('smooth '+data.value,actionkit.delegate(path,data),true,data.position);
							}
						});
						break;
					case 'render':
						return ['convolve_smooth',data.value];
				}
			},
			mean: function(data,path) {
				switch(data.method) {
					case 'setup':
						if(data.position >= 0) return;
						data.position = renderkit.len();
						renderkit.ins('mean',actionkit.delegate(path,data),true,data.position);
						break;
					case 'render':
						return ['convolve_custom',1,1,1,1,1,1,1,1,1,9,0];
				}
			},
			laplacian: function(data,path) {
				switch(data.method) {
					case 'setup':
						$.prompt('laplacian:<br /><input type="text" name="value" value='+JSON.stringify(data.value?data.value:0)+' style="width: 4em;" /> float > 0',{
							overlayspeed: 'fast',
							buttons: { OK: true, Cancel: false },
							callback: function(v,m,f) {
								if(!v) return;
								var insert = data.position<0;
								if(insert) data.position = renderkit.len();
								data.value = parseFloat(f.value);
								renderkit[insert?'ins':'set']('laplacian '+data.value,actionkit.delegate(path,data),true,data.position);
							}
						});
						break;
					case 'render':
						var val = data.value>0?data.value:0;
						return ['convolve_custom',0,val,0,val,-4*val,val,0,val,0,1,0];
				}
			},
			emboss: function(data,path) {
				switch(data.method) {
					case 'setup':
						if(data.position >= 0) return;
						data.position = renderkit.len();
						renderkit.ins('color emboss',actionkit.delegate(path,data),true,data.position);
						break;
					case 'render':
						return ['convolve_custom',-2,-1,0,-1,1,1,0,1,2,1,0];
				}
			},
			blur: function(data,path) {
				switch(data.method) {
					case 'setup':
						$.prompt('blur:<br /><input type="text" name="value" value='+JSON.stringify(data.value?data.value:0)+' style="width: 4em;" /> float',{
							overlayspeed: 'fast',
							buttons: { OK: true, Cancel: false },
							callback: function(v,m,f) {
								if(!v) return;
								var insert = data.position<0;
								if(insert) data.position = renderkit.len();
								data.value = parseFloat(f.value);
								renderkit[insert?'ins':'set']('blur '+data.value,actionkit.delegate(path,data),true,data.position);
							}
						});
						break;
					case 'render':
						var val = data.value>0?1/data.value:1e6;
						return ['convolve_custom',1,val,1,val,Math.pow(val,2),val,1,val,1,4+4*val+Math.pow(val,2),0];
				}
			},
			sharpen: function(data,path) {
				switch(data.method) {
					case 'setup':
						$.prompt('sharpen:<br /><input type="text" name="value" value='+JSON.stringify(data.value?data.value:0)+' style="width: 4em;" /> float',{
							overlayspeed: 'fast',
							buttons: { OK: true, Cancel: false },
							callback: function(v,m,f) {
								if(!v) return;
								var insert = data.position<0;
								if(insert) data.position = renderkit.len();
								data.value = parseFloat(f.value);
								renderkit[insert?'ins':'set']('sharpen '+data.value,actionkit.delegate(path,data),true,data.position);
							}
						});
						break;
					case 'render':
						var val = data.value>0?1/data.value:1e6;
						return ['convolve_custom',0,-1,0,-1,val+4,-1,0,-1,0,val,0];
				}
			}
		}
	},
	menu: {
		help: {
			homepage: function(data,path) {
				if(!window.open('http://inportb.com/')) $.prompt('cannot open new window! close workspace and redirect?',{
					overlayspeed: 'fast',
					buttons: { No: false, Yes: true },
					callback: function(v,m,f) {
						if(v) location.href = 'http://inportb.com/';
					}
				});
			}
		}
	}
}));

remoteaction.registerAll(flatten({
	url: function(data,path) {
		if((!parenturl)&&(window != window.top)) parenturl = data;
	},
	clear: function(data,path) {
		renderkit.clear();
	},
	queue: function(data,path) {
		renderkit.ins(data.label,data.action,data.state,-1);
	},
	setall: function(data,path) {
		renderkit.clear();
		for(var i = 0, item = null; (i < data.length)&&(item = data[i]); i++) renderkit.ins(item.label,new ActionKit.Action(actionkit,item.action),item.state,-1);
	},
	act: function(data,path) {
		actionkit.invoke(data.method,data.data);
	}
}));

renderkit.registerHandler('len',function(data,e) {
	return $('#renderlist').children().length;
});
renderkit.registerHandler('get',function(data,e) {
	var item = $($('#renderlist').items(data));
	return {label: item.attr('name'), action: item.attr('role'), state: item.hasClass('state_true'), position: data};
});
renderkit.registerHandler('getall',function(data,e) {
	var items = $('#renderlist').items(), item;
	for(var i = 0; i < items.length; i++) {
		item = $(items[i]);
		items[i] = {label: item.attr('name'), action: item.attr('role'), state: item.hasClass('state_true'), position: i};
	}
	return items;
});
renderkit.registerListener('onClear',function(data,e) {
	$('#renderlist').items('empty').chain({
		self: {
			name: '{label}',
			role: '{action}',
			title: '{action}',
			'class': 'state_{state}',
			content: '⇕ {label}'
		}
	});
});
renderkit.registerListener('onInsert',function(data,e) {	// label,action,state,position
	var chain = $('#renderlist');
	var count = chain.children().length;
	if((data.position < 0)||(data.position > count)) data.position = count;
	for(var i = 0; i < data.position; i++) chain.items(i).item({position: i});
	for(var i = data.position; i < count; i++) chain.items(i).item({position: i+1});
	$('#renderlist').items('add',data).items('sort','position');
});
renderkit.registerListener('onDelete',function(data,e) {	// label,action,state,position
	var chain = $('#renderlist').items('remove',data);
});
renderkit.registerListener('onSet',function(data,e) {	// label,action,state,position
	$('#renderlist').items(data.position).item(data);
	$($('#renderlist').items(data.position)).removeClass(data.state?'state_false':'state_true').addClass(data.state?'state_true':'state_false');
});
renderkit.registerListener('onPosition',function(data,e) {	// label,action,state,position
	var chain = $('#renderlist');
	var count = chain.children().length;
	for(var i = 0; i < count; i++) chain.items(i).item({position: i});
});
renderkit.registerListener('onChange',function(data,e) {	// label,action,state,position
	if(arguments.callee.timer) clearTimeout(arguments.callee.timer);
	arguments.callee.timer = setTimeout(function() {
		renderkit.render();
		if(parenturl) {
			var items = $('#renderlist').items(), list = [], item;
			for(var i = 0; i < items.length; i++) {
				item = $(items[i]);
				list.push({label: item.attr('name'), action: item.attr('role'), state: item.hasClass('state_true'), position: i});
			}
			$.postMessage(JSON.stringify(list),parenturl,window.parent);
		}
		arguments.callee.timer = null;
	},100);
});
renderkit.registerListener('onRender',function(data,e) {	// label,action,state,position
	$('#image').attr('src',data?'imgcache/'+data:'spacer.gif');
	var cookie = $.cookie('history');
	cookie = cookie?cookie.split(':'):[];
	if(data) cookie[0] = data;
	else if(cookie[0]) cookie.unshift('');
	var date = new Date();
	date.setTime(date.getTime()+(30*24*60*60*1000));
	$.cookie('history',cookie.join(':'), {expires:date});
});

$.receiveMessage(function(e) {
	try {
		var obj = JSON.parse(e.data);
		remoteaction.invoke(obj.path,obj.data);
	//	remoteaction.invoke(obj.path,{label:obj.label,action:new ActionKit.Action(actionkit,obj.action),state:obj.state});
	//	console.log('parsed',obj);
	} catch(ex) {
	//	console.log('cannot parse:',e)
	}
});
