;
(function($) {
    var DAY_MS = 86400000, 
    HOUR_MS = 3600000, 
    MINUTE_MS = 60000;

    var dateFormatters = {
        s	: function(d)	{
            return d.getSeconds()
        },
        ss	: function(d)	{
            return $.cmccal.zeroPad(d.getSeconds())
        },
        m	: function(d)	{
            return d.getMinutes()
        },
        mm	: function(d)	{
            return $.cmccal.zeroPad(d.getMinutes())
        },
        h	: function(d)	{
            return d.getHours() % 12 || 12
        },
        hh	: function(d)	{
            return $.cmccal.zeroPad(d.getHours() % 12 || 12)
        },
        H	: function(d)	{
            return d.getHours()
        },
        HH	: function(d)	{
            return $.cmccal.zeroPad(d.getHours())
        },
        d	: function(d)	{
            return d.getDate()
        },
        dd	: function(d)	{
            return $.cmccal.zeroPad(d.getDate())
        },
        ddd	: function(d,o)	{
            return o.dayNamesShort[d.getDay()]
        },
        dddd: function(d,o)	{
            return o.dayNames[d.getDay()]
        },
        M	: function(d)	{
            return d.getMonth() + 1
        },
        MM	: function(d)	{
            return $.cmccal.zeroPad(d.getMonth() + 1)
        },
        MMM	: function(d,o)	{
            return o.monthNamesShort[d.getMonth()]
        },
        MMMM: function(d,o)	{
            return o.monthNames[d.getMonth()]
        },
        yy	: function(d)	{
            return (d.getFullYear()+'').substring(2)
        },
        yyyy: function(d)	{
            return d.getFullYear()
        },
        t	: function(d)	{
            return d.getHours() < 12 ? 'a' : 'p'
        },
        tt	: function(d)	{
            return d.getHours() < 12 ? 'am' : 'pm'
        },
        T	: function(d)	{
            return d.getHours() < 12 ? 'A' : 'P'
        },
        TT	: function(d)	{
            return d.getHours() < 12 ? 'AM' : 'PM'
        },
        u	: function(d)	{
            return $.cmccal.formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'")
        },
        S	: function(d)	{
            var date = d.getDate();
            if (date > 10 && date < 20) return 'th';
            return ['st', 'nd', 'rd'][date%10-1] || 'th';
        }
    }

    $.cmccal = {
        addYears: function (d, n, keepTime) {
            d.setFullYear(d.getFullYear() + n);
            if (!keepTime) {
                $.cmccal.clearTime(d);
            }
            return d;
        },

        addMonths: function (d, n, keepTime) { // prevents day overflow/underflow
            if (+d) { // prevent infinite looping on invalid dates
                var m = d.getMonth() + n,
                check = $.cmccal.cloneDate(d);
                check.setDate(1);
                check.setMonth(m);
                d.setMonth(m);
                if (!keepTime) {
                    $.cmccal.clearTime(d);
                }
                while (d.getMonth() != check.getMonth()) {
                    d.setDate(d.getDate() + (d < check ? 1 : -1));
                }
            }
            return d;
        },

        addDays: function (d, n, keepTime) { // deals with daylight savings
            if (+d) {
                var dd = d.getDate() + n,
                check = $.cmccal.cloneDate(d);
                check.setHours(9); // set to middle of day
                check.setDate(dd);
                d.setDate(dd);
                if (!keepTime) {
                    $.cmccal.clearTime(d);
                }
                $.cmccal.fixDate(d, check);
            }
            return d;
        },

        fixDate: function (d, check) { // force d to be on check's YMD, for daylight savings purposes
            if (+d) { // prevent infinite looping on invalid dates
                while (d.getDate() != check.getDate()) {
                    d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS);
                }
            }
        },

        addMinutes: function (d, n) {
            d.setMinutes(d.getMinutes() + n);
            return d;
        },

        clearTime: function (d) {
            d.setHours(0);
            d.setMinutes(0);
            d.setSeconds(0); 
            d.setMilliseconds(0);
            return d;
        },

        cloneDate: function (d, dontKeepTime) {
            if (dontKeepTime) {
                return clearTime(new Date(+d));
            }
            return new Date(+d);
        },

        zeroDate: function() { // returns a Date with time 00:00:00 and dateOfMonth=1
            var i=0, d;
            do {
                d = new Date(1970, i++, 1);
            } while (d.getHours() != 0);
            return d;
        },

        zeroPad: function (n) {
            return (n < 10 ? '0' : '') + n;
        },

        /* Date Formatting
    -----------------------------------------------------------------------------*/

        formatDate: function(date, format, options) {
            return $.cmccal.formatDates(date, null, format, options);
        },

        formatDates: function(date1, date2, format, options) {
            options = options || {}; // defaults
            var date = date1,
            otherDate = date2,
            i, len = format.length, c,
            i2, formatter,
            res = '';
            for (i=0; i<len; i++) {
                c = format.charAt(i);
                if (c == "'") {
                    for (i2=i+1; i2<len; i2++) {
                        if (format.charAt(i2) == "'") {
                            if (date) {
                                if (i2 == i+1) {
                                    res += "'";
                                }else{
                                    res += format.substring(i+1, i2);
                                }
                                i = i2;
                            }
                            break;
                        }
                    }
                }
                else if (c == '(') {
                    for (i2=i+1; i2<len; i2++) {
                        if (format.charAt(i2) == ')') {
                            var subres = formatDate(date, format.substring(i+1, i2), options);
                            if (parseInt(subres.replace(/\D/, ''))) {
                                res += subres;
                            }
                            i = i2;
                            break;
                        }
                    }
                }
                else if (c == '[') {
                    for (i2=i+1; i2<len; i2++) {
                        if (format.charAt(i2) == ']') {
                            var subformat = format.substring(i+1, i2);
                            var subres = formatDate(date, subformat, options);
                            if (subres != formatDate(otherDate, subformat, options)) {
                                res += subres;
                            }
                            i = i2;
                            break;
                        }
                    }
                }
                else if (c == '{') {
                    date = date2;
                    otherDate = date1;
                }
                else if (c == '}') {
                    date = date1;
                    otherDate = date2;
                }
                else {
                    for (i2=len; i2>i; i2--) {
                        if (formatter = dateFormatters[format.substring(i, i2)]) {
                            if (date) {
                                res += formatter(date, options);
                            }
                            i = i2 - 1;
                            break;
                        }
                    }
                    if (i2 == i) {
                        if (date) {
                            res += c;
                        }
                    }
                }
            }
            return res;
        },
        
        parseISO8601: function(s, ignoreTimezone) {
            // derived from http://delete.me.uk/2005/03/iso8601.html
            // TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html
            var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$/);
            if (!m) {
                return null;
            }
            var date = new Date(m[1], 0, 1),
            check = new Date(m[1], 0, 1, 9, 0),
            offset = 0;
            if (m[3]) {
                date.setMonth(m[3] - 1);
                check.setMonth(m[3] - 1);
            }
            if (m[5]) {
                date.setDate(m[5]);
                check.setDate(m[5]);
            }
            $.cmccal.fixDate(date, check);
            if (m[7]) {
                date.setHours(m[7]);
            }
            if (m[8]) {
                date.setMinutes(m[8]);
            }
            if (m[10]) {
                date.setSeconds(m[10]);
            }
            if (m[12]) {
                date.setMilliseconds(Number("0." + m[12]) * 1000);
            }
            $.cmccal.fixDate(date, check);
            if (!ignoreTimezone) {
                if (m[14]) {
                    offset = Number(m[16]) * 60 + Number(m[17]);
                    offset *= m[15] == '-' ? 1 : -1;
                }
                offset -= date.getTimezoneOffset();
            }
            return new Date(+date + (offset * 60 * 1000));
        },

        parseTime: function(s) { // returns minutes since start of day
            if (typeof s == 'number') { // an hour
                return s * 60;
            }
            if (typeof s == 'object') { // a Date object
                return s.getHours() * 60 + s.getMinutes();
            }
            var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/);
            if (m) {
                var h = parseInt(m[1]);
                if (m[3]) {
                    h %= 12;
                    if (m[3].toLowerCase().charAt(0) == 'p') {
                        h += 12;
                    }
                }
                return h * 60 + (m[2] ? parseInt(m[2]) : 0);
            }
        
        }
    };
   
    $.cmccal.gcalFeed = function(feedUrl, options) {
		
        //feedUrl = feedUrl.replace(/\/basic$/, '/full');
        options = options || {};
		
        var params = {
            'start-min': $.cmccal.formatDate(options.start, 'u'),
            'start-max': $.cmccal.formatDate(options.end, 'u'),
            'singleevents': true,
            'max-results': options.limit,
			'orderby': 'starttime',
			'sortorder': 'ascending'
        };
        var ctz = options.currentTimezone;
        if (ctz) {
            params.ctz = ctz = ctz.replace(' ', '_');
        }

		var status = 'ok';
		$.ajax({
			url: '../site_resources/php/xmlProxy.php?url=' + feedUrl,
			type: 'get',
			dataType: 'json',
			async: false,
			data: params,
			error: function(jqXHR, textStatus, errorThrown)
			{
				if (jqXHR.status == 0 || jqXHR == 403)
				{
					alert('Failed. Are you sure your calendar is public?');
					status = 'failed';
				}
			}
		});
		
		if (status == 'ok')
		{
        	$.getJSON(feedUrl + "?alt=json-in-script&callback=?", params, function(data) {
	              var events = [];
	            if (data.feed.entry) {
	                $.each(data.feed.entry, function(i, entry) {
	                    var startStr = entry['gd$when'][0]['startTime'],
	                    start = $.cmccal.parseISO8601(startStr, true),
	                    end = $.cmccal.parseISO8601(entry['gd$when'][0]['endTime'], true),
	                    allDay = startStr.indexOf('T') == -1,
	                    url;
	                    $.each(entry.link, function() {
	                        if (this.type == 'text/html') {
	                            url = this.href;
	                            if (ctz) {
	                                url += (url.indexOf('?') == -1 ? '?' : '&') + 'ctz=' + ctz;
	                            }
	                        }
	                    });
	                    if (allDay) {
	                        $.cmccal.addDays(end, -1); // make inclusive
	                    }
	                    events.push({
	                        id: entry['gCal$uid']['value'],
	                        title: entry['title']['$t'],
	                        url: url,
	                        start: start,
	                        end: end,
	                        allDay: allDay,
	                        location: entry['gd$where'][0]['valueString'],
	                        description: entry['content']['$t']
	                    });
	                });
	            }
	            if($.isFunction(options.callback)){
	                options.callback(events);
	            }else{
	                alert("Not a function!");
	            }
	        });
		}
    }
})(jQuery);

