(function($) {
    var val_ = $.fn.val;

    /* Runs an ajax post query with the given url, post data and callback. If
     * a throbber is specified, it will be faded in when the request starts,
     * and faded out when the request completes.
     */
    $.postJSON = function (url, data, callback, throbber, suppress_global_ajax) {
        var global = !suppress_global_ajax;
        return $.ajax({
            'type': 'POST',
            'url': url,
            'data': JSON.stringify(data),
            'beforeSend': function () {
                if (throbber) {
                    throbber.fadeIn('fast');
                }
            },
            'success': function (data, status, request) {
                if (!data || !data.status) {
                    return display_error_message('Error connecting to server');
                } else {
                    return callback(data, status, request);
                }
            },
            'complete': function () {
                if (throbber) {
                    throbber.fadeOut();
                }
            },
            'dataType': 'json',
            'global': global,
            'contentType': 'text/json'
        });
    }

    // This function doesn't convert the POST data to json
    $.postJSON2 = function (url, data, callback, throbber, suppress_global_ajax) {
        var global = !suppress_global_ajax;
        return $.ajax({
            'type': 'POST',
            'url': url,
            'data': data,
            'beforeSend': function () {
                if (throbber) {
                    throbber.fadeIn('fast');
                }
            },
            'success': function (data, status, request) {
                if (!data || !data.status) {
                    return display_error_message('Error connecting to server');
                } else {
                    return callback(data, status, request);
                }
            },
            'complete': function () {
                if (throbber) {
                    throbber.fadeOut();
                }
            },
            'dataType': 'json',
            'global': global
        });
    }

    $.fn.val = function (value) {
        if (value === undefined) {
            if ($(this).hasClass('default-value')) {
                return '';
            } else {
                return $.proxy(val_, this)();
            }
        } else {
            return $.proxy(val_, this)(value);
        }
    }

    /* Sets the default value of the given input element.
     *
     * Initially, the element will display the default value. When focused, the
     * default value will be removed. If the element loses focus, and its value
     * is still empty, the default value will be displayed again. The element
     * will have the class "default-value" whenever the default value is
     * displayed.
     *
     * If the input field is a password field, the input field will be changed
     * to a text field while the default value is displayed.
     */
    $.fn.defaultValue = function (default_value) {
        this.each(function () {
            var self = $(this);
            var defaultDisplay = self;

            // if the input element is a password field, we need to replace it
            // with a text input element to display the default value
            if (self.attr('type') == 'password') {
                defaultDisplay = $('<input type="text"/>');
                defaultDisplay.attr('tabindex', self.attr('tabindex'));
                defaultDisplay.attr('class',    self.attr('class'));
                defaultDisplay.attr('id',       self.attr('id'));
                defaultDisplay.attr('style',    self.attr('style'));
            }

            function setDefaultDisplay() {
                defaultDisplay.val(default_value);
                defaultDisplay.addClass('default-value');
                defaultDisplay.focus(focus);
                if (self !== defaultDisplay) {
                    self.replaceWith(defaultDisplay);
                }
            }

            function blur() {
                if ($.trim(self.val()) == '') {
                    setDefaultDisplay();
                }
            }

            function focus() {
                if (defaultDisplay.hasClass('default-value')) {
                    self.val('');
                    defaultDisplay.removeClass('default-value');
                    self.blur(blur);
                    if (self !== defaultDisplay) {
                        defaultDisplay.replaceWith(self);
                        self.focus();
                    }
                }
            }

            if ($.trim(self.val()) === '' ||

                    // This is ugly, but if we don't have this here, returning
                    // to the page via "back" button will cause the default
                    // value to be auto-form-filled by the browser as the value
                    // of the input element.
                    $.trim(self.val()) === default_value) {
                defaultDisplay.focus(focus);
                setDefaultDisplay();
            } else {
                self.blur(blur);
            }
        });
    };

    function autocomplete_school(request, callback) {

        // split the terms from the text input into its constituent words
        var terms = request.term.split(' ');
        terms = $.map(terms, function(term) {
            term = $.trim(term);
            if (term === '') {
                return null;
            } else {
                return term.toLowerCase();
            }
        });

        // find the schools that match the search terms
        var schools = $.grep(global_data.schools, function (school) {
            var non_matching_terms = $.grep(terms, function(term) {
                if (school.name.toLowerCase().indexOf(term) >= 0) {
                    return false;
                } else if (school.slug.toLowerCase().indexOf(term) >= 0) {
                    return false;
                } else {
                    return true;
                }
            });

            // include the school only if there are no non_matching_terms
            return !non_matching_terms.length;
        });

        schools = $.map(schools, function (school) {
            return {
                value: school.name,
                label: $('<div/>').text(school.name)
                        .append($('<br/>'))
                        .append($('<i/>').text(
                                school.slug + '.' + base_host())),
                slug: school.slug
            }
        });

        if (schools.length > 3) {
            schools = schools.slice(0, 3);

            // append a '...' element at the end
            schools.push({
                value: '',
                label: '<div class="dddots">...</div>',
                slug: 'www'
            });
        }

        callback(schools);
    }

    function submit_demo(_, ui) {
        if (ui) {
            if (ui.item.value == '') {
                // if the '...' button was clicked...
                display_content('school-list');
            } else {
                $('#demo-school').val(ui.item.value);
                window.location = window.location.protocol + '//'
                                + ui.item.slug + '.' + base_host() + '/';
            }
        } else if ($.trim($('#demo-school').val()) !== '') {
            var matches = autocomplete_school({term: $('#demo-school').val()},
                function (results) {
                    if (results.length) {
                        $('#demo-school').val(results[0].value);
                        window.location = window.location.protocol + '//'
                                        + results[0].slug + '.' + base_host()
                                        + '/';
                    } else {
                        display_content('school-list');
                        $('#cant-find-school span').text($('#demo-school').val());
                        $('#cant-find-school').show();
                    }
                });
        }
        // don't do anything if nothing was entered into the demo-school input

        return false;
    }

    function base_host() {
        split = window.location.host.split('.');
        return split.slice(1).join('.');
    }

    /* Populates the login page's table of schools */
    function populate_list_of_schools() {

        /* adds a link for the given school to the given table row */
        function add_school_link(tr, school) {
            var td = $('<td/>').appendTo(tr);
            if (school) {
                var href = window.location.protocol + '//'
			 + school.slug + '.' + base_host() + '/';
                $('<a/>').appendTo(td)
                         .attr('href', href)
                         .addClass('school-name')
                         .text(school.name);
                $('<a/>').appendTo(td)
                         .attr('href', href)
                         .addClass('school-url')
                         .text(school.slug + '.' + base_host());
                td.addClass('non-empty');
                td.click(function () {window.location = href});
            }
        }

        var table = $('#list-of-schools table');
        var schools = global_data.schools;
        var middle_index = Math.floor((schools.length+1)/2);
        for (var i = 0; i < middle_index; i++) {
            var tr = $('<tr/>').appendTo(table);
            add_school_link(tr, schools[i]);
            add_school_link(tr, schools[i + middle_index]);
        }
    }

    /* Displays a box at the top of the webpage allowing the user to resend
     * their confirmation email.
     */
    function show_resend_confirmation(email) {
        $('.notification').slideUp();
        var div = $('#resend-confirmation-email');
        div.slideDown().find('span')
                  .text('Your account has not been confirmed, yet.');
        div.find('input').show();
        div.find('.throbber').hide();
        div.find('form').unbind('submit');
        div.find('form').submit(function (event) {
            event.preventDefault();
            $.postJSON('/ajax-resend-confirmation/',
                       {email: email}, function (response) {
                var status = response.status;
                if (status === 'confirmed') {
                    div.find('input').hide();
                    div.slideDown().find('span').html('')
                    .append('Your account ')
                    .append($('<b/>').text(email))
                    .append(' has already been confirmed. No additional ' +
                            'confirmation email has been sent.');
                } else if (status === 'ok') {
                    div.find('input').hide();
                    div.slideDown().find('span').html('')
                    .append('Your confirmation email has been sent to ')
                    .append($('<b/>').text(email))
                    .append('.<br/><br/>Please follow the link in this email ' +
                            "to confirm your account.<br/>If you don't receive " +
                            "this email within the next 5 minutes, ")
                    .append($('<a href="#contact">')
                            .text('please let us know')
                            .click(function (event) {
                                event.preventDefault();
                                div.slideUp();
                                display_content('contact');
                            }))
                    .append('.');
                } else {
                    div.slideUp();
                    display_error_message('Error connecting to server');
                }

            // suppress global ajax events
            }, div.find('.throbber'));
        });
    }

    function display_content(which_content) {
        // hide the error boxes on page changes...
        $('#error-header, .notification').slideUp();

        which_content = $.trim(which_content)
        if (which_content.charAt(0) === '#') {
            which_content = which_content.substring(1);
        }

        // display the error bar stating that the user's account is not yet
        // confirmed
        var unconfirmed_match = which_content.match(/[?]unconfirmed=(.*)$/);
        if (unconfirmed_match) {
            var email = decodeURIComponent(unconfirmed_match[1]);
            show_resend_confirmation(email);
            $('#email').val(email);
            $('#email').removeClass('default-value');
        }

        // display the error bar stating that the user's account is currently
        // updating...
        var updating_match = which_content.match(/[?]updating=(.*)$/);
        if (updating_match) {
            var email = decodeURIComponent(updating_match[1]);
            display_error_message('Your account is currently being updated. ' +
                                  'Please try again in a few seconds');
            $('#email').val(email);
            $('#email').removeClass('default-value');
        }

        // display an error dialog stating that the user specified an invalid
        // password...
        var invalid_password_match = which_content.match(/[?](invalid-password|invalid-email)=(.*)$/);
        if (invalid_password_match) {
            var email = decodeURIComponent(invalid_password_match[2]);
            $('#invalid-password').slideDown();
            $('#email').val(email);
            $('#email').removeClass('default-value');
        }

        // remove all temporary data behind the '?' or the url hash
        which_content = which_content.replace(/[?].*$/, '');

        if (which_content === '') {
            which_content = 'welcome';
        }
        window.location.hash = '#' + which_content;

        $('#header-right, #content tr:first').show();
        $('#list-of-schools, #go-back-link, #forgot-form, '
        + '#contact-form, #request-form, #tos-div, #privacy-div, #welcome-text, '
        + '#browse-all-link, #cant-find-school, #post-signup').hide();
        switch (which_content) {
            case 'signup-completed':
                $('#header-right').fadeOut(800);
                $('#content tr:first').hide();
                $('#post-signup').show();
            case 'school-list':
                $('#list-of-schools, #go-back-link').show();
                break;
            case 'contact':
                $('#contact-form, #browse-all-link').show();

                // reset the contact form, so that another message can be sent
                // when the "Contact us" link is clicked again
                $('#contact-form #inner-contact-form').show();
                $('#contact-form img.throbber').hide();
                $('#contact-form #inner-contact-form-sent').hide();
                break;
            case 'request':
                $('#request-form, #browse-all-link').show();

                // reset the request form, so that another message can be sent
                // when the "request school" link is clicked again
                $('#request-form #inner-contact-form').show();
                $('#request-form img.throbber').hide();
                $('#request-form #inner-contact-form-sent').hide();
                break;
            case 'forgot':
                $('#forgot-form, #browse-all-link').show();

                // reset the password form, so that another password can be sent
                // when the "forgot password" link is clicked again
                $('#forgot-form .request').show();
                $('#forgot-form img.throbber').hide();
                $('#forgot-form .request-sent').hide();
                break;
            case 'tos':
                var throbber = $('#tos-div .throbber');
                throbber.show();
                $('#tos-inner').load('/static/tos.html', function () {
                    throbber.hide();
                });
                $('#tos-div, #browse-all-link').show();
                break;
            case 'privacy':
                var throbber = $('#privacy-div .throbber');
                throbber.show();
                $('#privacy-inner').load('/static/privacy.html', function () {
                    throbber.hide();
                });
                $('#privacy-div, #browse-all-link').show();
                break;
            default: // show the welcome screen by default
                $('#welcome-text, #browse-all-link').show();
                break;
        }
    }
    window.display_content = display_content; // For in-browser debugging...

    /* sorts schools to appear alphabetically */
    function sort_school_data() {
        global_data.schools.sort(function (a,b) {
            if (a.name.toLowerCase() < b.name.toLowerCase()) {
                return -1;
            } else if (a.name.toLowerCase() > b.name.toLowerCase()) {
                return 1;
            } else {
                return 0;
            }
        });
    }

    /* Sends a contact form message or a school request */
    function submit_message(parent_div) {
        var email = parent_div.find('#contact-email').val();
        var subject = parent_div.find('#contact-subject').val();
        var message = parent_div.find('#contact-message').val();

        $.postJSON2('/login-ajax.php?send_form_message',
            {email: email, subject: subject, message: message},
            function (response) {
                var status = response.status;
                if (status === 'ok') {
                    if (!$.trim(email)) {
                        display_error_dialog('Missing field',
                            'Please specify your email address.');
                    } else if (!$.trim(message)) {
                        display_error_dialog('Missing field',
                            'Please write your message in the text box.');
                    } else {
                        parent_div.find('#contact-email-sent').text(email);
                        parent_div.find('#contact-message-sent').text(message);
                        parent_div.find('#inner-contact-form').hide();
                        parent_div.find('#inner-contact-form-sent').show();
                    }
                } else {
                    display_error_message('Error connecting to server');
                }
            }, parent_div.find('img.throbber'));
    }

    /* checks if the given email is valid. If the email is valid, null is
     * returned. Otherwise, a string containing the problem with the email
     * is returned.
     */
    function validate_email(email) {
        var email = $.trim(email);
        if (email === '') {
            return 'Please enter your email address.';
        } else if (!email.match(
                /^[+a-z0-9._\-]+[@]([a-z0-9\-]+[.])+([a-z]{2,4})$/i)) {
            return 'Please enter a valid email address.';
        } else if ($.grep(global_data.schools, function (school) {
                   return $.grep(school.email_domains, function (domain) {
                       return email.match(domain + '$') == domain;
                   }).length;
               }).length == 0) {
            return 'You specified an email address that is not from one of '
                 + 'our supported schools.<br/><br/>If you do not have '
                 + 'an email address from your school, please contact us at '
                 + 'contact@schedulizer.com.';
        } else {
            return null;
        }
    }

    /* displays a notification box at the top of the html page. If a
     * ``retry_handler`` is specified, there will be a "retry" link that 
     * executes the handler, when clicked.
     */
    function display_error_message(message, retry_handler) {
        if (retry_handler) {
            $('#error-retry-link').show().click(retry_handler);
        } else {
            $('#error-retry-link').hide();
        }
        $('#error-header').slideDown()
                          .find('#error-message').text(message);
    }

    /* Displays a modal error dialog with the given title and message.
     * The optional ``handler`` is called when the dialog is closed.
     */
    function display_error_dialog(title, message, handler) {
        $('#error-dialog p').html(message);
        $('#error-dialog').dialog('option', 'title', title)
                          .dialog('open');
        function close_dialog () {
            $(document).unbind('click', close_dialog);
            $(document).unbind('keypress', close_dialog);
            $('#error-dialog').dialog('close');
            if (handler) {
                handler();
            }
            return false;
        }
        $(document).keypress(close_dialog);
        $(document).click(close_dialog);
    }

    function submit_forgot_password(parent_div) {
        var email = $.trim(parent_div.find('#forgot-email').val());

        // don't do anything when no email was entered
        if (email === '') {
            return false;
        }

        var validation_error = validate_email(email);
        if (validation_error) {
            display_error_dialog('Invalid email address', validation_error,
                function () { $('#forgot-email').focus(); });
        } else {
            $.postJSON2('/login-ajax.php?recover_password', 
                {email: email},
                function (response) {
                    var status = response.status;
                    if (status == 'hashed-password' || status == 'ok') {
                        parent_div.find('#forgot-email-sent').text(
                                parent_div.find('#forgot-email').val());
                        parent_div.find('.request').hide();
                        parent_div.find('.request-sent').show();
                    } else if (status == 'no-password') {
                        display_error_dialog('Password recovery',
                            'A representatives will contact you ' +
                            'shortly, to assist you in recovering your password. ' +
                            '<br/><br/>' +
                            'Please email us at contact@schedulizer.com if ' +
                            'you have any questions.');
                    } else if (status == 'not-registered') {
                        display_error_dialog('Not registered',
                            'The email address you specified has not been ' +
                            'registered. <br/>Please sign up, first!',
                            function () { $('#forgot-email').focus() });
                    } else if (status == 'unconfirmed') {
                        show_resend_confirmation(email);
                    } else {
                        display_error_message('Error connecting to server');
                    }
                }, parent_div.find('img.throbber'));
        }
    }

    function sign_up() {
        var email = $('#signup-email').val();
        var password = $('#signup-password-1').val();
        var password2 = $('#signup-password-2').val();

        if (!email) {
            display_error_dialog('Missing field',
                    "Please enter your email address.",
                    function () { $('#signup-email').focus(); });
        } else if (!password) {
            display_error_dialog('Missing field',
                    "Please enter a password for your account.",
                    function () { $('#signup-password-1').focus(); });
        } else if (password.length < 3) {
            display_error_dialog('Password too short',
                    "Please enter a password that is at least 3 characters long.",
                    function () { $('#signup-password-1').focus(); });
        } else if (password !== password2) {
            display_error_dialog('Password mismatch',
                    "The passwords you entered don't match.",
                    function () { $('#signup-password-2').focus(); });
        } else {
            var validation_error = validate_email(email);
            if (validation_error) {
                display_error_dialog('Invalid email address',
                        validation_error, function () {
                            $('#signup-email').focus();
                        });
            } else {
                // everything looks fine... let's ask the server to sign up
                $.postJSON('/ajax-register/',
                    {email: email, password: password},
                    function (response) {
                        // on success...
                        $('#sign-up-form .throbber').fadeOut();
                        var status = response.status;
                        if (status === 'ok') {
                            $('#post-signup span').text(' ' + email);
                            display_content('signup-completed');
                        } else if (status === 'unconfirmed') {
                            show_resend_confirmation(email);
                        } else if (status === 'invalid-password') {
                            // The user tried registering an existing account.
                            // The password they entered didn't match their
                            // previous password.

                            $('.notification').slideUp();
                            $('#already-registered').slideDown();
                        } else if (status === 'logged-in') {
                            // the user specified the same password as when
                            // they registered, previously... let's log them
                            // in!
                            window.location =
                                window.location.protocol + '//' +
                                response.school + '.' + base_host();
                        } else if (status === 'updating') {
                            display_error_message(
                                  'Your account is currently being updated. ' +
                                  'Please try again in a few seconds');
                        } else {
                            display_error_message('Error connecting to server');
                        }
                    }, $('#sign-up-form .throbber')
                );
            }
        }
    }

    $(function () {
        $('.modal-dialog').dialog({
            width: 500,
            modal: true,
            buttons: { Ok: function () { $(this).dialog('close'); } },
            draggable: false,
            resizable: false,
            autoOpen: false
        });
        sort_school_data();
        setTimeout(function () {
            if (!$.proxy(val_, $('#email'))()) {
                $('#email').defaultValue('Email');
            }
            if (!$.proxy(val_, $('#password'))()) {
                $('#password').defaultValue('Password');
            }
        }, 200);

        $('#forgot-email').defaultValue('Your email address');
        $('#contact-form #contact-email').defaultValue('Your email address');
        $('#request-form #contact-email').defaultValue('Your email address');
        $('#contact-form #contact-message').defaultValue(
            'Type your message here.');
        $('#request-form #contact-message').defaultValue(
            "Enter your school's name here.");
        $('#demo-school').defaultValue("Enter your school's name");
        $('#demo-school').autocomplete({
            open: function (event, ui) {
                // highlight the first dropdown value when a user starts typing
                var first = $($('.ui-autocomplete li a').get(0));
                first.mouseenter();
            },
            delay: 0,
            source: autocomplete_school,
            select: submit_demo
        });
        $('#demo-form').submit(function (event) {
            event.preventDefault();
            submit_demo();
        });
        populate_list_of_schools();
        $('#browse-all-link').click(function(event) {
            event.preventDefault();
            display_content('school-list')
        });
        $('.go-back-link').click(function(event) {
            event.preventDefault();
            display_content('welcome');
        });
        $('#header h1').click(function(event) {
            event.preventDefault();
            display_content('welcome')
        });
        $('.request-link').click(function(event) {
            event.preventDefault();
            display_content('request')
        });
        $('.contact-link').click(function(event) {
            event.preventDefault();
            display_content('contact')
        });
        $('#tos-link').click(function(event) {
            event.preventDefault();
            display_content('tos')
        });
        $('#privacy-link').click(function(event) {
            event.preventDefault();
            display_content('privacy')
        });
        $('.forgot-link').click(function(event) {
            event.preventDefault();
            display_content('forgot');
        });

        $('form#request-form').submit(function(event) {
            event.preventDefault();
            submit_message($('#request-form'))
        });
        $('form#contact-form').submit(function(event) {
            event.preventDefault();
            submit_message($('#contact-form'))
        });
        $('form#forgot-form-email').submit(function(event) {
            event.preventDefault();
            return submit_forgot_password($('#forgot-form'))
        });
        $('form#sign-up-form').submit(function(event) {
            event.preventDefault();
            return sign_up();
        });
        $('form#facebook-connect-form').submit(function(event) {
            event.preventDefault();
//            $.getScript("http://connect.facebook.net/en_US/all.js",
//            function () {
                FB.init({
                    appId: $fb_appapikey,
                    status: true,
                    cookie: true,
                    xfbml: false,
                    oauth: true
                });
                FB.login(function(response) {
                  if (response.authResponse) {
                    // user successfully logged in
                    window.location = '/fbconnect/'
                  } else {
                    // user cancelled login
                  }
                });
//            });
        });
        $('#login-fields').submit(function(event) {
            // set the 'hash' form input, and allow the form to be submitted
            $('#login-fields input[name=hash]').val(
                window.location.hash.replace(/^[#]/, ''));
            if (!$('#login-fields input[name=password]').val() ||
                !$('#login-fields input[name=email]').val()) {
                return false;
            } else {
                return true;
            }
        });

        // Display an error message for failed ajax requests
        $('#error-header #close-error').click(
                function () {$('#error-header').slideUp()});
        $('#error-header').ajaxError(ajaxError);
        function ajaxError(_, __, options, error) {
            $('.notification').slideUp();
            display_error_message('Error connecting to server');
        };
        $('#error-header').ajaxStart(function (_, request) {
            $('#error-header').slideUp();
        });

        $.ajaxSetup({timeout: 20000})

        display_content(window.location.hash);
    });
})(jQuery);


