YUI.add("didid", function(Y) {

	if(typeof BLUE.didid == 'object')
		return;

	if(!Array.prototype.indexOf)
		Array.prototype.indexOf = function(o) { for(var i=0;i<this.length;i++) if(this[i] == o) return i; return -1; }

	BLUE.didid = { }; // namespace for this module

	var sourceEl;
	var swapEl;
	var ignoreClick = false;

	var curList;
	var dndList;

	// use this to inform server of an item moved.
	// movedItem is item that moved. heh.
	// afterItem is the item it was moved IN FRONT OF - or "end" if it was moved to the end of the list
	function sendMove(movedItemNode,afterItemNode)
	{
		var movedItem = curList.gi(movedItemNode);
		var afterItemId = "end";
		if(afterItemNode != "end")
			afterItemId = curList.gi(afterItemNode).id;
		BLUE.jsonCall("services.xml?name=setOrder&listId=" + curList.id  + "&editKey=" + curList.ekey + "&itemId=" + movedItem.id + "&afterId=" + afterItemId);
	}

	function openminime(e)
	{
		e.halt();
		var url = 'services.xml?name=miniMe&listId=' + curList.id + '&editKey=' + curList.ekey + '&viewKey=' + curList.vkey;
		var name = "mini_" + curList.id; // IE requires no "-" or " "...
		window.open(url,name,'left=30,top=20,width=200,height=275,titlebar=no,location=no,status=no,resizable=yes,scrollbars=no');
	}

	/*function openminimeNew(e)
	{
		e.halt();
		win = window.open("",curList.id,'left=30,top=20,width=200,height=275,titlebar=no,location=no,status=no,resizable=yes,scrollbars=no');
		Y.applyConfig({win: win}); // move Y window to point to mini

		var premount = document.location.href.match("(.*)/.*")[1]; // URL up to slash
		var mmServ = premount + "/services.js?name=showMini&listId=" + curList.id + "&editKey=" + curList.ekey + "&viewKey=" + curList.vkey;

		Y.one("body").ad({tag: "script", src: mmServ});

		Y.applyConfig({win: Y.config.win.opener }); // Reset Y back to main
	}*/

	function showPrintableVersion(a)
	{
		a.halt();
		var url = 'services.xml?name=printwin&listId=' + curList.id + '&editKey=' + curList.ekey + '&viewKey=' + curList.vkey;
		window.open(url,'print','left=30,top=20,width=640,height=480,titlebar=no,location=no,status=no,resizable=yes,scrollbars=yes');
	}

	function showBlogYourTodo(e)
	{
		var pconfig = { width: 720, title: "Blog (or otherwise publish) Your To-Do", buttons: [ ]};
		var url = 'services.xml?name=blogYourList&editKey=' + curList.ekey + '&viewKey=' + curList.vkey + '&listId=' + curList.id;
		YUI().use("dialog", function() {
				BLUE.dialog.showDialogFromModServ(url,pconfig);
				DIDID.setThemeStyles(curList.theme,"#mtodo",.5);
			});
	}

	function sendEmailButtonPushed(e)
	{
		e.halt();
		var pconfig = { width: 720, title: "Email To-Do List URL", buttons: [ ]};
		var url = 'services.xml?name=sendListLinkPanel&editKey=' + curList.ekey + '&viewKey=' + curList.vkey + '&listId=' + curList.id;
		YUI().use("dialog", function() {
				BLUE.dialog.showDialogFromModServ(url,pconfig);
			});
	}

	// This represents a list.  Items are stored in this.items array.
	function DididList(id,vkey,ekey,theme)
	{
		this.id = id;
		this.vkey = vkey;
		this.ekey = ekey;
		this.theme = theme;
		this.items = [ ];

		function addItem(txt)
		{
			var item = {status: "pending", description: txt};
			this.items.push(item);
			return item;
		}

		function setposted(pendingItem,fullItem)
		{
			var i = this.items.indexOf(pendingItem);
			this.items[i] = fullItem;
		}

		function gi(node) // get item from node
		{
			var ind = node.getAttNum("data-itemNum");
			if(ind == null) return null;
			return this.items[ind];
		}

		this.addItem = addItem;
		this.gi = gi;
		this.setposted = setposted;
		this.size = function() { return this.items.length; }
		this.editable = ((this.ekey != null) && (this.ekey != ""));
	}

	// Clears any existing list and renders the curList into the page.
	function renderItems()
	{
		Y.one("#itemsListArea").clear();
		var listNode = Y.one("#itemsListArea").ad({ tag: "ul", klass: "listlist"});
		Y.each(curList.items, function(item,index) { renderOneItem(listNode,item,index); });
		if(curList.editable)
		{
			dndList = new BLUE.dndList.DndList(listNode, { scrollNode: Y.one("#todoList") });
			dndList.on("drop", sendMove)
		}
	}

	// Renders a single todo item at the end of the list.  listNode is optional.
	function renderOneItem(listNode,item,index)
	{
		if(!listNode)
			listNode = Y.one("#itemsListArea ul");
		return listNode.ad({tag: "li", content: item.description, klass: "themeline " + item.status, "data-itemnum": index});
	}

	// Called when a single click is confirmed (i.e. no double-click happened)
	// For open items, this sets them to done.
	// for done items, this archives them...
	function todoItemSingleClicked(itemNode,ind)
	{
		sclick[ind] = false;
		var item = curList.items[ind];

		if(item.status == "open")
		{
			item.status = "done";
			BLUE.jsonCall("services.js?name=didit&listId=" + curList.id + "&editKey=" + curList.ekey + "&itemId=" + item.id);
		}
		else
		{
			BLUE.jsonCall("services.js?name=archive&listId=" + curList.id + "&editKey=" + curList.ekey + "&itemId=" + item.id,
				function() {
						item.status = "archived";
						itemNode.fadeOutHide()
					});
		}
	}

	// Called when double click is confirmed.
	// For open items, this puts you in "edit" mode
	// For done items, this re-opens them
	function todoItemDoubleClicked(itemNode,ind)
	{
		sclick[ind] = false;
		var item = curList.items[ind];

		itemNode.removeClass("done");

		if(item.status == "open")
		{
			BLUE.pageedit.eClickedHandler(itemNode); // will call editedItem() when done
			itemNode.focus();
		}
		else
		{
			item.status = "open";
			itemNode.removeClass("archived");
			BLUE.jsonCall("services.js?name=undoDoneService&listId=" + curList.id + "&editKey=" + curList.ekey + "&itemId=" + item.id);
		}
	}

	var warnedNonEditor = 0;
	var sclick = { };
	function todoItemClicked(e) // this may be single or double click...
	{
		if(!curList.editable)
		{
			warnedNonEditor++;
			if(warnedNonEditor < 2)
				YUI().use("dialog", function() {
						BLUE.dialog.info("You are currently in view-only mode.  To edit this list, you will need the edit key.  Or, create your own list and you will have full edit-ability!");
					});
			return;
		}

		var itemNode = e.target;

		if(itemNode.getAttribute("contenteditable") && itemNode.getAttribute("contenteditable") == "true")
		{
			return; // ignore this click if user is currently editing...
		}

		e.halt();

		if(document.getSelection && document.getSelection().setPosition)
			document.getSelection().setPosition(0);

		var ind = itemNode.getAttNum("data-itemNum");
		var item = curList.items[ind];

		if(item.status == "open")
			itemNode.addClass("done"); // give this feedback immediately - even though if its a double-click we don't "done" it.
		else
			itemNode.addClass("archived"); // agian, this is "pending"...

		if(sclick[ind])
		{
			sclick[ind] = false;
			todoItemDoubleClicked(itemNode,ind);
		}
		else
		{
			sclick[ind] = true;
			setTimeout(function() { if(sclick[ind]) { todoItemSingleClicked(itemNode,ind);} }, 500);
		}
	}

	// Called from pageedit library - after we tell pageedit an edit has begun (see todoItemDoubleClicked)
	function editedItem(node,before,after)
	{
		var ind = node.getAttNum("data-itemNum");
		var item = curList.items[ind];
		item.description = after;
		BLUE.jsonCall("services.js?name=editItem&listId=" + curList.id + "&editKey=" + curList.ekey + "&itemId=" + item.id + "&description=" + encodeURIComponent(after));
	}

	// Lets a user change themes.  This tells the server the new theme
	// and sets the GUI.
	function changeTheme(name)
	{
		var theme = DIDID.themes[name];
		if(!theme)
			BLUE.dialog.alert("Unknown Theme","The theme you are trying to use was not found or has been deleted.  Please try another.");
		else
		{
			DIDID.setThemeStyles(name,"#todo");
			curList.theme = name;
			BLUE.jsonCall("services.js?name=setTheme&listId=" + curList.id + "&editKey=" + curList.ekey + "&theme=" + encodeURIComponent(name));
		}
	}

	// name = name of font, i.e. 'Mountains+of+Christmas::latin'
	function loadGFont(name)
	{
		WebFontConfig = { google: { families: [ name ] } };
		Y.Get.script("http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js");
	}

	function initTodoList(id,vkey,ekey,items,theme)
	{
		// initialize the list
		curList = new DididList(id,vkey,ekey,theme);
		curList.items = items;

		var editable = (ekey != null);
		DIDID.setThemeStyles(theme,"#todo");

		if(editable)
		{
			// add drag-n-drop for all todo items (opened and closed)
			Y.all(".todoEntry").each(function(n) {
					{
						/*var dd = new YAHOO.util.DD(nodes[i]);
						var ddt = new YAHOO.util.DDTarget(nodes[i]);
						dd.startDrag = function(e) { startDrag(this); };
						dd.onDragEnter = function(e,id) { maySwap(id); };
						dd.onMouseUp = function(e,id) { finalizeSwap(); };*/
						n.on("click", todoClick);
					}
				});

			// CTRL-A to give focus to next input line
			BLUE.keyutils.onKey(function() { Y.one("#additem input").focus()} , "A", "CTRL");
			BLUE.keyutils.onKey(function() { Y.one("#additem input").blur()} , "ESC");
		}
		else
		{
			var voAlert = function(e)
			{
				e.halt();
				var msg = "<p>This To-Do list is in <i>'View-Only'</i> mode.  To make changes you need to open it in <i>'Edit Mode'</i>.</p>";
				msg += "<p>If you are the creator of this list, refer to your original URL which includes the edit key.</p>";
				msg += "<p>If you received this URL from the list creator and wish to edit it, you must ask for edit permission.</p>";
				BLUE.alert('View Mode Only', msg,400); // TODO:
			};

			Y.all(".setTheme").each(function(n) { n.on("submit", voAlert); });
			Y.one("#titleForm").on("submit", voAlert);
		}

		renderItems(); // render the todo list

		// Put in event listeners
		var listNode = Y.one("ul.listlist");
		listNode.delegate("click", todoItemClicked, "li");

		function submitNewLine(e)
		{
			e.halt();
			var addField = Y.one("#additem input.entryField");
			var line = addField.get("value");
			if(line != "")
			{
				var item = curList.addItem(line);
				var iNode = renderOneItem(null,item,curList.size()-1);
				if(curList.editable)
					dndList.addItem(iNode);

				// simulate posting to server
				BLUE.jsonCall("services.js?name=newItem&listId=" + curList.id + "&editKey=" + curList.ekey + "&description=" + encodeURIComponent(line),
					function(newitem) { curList.setposted(item,newitem); });

				addField.set("value","");
				Y.one("#additem input.entryField").focus();
			}
		}

		Y.all("#additem").on("submit", submitNewLine); // use all in case it doesn't exist
		Y.all("#additem input.entryField").on("blur", submitNewLine);

		Y.one("#printButton").on("click", showPrintableVersion);
		Y.one("#blogListButton").on("click", showBlogYourTodo);
		Y.one("#mm").on("click", openminime);
		Y.one("#sendEmailButton").on("click", sendEmailButtonPushed);

		BLUE.pageedit.install({parent: Y.one(".listlist"), eSelector: "li", externalClickDetect: true, cbOnEdit: editedItem, enterAcceptsEdits: true});
	}

	function showPicks(themes)
	{
		var tpNode = Y.one("#pickATheme");
		tpNode.clear();
		Y.each(themes, function(theme) {
				var tn = tpNode.ad({tag: "img", src: "images/thumb-" + theme + ".png"});
				tn.on("click", function() {
						changeTheme(theme);
					});
			});
	}

	BLUE.didid.initTodoList = initTodoList;
	BLUE.didid.getCurList = function() { return curList; }
	BLUE.didid.renderItems = renderItems;
	BLUE.didid.showPicks = showPicks;
	BLUE.didid.taSwitch = function() { var h = document.getElementById('ta1').value; document.getElementById('ta1').value = document.getElementById('ta2').value; document.getElementById('ta2').value = h; }

}, '0.0.1', { requires : ['blue','keyutils','pageedit','dndList']});


