/**
 * Retrive events from Google Calendar.
 * Make sure you include google's library before this one:
 * <script type="text/javascript" src="http://www.google.com/jsapi?key=YOUR_KEY_HERE"></script>
 * The two entry points are registerQuickEventDiv and registerEventDiv
 * JQuery 1.2.6 required see http://jquery.com/
 *
 * @author Devin Weaver <weaver.devin (at) gmail.com>
 * Copyright (c) 2008 Devin Weaver
 */

/**
 * This event object is responsible for makeing a connection with google and
 * retriving the data.
 * @param cal_id the ID for the public Google calendar.
 * @param div_id the ID of the div used to display output.
 */
// class Events {{{
var Events = function (cal_id, div_id) {
    var that = this;
    this.cal_url = 'http://www.google.com/calendar/feeds/' + cal_id + '/public/full';
    this.div_id = div_id;
    /**
     * Handle errors.
     * @param err the Error object.
     */
    // Events.error() {{{
    this.error = function (err) {
        var msg = "<div class=\"error\">";
        msg = msg + (err.cause ? err.cause.statusText : err.message);
        msg = msg + "</div>";
        $('#'+that.div_id).append(msg);
    };
    // }}}
    /**
     * Run the actual query. The function you pass in must accept a parameter
     * for the feed object from gdata.
     * @param q the CalendarEventQuery object to use.
     * @param handler the function to call on success.
     */
    // Events.runQuery() {{{
    this.runQuery = function (query, handler) {
        that.service.getEventsFeed(query, handler, that.error);
    };
    // }}}
    /**
     * Run a simple query. The function you pass in must accept a parameter
     * for the feed object from gdata. This makes for a default query.
     * @param handler the function to call on success.
     */
    // Events.runQuickQuery() {{{
    this.runQuickQuery = function (handler) {
        var query = new google.gdata.calendar.CalendarEventQuery(that.cal_url);
        query.setOrderBy('starttime');
        query.setSortOrder('ascending');
        query.setFutureEvents(true);
        query.setSingleEvents(true);
        query.setMaxResults(Events.QUICK_QUERY_MAX_RESULTS);
        that.runQuery(query, handler);
    };
    // }}}
    google.gdata.client.init(this.error);
    // Does it matter what string is aut here?
    this.service = new google.gdata.calendar.CalendarService('thesocietyct-events-1');
};
// }}}

// Constants
Events.QUICK_QUERY_MAX_RESULTS = 20;
Events.EXCLUDE_PATTERNS = [ /open\s+play/i ];

/**
 * global property for monitoring the state of loading.
 */
Events.load_state = 0;

/**
 * global property to the error <div> id.
 */
Events.error_div = null;

/**
 * initiaize the Events object to handle errors.
 * @param div the div ID to place any error messages.
 */
// Events.errorInit() {{{
Events.errorInit = function (div_id) {
    Events.error_div = div_id;
    Events.load_state = 0;
    Events.errorTimeoutPointer = setTimeout(Events.loadTimeOut, 15000);
};
// }}}

/**
 * Execute an error message if and when the system time out occurs.
 */
// Events.loadTimeOut() {{{
Events.loadTimeOut = function () {
    clearTimeout(Events.errorTimeoutPointer);
    switch (Events.load_state)
    {
        case 0:
            $('#' + Events.error_div).empty();
            $('#' + Events.error_div).append('<div class="error">Unable to contact Google Calendar (Do you have an internt connection?). Please try again later.</div>');
            break;
        case 1:
            $('#' + Events.error_div).empty();
            $('#' + Events.error_div).append('<div class="error">Google Calendar seems to be taking too long to load. Please try again later.</div>');
            break;
        default: // Do nothing.
    }
};
// }}}

/**
 * Return the string representation of the week days.
 * @param day the number of the weekday.
 * @return the string version of the weekday.
 */
