var audio_muted = false;
/**
 * Create namespaces
 *
 * Note that "public" is accessed by lookup instead of as a property 
 * since it is a reserved keyword in JavaScript. While the ECMAScript 5 
 * specification allows for unquoted use of reserved keywords, their 
 * use is not recommended for backwards compatibility.
 */
if (typeof ShootQ === 'undefined') {
    ShootQ = {};
}
if (typeof ShootQ['public'] === 'undefined') {
    ShootQ['public'] = {};
}
if (typeof ShootQ['public'].sites === 'undefined') {
    ShootQ['public'].sites = {};
}

/**
 * Formats JSON errors into their appropriate HTML representation. This 
 * assumes the errors are stored in result.errors. If a string, returns the 
 * string as an error. If a list, returns an unordered list of messages. If 
 * a dictionary, it assumes the errors are field errors. For those, it'll 
 * collect all input fields that are of type "hidden" or that are visible 
 * and output errors for only those. See getFields and formatFormErrors for 
 * more information on how form errors are handled.
 */
ShootQ['public'].formatErrors = function(result, form) {

    // Get the messages from the errors
    var messages = null;
    if (result && result.errors) {
        if (ShootQ.$.type(result.errors) == 'string') {
            messages = result.errors;
        } else if (ShootQ.$.isArray(result.errors)) {
            messages = ShootQ.$.map(result.errors, function() {
                return '<li>' + this + '</li>';
            });
        } else if (ShootQ.$.isPlainObject(result.errors)) {
            if (!form) {
                form = ShootQ.$(document);
            }
            form.find(':input.nb-error').removeClass('nb-error');
            messages = ShootQ['public'].formatFormErrors(result.errors, form, 
                        ShootQ['public'].getFields(form), '');
        }
    }
    
    // If we have no messages, set a default
    if (!messages || messages.length == 0) {
        messages = 'There was an error submitting your information! Please try again later.';
    }
    
    // Wrap the messages appropriately
    if (ShootQ.$.isArray(messages)) {
        return '<div class="nb-error-explanation">' + 
            '<p>Please correct the following ' + (messages.length == 1 ? 'error' : 'errors') + ':</p>' + 
            '<ul>' + messages.join('\n') + '</ul></div>';
    } else {
        return '<p class="nb-error">' + messages + '</p>';
    }
};

/**
 * Collects all fields within the given form. Only inputs of type of 
 * "hidden" or that are visible and that have a "name" attribute are 
 * collected. Each field is returned as a 3-element array containing 
 * (1) the field key, (2) the optional field label as defined in 
 * "data-label", and (3) the subfields for the field. Fields are 
 * grouped using standard variable-decode convention.
 */
ShootQ['public'].getFields = function(form) {
    var result = [],
        knownKeys = {};
    ShootQ.$.map(form.find(':input[type="hidden"], :input:visible:enabled').filter('[name]'), function(el) {
        el = ShootQ.$(el);
        var keys = el.attr('name').split('.'),
            cursor = result,
            lookup = knownKeys;
        ShootQ.$.each(keys, function(i, key) {
            if (!lookup[key]) {
                var label = null;
                if (i == keys.length - 1) {
                    label = el.data('label');
                } else {
                    label = form.find('#nb-' + key).data('label');
                }
                cursor.push([key, label, []]);
                lookup[key] = [cursor.length - 1, {}];
            }
            cursor = cursor[lookup[key][0]][2];
            lookup = lookup[key][1];
        });
    });
    return result;
};

/**
 * Collects form errors for the given set of errors, the form, and the 
 * set of fields. Prefix should not given; it is used for recursive 
 * calls to this method. Only errors for fields in the fields list are 
 * collected. If a field has no label, its label is determined by taking 
 * the key, converting any trailing list identifier, replacing underscores 
 * with spaces, and title casing it. For any field that has an error, an 
 * "nb-error" class is added to the field.
 */
ShootQ['public'].formatFormErrors = function(errors, form, fields, prefix) {
    
    // Default the prefix if not provided
    if (prefix == undefined) {
        prefix = '';
    }
    
    // Collect the form errors for each field
    var formErrors = [];
    ShootQ.$.each(fields, function(i, field) {
        var content = '',
            key = field[0],
            label = field[1];
        if (!label) {
            label = field[0].replace(/_/g, ' ').replace(/\-([0-9]+)$/, function(match, p1) {
                        return ' #' + (parseInt(p1) + 1);
                    });
            label = ShootQ.shared.toTitleCase(label);
        }
        if (prefix) {
            key = prefix + '.' + key;
        }
        if (field[2].length > 0) {
            content = ShootQ['public'].formatFormErrors(errors, form, field[2], key)
            if (content.length > 0) {
                content = '<ul>' + content.join('\n') + '</ul>';
            }
        } else if (errors[key]) {
            form.find(':input[name="' + key + '"]').addClass('nb-error');
            content = errors[key];
            if (ShootQ.$.isArray(content)) {
                content = ShootQ.$.map(content, function(error) {
                    return '<li>' + error + '</li>';
                });
                content = '<ul>' + content + '</ul>';
            }
        }
        if (content != '') {
            formErrors.push('<li>' + label + ': ' + content + '</li>');
        }
    });
    
    // Return the errors
    return formErrors;
};

/**
 * Comment form
 */
