(function ($) {

var MONTHS;
if (LANG == "en") {
    MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
} else {
    MONTHS = ['jan', 'feb', 'maa', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'];
}
var CALENDAR_WIDTH = 700;
var CARDINFO_TAB_WIDTH = 110; // cardinfo.children(".cardinfo_tab").width();
var CARDINFO_DY = 80;

var draggingCard = false;

// Utilities

// immutable date class representing month/year combos
function simpleDate (y, m) {
    var that = {month: m, year: y};
    function add (months) {
        var ret = simpleDate(that.year, that.month);
        ret.year += (months < 0) ? Math.ceil(months/12) : Math.floor(months / 12);
        ret.month += months % 12;
        // log("add "+that.unparse()+" + "+months+" = "+ret.unparse());
        if (ret.month <= 0) {
            ret.month += 12
            ret.year -= 1
        } else if (ret.month > 12) {
            ret.month -= 12;
            ret.year += 1;
        }
        // log("  returning "+ret.unparse());
        return ret;
    }
    that.add = add;
    function monthsUntil (d) {
        return ((d.year - that.year) * 12) + (d.month - that.month);
    }
    that.monthsUntil = monthsUntil;    
    that.unparse  = function () {
        return that.month+"/"+that.year
    }
    return that;
}

/*
// Testing
d1 = simpleDate(2009, 8)
log("start " + d1.unparse());
for (var i=-50; i<50; i++) {
    var d = d1.add(i);
    log(i + " " + d.unparse() + " " + d.monthsUntil(d1) + " months until " + d1.unparse());
}
*/

function compareDates (m1, y1, m2, y2) {
    if (y1 < y2) {
        return -1;
    } else if (y2 > y1) {
        return 1;
    } else {
        if (m1 < m2) {
            return -1;
        } else if (m2 > m1) {
            return 1;
        } else {
            return 0;
        }
    }
}


function getWindowWidth() {
	if (window.innerWidth!=window.undefined) return window.innerWidth;
	if (document.compatMode=='CSS1Compat') return document.documentElement.clientWidth;
	if (document.body) return document.body.clientWidth;
	return window.undefined;
}

function getWindowHeight() {
	if (window.innerHeight!=window.undefined) return window.innerHeight;
	if (document.compatMode=='CSS1Compat') return document.documentElement.clientHeight;
	if (document.body) return document.body.clientHeight;
	return window.undefined;
}

function log() {
	// safe log, avoid javascript breaks if firebug isn't installed
	try {
		console.log.apply(this, arguments);
	} catch (e) {
		var msg = "";
		for (var i=0; i<arguments.length; i++) {
			msg += arguments[i] + " ";
		}
		$("#debug").append("<p>"+msg+"</p>");
	}
}

function randint(from, to) {
	return Math.floor(from + (Math.random() * (to - from + 1)));
}


/**********************/
/* Event Detail Panel */
/**********************/

function eventDetailPanel () {
	var that = {};
	// var elt = $("#event_detail").remove().appendTo("#calendar_table_wrap");
    var dialogWidth, dialogHeight;
    var dialogPosition = "center";
    var dialog_elt;
    
	var elt = $("#event_detail").dialog({
	    autoOpen: false,
	    width: 320,
	    close: function () {
	        setEvent(null);
	        dialogWidth = elt.dialog("option", "width");
	        dialogHeight = elt.dialog("option", "height");
	        if (dialog_elt) {
    	        var left = parseInt($(dialog_elt).css('left'));
    	        var top = parseInt($(dialog_elt).css('top'));
    	        dialogPosition = [left, top];
    	    }
	        // log("dialogPosition", this, dialogPosition);
	        // alert("dialog size was " + dialogWidth +"x" + dialogHeight);
	    }
	});
    that.elt = elt;
    dialog_elt = $(elt).parent().get(0);
    
	var getEvent = function () {
		return $(elt).data("event");
	}
	that.getEvent = getEvent;
	var setEvent = function (event) {
        // clear save message
        $("span.event_button_save", elt).html("");

        var oldEvent = $(elt).data("event");
        if (event === oldEvent) return;
        // log("setEvent", oldEvent, event);
		if (oldEvent) {
            // log("cleaning up oldEvent");
            oldEvent.deactivate();
			// var head = oldEvent.getHead();
			// $(head).removeClass("detail-open");
   			writeToEvent();
            var dialog = elt.parent();
            dialog.removeClass(oldEvent.style_name);
        }
		$(elt).data("event", event);
		if (event) {
            // log("setting up event");
    		// var eventpos = $(head).offset(); // offset == screen position (incorporates scrolling)!
    		// var tablepos = $("#calendar_table_wrap").offset();
    		// var pos = {left: (eventpos.left-tablepos.left), top: (eventpos.top-tablepos.top)};
    		// var wh = getWindowHeight();
    		// Show the right Event title (select)
    		$("select.event_titles", elt).hide();
    		$("select#event_titles_"+event.card_id, elt).show();
    		// Set form values based on event data
    		readFromEvent();
    		elt.dialog("option", "title", event.card_name);
            var dialog_elt = elt.parent();
            dialog_elt.addClass(event.style_name);
    		// elt.dialog('open');
            event.activate();
    		// $(head).parent().css("z-index", 2);
		}
	}
	that.setEvent = setEvent;

	var readFromEvent = function () {
			var event = $(elt).data("event");
			// log("readFromEvent", event.id);
			if (!event) return;	
			$("#event_startMonth", elt).val(event.getStartMonth());
			$("#event_startYear", elt).val(event.getStartYear());
			$("#event_endMonth", elt).val(event.getEndMonth());
			$("#event_endYear", elt).val(event.getEndYear());
			// visible selector seems to not be valid immediately after showing...
			// so just brute forces the value to ALL of the selects
			// $(".event_titles:visible", elt).val(event.getTitle());
            var title = event.getTitle();
            // log("setTitle", title, title === undefined, title === null, title === "");
            // weird, IE seems to fail when setting a select to "", & " " bizarely works...
            if (title === "") title = "_";
			$(".event_titles", elt).val(title);
			$("#event_text", elt).val(event.getText());
	}

	var writeToEvent = function () {
			// text
			var event = $(elt).data("event");
			// log("writeToEvent", event.id);
			if (!event) return;	
			event.setStart($("#event_startYear", elt).val(), $("#event_startMonth", elt).val());
			event.setEnd($("#event_endYear", elt).val(), $("#event_endMonth", elt).val());
			var title = $(".event_titles:visible", elt).val();
			if (title === "_") title = "";
			event.setTitle(title);
			event.setText($("#event_text", elt).val());
	}

	var close = function () {
        $(elt).dialog("close");
	}
    that.close = close;
    
	var show = function () {
	    if (isOpen()) return;
        $(elt).dialog("option", "height", "auto");
        // log("show", dialogPosition);
        $(elt).dialog("option", "position", dialogPosition);
	    $(elt).dialog("open");
	}
	that.show = show;

	var isOpen = function () {
        return elt.dialog('isOpen');
	}
	that.isOpen = isOpen;

	that.doEventSave = function () {
        $("span.event_button_save", elt).html(LANG == "en" ? "Saving..." : "Bezig...");
		var event = $(elt).data("event");
		if (event) {
			writeToEvent();
            // VALIDATE / SANITIZE DATES
            // event end can't be before the event begin
            if (event.start.monthsUntil(event.end) < 0) {
                // log("end before start, fixing");
                event.setEnd(event.start.year, event.start.month);
                readFromEvent();
            }
            // event can't begin earlier than the calendar
            if (calendarStart.monthsUntil(event.start) < 0) {
                event.setStart(calendarStart.year, calendarStart.month);
                readFromEvent();
            }
			$.post("event_update_js/", {
				event_id: event.id,
				startMonth: event.getStartMonth(),
				startYear: event.getStartYear(),
				endMonth: event.getEndMonth(),
				endYear: event.getEndYear(),
				title: event.getTitle(),
				text: event.getText()
			}, processEvents, "json");
		}
	}
	that.doEventDelete = function () {
		// var foo = message("Ben je zeker?");
		// log(foo);
		if (confirm((LANG == "en") ? "Remove this card from your plan?" : "Verwijder dit kaartje van je planning?")) {
			var event = $(elt).data("event");
			if (event) {
				$.post("event_delete_js/", { 'event_id': event.id }, function (data) {
					if (data.ok) {
						event.deleteSelf();
					}
				}, "json");
			}
			close();
		}
	}

	return that;
}

var detail = null;

/**************/
// Cell
/**************/

var cells = [];
var cellsByDate = {};

function cell (spec) {
	var that = {};
	var elt = spec.elt; // is the table cell (td)
	var month = spec.month;
	var year = spec.year;

	var addCard = function (e) {
		$(e).appendTo(elt).data("cell", that);
		layoutCards();
	}
	that.addCard = addCard;

	var removeCard = function (e) {
		$(e).remove();
		layoutCards();
	}
	that.removeCard = removeCard;

	var layoutCards = function () {
		var elts = $(".card", elt);
		var dh = 30;
		var y = 20;
		elts.each(function () {
			$(this).css({top: y+"px", left: 6+"px"});
			y += dh;
		});
		$(elt).css("height", (y+20) + "px");
	}

	that.month = month;
	that.year = year;
	that.elt = elt;

	cells.push(that);
	cellsByDate[month+"_"+year] = that;
	// log("created cell", that);
	return that;
}

function getCellByDate(m, y) {
	return cellsByDate[m+"_"+y];
}

///////////////////
/* CalendarEvent */
///////////////////

function extractFileName (url) {
    var pos = url.lastIndexOf("/");
    if (pos >= 0) url = url.substr(pos+1);
    return url;
}

function stripExtension (url) {
    var pos = url.indexOf(".");
    if (pos >= 0) url = url.substr(0, pos);
    return url;
}

function calendarEvent (spec) {
	var that = {}
	var id = spec.id;

	var card_id = spec.card;
	var title = spec.title;
	var text = spec.text;
	var cardImage = spec.cardImage;
	var head = null;
	var tails = [];
	var dragging = false;

    var start = simpleDate(spec.startYear, spec.startMonth);
    var end = (spec.endYear && spec.endMonth) ? simpleDate(spec.endYear, spec.endMonth) : start;
    that.start = start;
    that.end = end;
    
	that.card_name = $("#cardinfo"+card_id+"_hand h2").html();
	var style_name = stripExtension(extractFileName(cardImage));
	style_name = "dialog-" + style_name.toLowerCase();
    // that.style_name = "dialog-" + that.card_name.replace(/\W/, "").toLowerCase();
    that.style_name = style_name;
    // console.log("calendarEvent", card_id, style_name);


	function click () {
		detail.setEvent(that);
		detail.show();
	}
    function highlight (evt) {
        $(head).addClass("highlight");
        $(tails).addClass("highlight");
        if (evt && !draggingCard) {
            var offset = $(this).offset();
            $("#card_rollover").css({"left": (offset.left+10) + "px", "top" : (offset.top+30) + "px"}).show();
        }
    }
    function dehighlight (evt) {
        $(head).removeClass("highlight");
        $(tails).removeClass("highlight");
        if (evt) {
            $("#card_rollover").hide();
        }
    }    
    function activate () {
        $(head).addClass("active");
        $(tails).addClass("active");
    }
    that.activate = activate;
    function deactivate () {
        $(head).removeClass("active");
        $(tails).removeClass("active");
    }    
    that.deactivate = deactivate;

	var initScreen = function () {
		var template = "<div><a href=\"#\" onclick=\"return false;\"><img src=\"" + cardImage + "\" /></a></div>";
		var m = start.month, y = start.year;

        var dragOpts = {
			revert: 'invalid',
			distance: 3,
			start: function (evt, ui) {
				// log("start", this, ui);
				// $(ui).css("z-index", 3);
				draggingCard = true;
				dragging = true;
				dehighlight();
				detail.close();
			},
			stop: function (evt, ui) {
    			draggingCard = false;
				dragging = false;
			}
		};
		head = $(template).css("position", "absolute").draggable(dragOpts).addClass("card event eventhead event"+id)
		head.attr("id", "event"+id);
		head.data("event", that);
		head.hover(highlight, dehighlight).click(click);
		head = head.get(0);
		// getCellByDate(m, y).addCard(head);
		var cell = getCellByDate(m, y);
		if (!cell) {
		    log("warning: pre-calendar event, adjusting to fit", id, start);
		    start.month = startMonth;
		    start.year = startYear;
		    cell = getCellByDate(start.month, start.year);
		}
        cell.addCard(head);

        var curDate = start;
        
    	// Create / add event "tails" (follow on cards)
		if (end !== null) {
			while (curDate.monthsUntil(end) > 0) {
                curDate = curDate.add(1);
				var cell = getCellByDate(curDate.month, curDate.year);
				if (!cell) { break; } // event goes off calendar
				var tail = 	$(template).css("position", "absolute");
                if (curDate.monthsUntil(end) == 0) tail.draggable(dragOpts);
				tail.addClass("card event eventtail event"+id);
				tail.data("event", that);
                tail.hover(highlight, dehighlight).click(click);
                tail = tail.get(0);
				tails.push(tail);
				cell.addCard(tail);
			}
		}
		// log("initScreen, " + head + ", " + tails.length + ", " + tails);
	}

	var init = function () {
		initScreen();
	}
	var update = function (fields) {
		// log("event.update", fields);
		// if (detail.getEvent() === that) detail.setEvent(null);
		// log("updating event");

		start = simpleDate(fields.startYear, fields.startMonth);
		that.start = start;
		end = (fields.endYear && fields.endMonth) ? simpleDate(fields.endYear, fields.endMonth) : start;
		that.end = end;
		
		title = fields.title;
		text = fields.text;
		removeCards();
		initScreen();
	}

    function removeCards () {
		$(head).data("cell").removeCard(head);
		head = null;
		for (var i=0; i<tails.length; i++) {
		    var tail = tails[i];
		    $(tail).data("cell").removeCard(tail);
		}
		tails = [];
    }

	var deleteSelf = function () {
		if (detail.getEvent() === that) detail.setEvent(null);
		removeCards();
		title = null; text = null;
		delete calendarEventsById[id];
	}
	that.deleteSelf = deleteSelf;

	that.getHead = function () { return head; };

	that.getStartMonth = function () { return start.month };
	that.getStartYear = function () { return start.year };
	that.setStart = function (y, m) {
	    start = simpleDate(y, m);
	    that.start = start;
	};

	that.getEndMonth = function () { return end.month };
	that.getEndYear = function () { return end.year };
	that.setEnd = function (y, m) {
	    end = simpleDate(y, m);
	    that.end = end;
	};

	that.getTitle = function () { return title };
	that.setTitle = function (t) { title = t };
	that.getText = function () { return text };
	that.setText = function (t) { text = t };
	that.update = update;
	that.id = id;
	that.card_id = card_id;
	that.init = init;
	return that;
}

var calendarEventsById = {};

function processEvents (data) {
	// log("process Events", data);
    if ((data.ok !== undefined) && (data.ok === 0) && data.message) {
        // ERROR MESSAGE : DISPLAY
        $("#dialog").html(data.message);
        $("#dialog").dialog("open");
    }
    // hackybut...
    if (detail.isOpen()) {
        $("span.event_button_save", detail.elt).html(LANG == "en" ? "Saved" : "Opgeslagen");
    }
	if (data.length >= 0) {
		for (var ei=0; ei<data.length; ei++) {
			var d = data[ei];
			// log("event", d.id);
			var obj = calendarEventsById[d.id];
			if (obj) {
				obj.update(d);
			} else {
				obj = calendarEvent(d);
				calendarEventsById[d.id] = obj;
				obj.init();
			}
		}
	}
}

function doCardDrop (card, month, year) {
	qcard = $(card);
	var event = qcard.data("event");
	if (event) {
		// log("moving event", event);
		// preserve duration
		var curDuration = event.start.monthsUntil(event.end);
        var newStart, newEnd;
        if (!qcard.hasClass("eventtail")) {
            // drag from head
            newStart = simpleDate(year, month);
            newEnd = newStart.add(curDuration);
        } else {
            // drag from tail
            newEnd = simpleDate(year, month);
            // newStart = newEnd.add(-curDuration);
            newStart = event.start; // preserve start time
            // sanitize so end isn't earlier than start
            if (newStart.monthsUntil(newEnd) < 0)
                newEnd = newStart;
        }
        // WRITE BACK TO EVENT otherwise there's some weirdness possible with the detail panel bringing back the wrong state
        event.setStart(newStart.year, newStart.month);
        event.setEnd(newEnd.year, newEnd.month);
		$.post("event_update_js/", {
		    event_id: event.id,
		    startMonth: newStart.month,
		    startYear: newStart.year,
		    endMonth: newEnd.month,
		    endYear: newEnd.year
		}, processEvents, "json");
	} else {
		// log("new");
		var card_id = parseInt(card.id.substr(4));
		$.post("event_create_js/", { card_id: card_id, startMonth: month, startYear: year, endMonth: month, endYear: year }, processEvents, "json");
	}
}

function hideCardInfo(card_id) {
	if (card_id) {
		$("#cardinfo"+card_id+"_hand").hide();
	} else {
		$(".cardinfo").hide();
	}
}

function showCardInfo (card_id) {
	// detail.clearEvent();
	var cardinfo = $("#cardinfo"+card_id+"_hand");
	// position each cardinfo relative to its corresponding card
	var h = cardinfo.height();
	var w = cardinfo.width();
	var card = $('#card'+card_id);
	var cardwidth = card.width();
	var cardpos = card.position();
	var cardleft = parseInt(card.css("left")); // left is a % value
	if (cardleft < 50) {
		var x = cardpos.left + (cardwidth/2) - (CARDINFO_TAB_WIDTH/2);
		cardinfo.children(".cardinfo_tab").css("float", "left");
		// cardinfo.css({left: x, top: -h + CARDINFO_DY +'px'});
		cardinfo.css("left", x);
	} else {
		cardinfo.children(".cardinfo_tab").css("float", "right");
		var x = cardpos.left + (cardwidth/2) + (CARDINFO_TAB_WIDTH/2) - w;
		// cardinfo.css({left: x, top: -h + CARDINFO_DY + 'px'});
		cardinfo.css("left", x);
	}
	cardinfo.show();
}

/* INIT */
var startMonth, startYear, endMonth, endYear;
var calendarStart, calendarEnd;

$(function () {
	var count = 0;

    $("#dialog").dialog({
        autoOpen: false,
	    width: 320
    });

	// event detail panel (detail)
	detail = eventDetailPanel();
	$("#calendar_table").mousemove(function () {
		if (draggingCard) return;
		// if (detail.isOpen()) { detail.hideSoon(); }
	});

	$("#event_detail_save").click(function () { detail.doEventSave(); });
	$("#event_detail_delete").click(function () { detail.doEventDelete(); });

	// init cells
	// var startMonth, startYear, endMonth, endYear;
	$("div.calendar_cell").each(function (index, thing) {
			var parts = thing.id.split("_");
			var month = parseInt(parts[1]);
			var year = parseInt(parts[2]);
			var c = cell({month: month, year: year, elt: thing});
			if (!startMonth) {
			    startMonth = month;
			    startYear = year
			}
			endMonth = month; endYear = year;
	});
    calendarStart = simpleDate(startYear, startMonth);
    calendarEnd = simpleDate(endYear, endMonth);

	// stuff dates into detail date selects
	for (var mi=0; mi<MONTHS.length; mi++) {
		$("<option></option>").html(MONTHS[mi]).attr("value", (mi+1)).appendTo("#event_startMonth");
		$("<option></option>").html(MONTHS[mi]).attr("value", (mi+1)).appendTo("#event_endMonth");
	}
	for (var y=startYear; y<=endYear; y++) {
		$("<option></option>").html(y).appendTo("#event_startYear");
		$("<option></option>").html(y).appendTo("#event_endYear");
	}
	
	// Copy card texts into hand
	$('.cardinfo').each(function (index, thing) {
		// log(index, thing);
		var card_id = parseInt(thing.id.substr(8));
		var cardinfo = $(thing).remove().appendTo("#handinner").attr("id", "cardinfo"+card_id+"_hand").css("z-index", 1);
	});


	/* Category Cards */
	$('.card').draggable({
		//  delay: 250, // really not good in ie6
//		helper: 'clone',
		helper: function () {
			return $(this).clone().appendTo("#hand");
		},
		revert: 'invalid',
		start: function (evt, ui) {
			$("#gids_card").hide();
			// ie6: force hide card info
			hideCardInfo();
			draggingCard = true;
		},
		stop : function () {
			draggingCard = false;
		}
	}).click(function () {
		var offset = $(this).offset();
		var ph = $("#gids_card").height();
		if (offset.left < 500) {
			$("#gids_card").css({left: offset.left, top: (offset.top - ph - 40)}).show();
		} else {
			var pw = $("#gids_card").width();
			var l = offset.left + CARD_WIDTH - pw - 30;
			$("#gids_card").css({left: l, top: (offset.top - ph - 40)}).show();
		}
	}).hover(function () {
		if (draggingCard) return;
		var card_id = parseInt(this.id.substr(4));
		$(this).css("z-index", 2);
		var pos = $(this).position();
		showCardInfo(card_id);
	}, function () {
		$(this).css("z-index", "");
		var card_id = parseInt(this.id.substr(4));
		hideCardInfo(card_id);
	});

	$('td.calendar_cell').droppable({
		// accept: '.card',
		accept: function (elt) {
			// log(this, elt);
			if ($(elt).hasClass("card")) {
				var td = (elt.get(0).parentNode.parentNode);
				// log(td, this);
				return td !== this;
			} else {
				return false;
			}
		},
		hoverClass: 'drophover',
		drop: function (evt, ui) {
			var card = ui.draggable[0];
			var div = $("div.calendar_cell", this).get(0);
			var parts = div.id.split("_");
			var month = parseInt(parts[1]);
			var year = parseInt(parts[2]);
			doCardDrop(card, month, year);
		}
	});

	// Load events
	// log("loading events...");
	// $.get("events_js/", {}, processEvents, "json");
    $.ajaxSetup ({  cache: false });  
   	$.ajax({
		url: "events_js/",
		dataType: "json",
		success: processEvents,
		error: function (req, msg) {
			log("xhr error", msg, req);
		}
	});			

});


})(jQuery);