// Events.weekDayString() {{{
Events.weekDayString = function (day) {
    var days = new Array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday');
    return days[day - 1];
};
// }}}

/**
 * Pad a number with a leading zero.
 * @param num the number to pad.
 * @return string with a leading zero.
 */
// Events.padNum() {{{
Events.padNum = function (num) {
    if (num === null)
    {
        return '00';
    }
    else if (num < 10)
    {
        return '0' + num;
    }
    else
    {
        return num;
    }
};
// }}}

/**
 * Convert 24 hour time to 12 hour time.
 * @param hour the hour to convert.
 * @return the converted hour.
 */
// Events.twelveHour() {{{
Events.twelveHour = function (hour) {
    if (hour == 0)
    {
        return 12;
    }
    else if (hour > 12)
    {
        return hour - 12;
    }
    else
    {
        return hour;
    }
};
// }}}

/**
 * Find the string AM or PM of the time.
 * @param hour the hour of the time.
 * @return the string AM or PM.
 */
// Events.ampm() {{{
Events.ampm = function (hour) {
    if (hour < 12)
    {
        return 'am';
    }
    else
    {
        return 'pm';
    }
};
// }}}

/**
 * Display the events to a defined div.
 * @param e the Events object to use.
 */
// Events.registerEventDiv() {{{
Events.registerEventDiv = function (e) {
    $('#' + Events.error_div).append('<div class="error">Events.registerEventDiv() not implemented yet.</div>');
};
// }}}

/**
 * Display the events to a defined div in a constrined (quick) view.
 * @param e the Events object to use.
 */