// Code mostly "borrowed" from YUI example on drag/sort.  Thanks guys!!
YUI.add("dndList", function(Y) {

	if(typeof BLUE.dndList == 'object')
		return;

	BLUE.dndList = { }; // namespace for this module

	function DndList(listNode,conf)
	{
		var me = this; // copy this

		listNode = gn(listNode);

		var goingUp = false, lastY = 0, lastDrop = null;

		// We setsup a delegate for drag...
		var dddel = new Y.DD.Delegate({ container: listNode, nodes: "li" });
		dddel.dd.plug(Y.Plugin.DDConstrained, { stickY: true });
		dddel.dd.plug(Y.Plugin.DDProxy, { moveOnEnd: false });
		if(conf.scrollNode)
			dddel.dd.plug(Y.Plugin.DDNodeScroll, { node: conf.scrollNode });

		// But for drop, we iterate over existing items - so new items NEED TO RUN THIS TOO!
		listNode.all("li").each(function(n) {
				new Y.DD.Drop({ node: n });
			});

		dddel.dd.on('drag:start', function(e) {
				//Get our drag object
				var drag = e.target;
				//Set some styles here
				drag.get('node').addClass("ordering"); // node that pops around list as you order
				drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML'));
				/*drag.get('dragNode').setStyles({
					opacity: '.5',
					borderColor: drag.get('node').getStyle('borderColor'),
					backgroundColor: drag.get('node').getStyle('backgroundColor')
				});*/
				drag.get('dragNode').addClass("dndListProxy");
			});

		// Track whether user is going up or down..
		Y.DD.DDM.on('drag:drag', function(e) {
				var y = e.target.lastXY[1];
				if(y < lastY) { goingUp = true; }
				else { goingUp = false; }
			lastY = y; });

		Y.DD.DDM.on('drop:over', function(e) {
				//Get a reference to our drag and drop nodes
				var drag = e.drag.get('node'),
				drop = e.drop.get('node');

				//Are we not going up?
				if (!goingUp) { drop = drop.get('nextSibling'); }

				//Add the node to this list
				e.drop.get('node').get('parentNode').insertBefore(drag, drop);

				//Resize this nodes shim, so we can drop on it later.
				e.drop.sizeShim();

				if(drop == null)
					lastDrop = "end";
				else
					lastDrop = drop;
			});

		dddel.dd.on('drag:end', function(e) {
				var drag = e.target;
				drag.get('node').removeClass("ordering");
				if(lastDrop)
					// sends item immediately AFTER this item in the list.  i.e. returns item this was dropped in FRONT of!
				 	// Note, if this was dropped at the end, the string "end" will be sent instead of an item node.
					me.fire("drop", drag.get('node'), lastDrop);
				lastDrop = null;
			});

		this.getDD = function() { return dddel.dd; }
		this.addItem = function(itemNode) { new Y.DD.Drop({ node: itemNode }); }
	}

	Y.augment(DndList, Y.EventTarget); // add events to the DndList Objects

	BLUE.dndList.DndList = DndList;

}, '0.0.1', { requires : ['blue','dd','get']});