ShootQ['public'].sites.CommentForm = function(selector) {
    
    // Make sure we have a match
    this.form = ShootQ.$(selector);
    if (!this.form.length) return;
    
    // Hook up the form
    this.form.submit(ShootQ.$.proxy(this, 'submit'));
};
ShootQ['public'].sites.CommentForm.prototype = {
    
    displayError: function(result) {
        this.form.find('.nb-errors').html(ShootQ['public'].formatErrors(result, this.form)).show();
    },
    
    insertComment: function(comment) {
        var commentList = ShootQ.$('#nb-comment-list');
        commentList.find('article.nb-last').removeClass('nb-last');
        $(comment).addClass('nb-last')
            .hide()
            .appendTo(commentList)
            .slideDown();
    },
    
    reset: function() {
        this.toggleButtons(true);
        this.form.find('textarea').val('');
    },

    submit: function(event) {
        
        // Cancel the default action
        event.preventDefault();
        
        // Disable buttons and hide errors
        this.toggleButtons(false);
        this.form.find('.nb-errors').html('');
        
        // Grab the form data
        var params = this.form.serializeArray();
        
        // Submit the form data
        ShootQ.$.ajax({
            url: this.form.attr('action'),
            type: 'POST',
            data: params,
            dataType: 'json',
            context: this,
            success: function(data) {
                if (data.success) {
                    this.insertComment(data.content);
                    this.reset();
                } else {
                    this.displayError(data);
                    this.toggleButtons(true);
                }
            },
            error: function(request) {
                var data = {};
                if ((request.getResponseHeader('Content-Type') || '').indexOf('application/json') >= 0) {
                    data = ShootQ.$.parseJSON(request.responseText);
                }
                this.displayError(data);
                this.toggleButtons(true);
            }
        });
    },
    
    toggleButtons: function(enabled) {
        if (enabled) {
            this.form.find('input[type="submit"]').removeAttr('disabled');
        } else {
            this.form.find('input[type="submit"]').attr('disabled', 'disabled');
        }
    }
};

/**
 * DOM ready actions
 */

function setup_form_ajax() {
    // Firefox has this nasty habit of autocompleting form fields, including
    // the state of buttons. This is annoying, and we almost never want it,
    // so let's turn it off.
    $('input[type!="hidden"], button, textarea').each(function() {
        if (!$(this).hasClass('autocomplete')) {
            $(this).attr('autocomplete', 'off');
        }
    });

    // Hook up the comment forms
    $('.nb-comment-form form').each(function(i, el) {
        new ShootQ['public'].sites.CommentForm(el);
    });

}

ShootQ.$(document).ready(function($) {
    setup_form_ajax();
});

$(document).ready( function() {
    $('.nb-gallery-viewer.nb-gallery-automatic').each(function(index) {
        var el = $(this),
            url = window.location.href,i
            hashLoc = url.indexOf('image='),
            imageNum = 0,
            imageCrop = $(this).data('image-crop') ? $(this).data('image-crop') : false;
        
        if (hashLoc > 0) {
            imageNum = parseInt(url.substr(hashLoc+6)) - 1;

            if (isNaN(imageNum)) {
                imageNum = 0;
            }
        }
            
        $.ajax({
            url:el.attr('data-gallery') + 'index.json',
            cache:false,
            dataType:'json',
            type:'GET',
            success: function(data, textStatus, xhr) {
                $("#gallery-audio-info").html(data.audio);

                var g = el.galleria({
                    data_source: data.images,
                    carousel: false,
                    debug: true,
                    image_crop: imageCrop,
                    image_pan: false,
                    idle_time: 1000,
                    show: imageNum
                });

                $('#images').galleria(); // initialize the galleria
                var gallery = Galleria.get(0); // gallery is now the first galleria instance
                function set_comments(image_index) {
                    var image_guid =  gallery._data[image_index].guid;
                    var url = image_guid + "/ .nb-comments";
                    $("#image_comments").load(
                        url,
                        function(responseText, textStatus, XMLHttpRequest) {
                            setup_form_ajax();
                        }
                    );
                }

                //initial setup of image comments
                set_comments(0);
                $('.slideshow').click(function() { 
                    if (gallery_player != '') {
                        if (gallery_player.media.paused) {
                            $('.slideshow').css('background-image', "url('/images/icons/audio.png')");
                            gallery_player.play();
                        }
                        else {
                            $('.slideshow').css('background-image', "url('/images/icons/mute.png')");
                            gallery_player.pause();
                            audio_muted = true;
                        }
                    }
                });

                gallery.bind("loadstart", function(e) {
                    $("#image_comments").fadeOut();
                });

                gallery.bind("loadfinish", function(e) {
                    $("#image_comments").fadeIn();
                    scrollButtons($('.galleria-thumbnails-container'));
                });

                gallery.bind("image", function(e) {
                    set_comments(e.index);
                });

                gallery.bind("fullscreen_exit", function(e) {
                    if($('body').css('overflow') != 'auto') {
                        $('body').css('overflow', 'auto');
                    }
                });
            }
        });
    });
    $('.nb-gallery-viewer.nb-gallery-automatic').show();

    function scrollButtons($elem) {
        var paneHeight = $elem.height() - 75,
            $thumbs = $elem.find(".galleria-thumbnails"),
            thumbsHeight = $thumbs.height(),
            $up = $('<div class="thumb-nav-up">up</div>').css({
                    overflow: 'hidden',
                    textIndent: '-2000px'
                });
            $down = $('<div class="thumb-nav-down">down</div>').css({
                    overflow: 'hidden',
                    textIndent: '-2000px'
                });
        
        $elem.css({overflow: 'hidden'});
        $elem.append($up);
        $elem.append($down);
        $thumbs.css('top', 0);

        $down.click(function() {
            var top = Number($thumbs.css('top').replace('px', ''));

            if (top >= (thumbsHeight - paneHeight) * -1) {
                $thumbs.animate({top: top - paneHeight});
            }
        });

        $up.click(function() {
            var top = Number($thumbs.css('top').replace('px', ''));

            if (top < 0) {
                top += paneHeight;
                if (top > 0) {
                    top = 0;
                }
                $thumbs.animate({top: top});
            }
        });
    }

});