// Events.registerQuickEventDiv() {{{
Events.registerQuickEventDiv = function (e) {
    e.runQuickQuery(function (result) {
        var table = jQuery('<table class="event-list"></table>');
        var idx, i;
        var entry_row = {
            count: 0,
            buffer_length: 0,
            html: null,
            entry: null,
            even_odd: null,
            row_span: null,
            is_first: true
        };
        var last_date = { month: 0, day: 0 };
        var EventEntry = function () {
            return {
                month: 0,
                day: 0,
                hour: null,
                min: null,
                end_hour: null,
                end_min: null,
                weekday: null,
                title: null,
                content: null
            };
        };
        var isEntryExcluded = false;
        var excludePatternIndex = 0;
        var entry = null
        var gdate = null;
        var jsdate = null;
        var entries = result.feed.entry;
        var entry_fifo_buffer = new Array();
        // run loop one extra time past end of array index.
        for (idx = 0; idx <= entries.length; idx++)
        {
            // is idx within array bounds?
            if (idx < entries.length)
            {
                entry = new EventEntry();
                entry.title = entries[idx].getTitle().getText();

                // iterate over exlusion array.
                isEntryExcluded = false;
                for ( excludePatternIndex = 0;
                    excludePatternIndex < Events.EXCLUDE_PATTERNS.length;
                    excludePatternIndex++ )
                {
                    if ( Events.EXCLUDE_PATTERNS[ excludePatternIndex ].test( entry.title ) )
                    {
                        isEntryExcluded = true;
                        break;
                    }
                }

                // should we ignore this event?
                if (isEntryExcluded) continue;

                entry.content = entries[idx].getContent().getText();
                gdate = entries[idx].getTimes()[0].getStartTime();
                jsdate = gdate.getDate();
                entry.month = jsdate.getMonth() + 1; // zero indexed.
                entry.day = jsdate.getDate();
                entry.weekday = jsdate.getDay();
                if (gdate.isDateOnly())
                {
                    // All day event
                    entry.hour = null;
                    entry.min = null;
                    entry.end_hour = null;
                    entry.end_min = null;
                }
                else
                {
                    entry.hour = jsdate.getHours();
                    entry.min = jsdate.getMinutes();
                    // Current bussines rules have no end date for quick view.
                    entry.end_hour = null;
                    entry.end_min = null;
                    /*
                    gdate = entries[idx].getTimes()[0].getEndTime();
                    jsdate = gdate.getDate();
                    entry.end_hour = jsdate.getHours();
                    entry.end_min = jsdate.getMinutes();
                    */
                }
            }
            // Compare last_date. If we are in a new row: reset entry_fifo_buffer and fill
            // in new entry only if not first round through loop.
            if (idx == entries.length
                || (idx > 0 && (entry.month != last_date.month || entry.day != last_date.day)))
            {
                entry_row.count++;
                entry_row.buffer_length = entry_fifo_buffer.length;
                entry_row.html = '';
                entry_row.is_first = true;
                entry_row.even_odd = ((entry_row.count & 1) ? 'odd' : 'even');
                if (entry_row.buffer_length > 1)
                {
                    entry_row.row_span = ' rowspan="' + entry_row.buffer_length + '"';
                }
                else
                {
                    entry_row.row_span = '';
                }
                for (i = 0; i < entry_row.buffer_length; i++)
                {
                    entry_row.entry = entry_fifo_buffer.shift();
                    entry_row.html += '<tr class="event-row event-';
                    entry_row.html += entry_row.even_odd + '">';
                    if (entry_row.is_first)
                    {
                        entry_row.html += '<td class="event-cell event-date event-';
                        entry_row.html += entry_row.even_odd + '"';
                        entry_row.html += entry_row.row_span + '>';
                        entry_row.html += Events.weekDayString(entry_row.entry.weekday) + '<br />';
                        entry_row.html += entry_row.entry.month + '/' + entry_row.entry.day;
                        entry_row.html += '</td>';
                        entry_row.is_first = false;
                    }
                    entry_row.html += '<td class="event-cell event-title event-';
                    entry_row.html += entry_row.even_odd + '">';
                    if (entry_row.entry.content != '')
                    {
                        entry_row.html += '<a class="event-desc-link" title="Click for description">';
                        entry_row.html += entry_row.entry.title;
                        entry_row.html += '</a><div class="event-desc">';
                        entry_row.html += entry_row.entry.content;
                        entry_row.html += '</div>';
                    }
                    else
                    {
                        entry_row.html += entry_row.entry.title;
                    }
                    entry_row.html += '</td><td class="event-cell event-time event-';
                    entry_row.html += entry_row.even_odd + '">';
                    if (entry_row.entry.hour === null)
                    {
                        entry_row.html += 'All&nbsp;Day';
                    }
                    else
                    {
                        entry_row.html += Events.twelveHour(entry_row.entry.hour) + ':';
                        entry_row.html += Events.padNum(entry_row.entry.min);
                        entry_row.html += Events.ampm(entry_row.entry.hour);
                        if (entry_row.entry.end_hour !== null)
                        {
                            entry_row.html += '&nbsp;-&nbsp;';
                            entry_row.html += Events.twelveHour(entry_row.entry.end_hour) + ':';
                            entry_row.html += Events.padNum(entry_row.entry.end_min);
                            entry_row.html += Events.ampm(entry_row.entry.end_hour);
                        }
                    }
                    entry_row.html += '</td>';
                    entry_row.html += '</tr>';
                }
                table.append(entry_row.html);
            }
            if (idx < entries.length)
            {
                last_date.month = entry.month;
                last_date.day = entry.day;
                entry_fifo_buffer.push(entry);
            }
        } // for idx
        $('#'+e.div_id).empty().append(table);
        $('.event-desc').hide().click(function (e) {
            $(e.target).prev('.event-desc-link').toggleClass('event-desc-link-expanded');
            $(e.target).slideToggle('slow');
        });
        $('.event-desc-link').click(function (e) {
            $(e.target).toggleClass('event-desc-link-expanded');
            $(e.target).next('.event-desc').slideToggle('slow');
        });
        Events.load_state = 2;
    });
};
// }}}

/* vim:set et sw=4 fdm=marker: */
