/**
 * @namespace
 */
var namespace = {
    util: {},
    i18n: {},
    ui: {}
};


// --- i18n namespace -------------------------------------


namespace.i18n = {
    next: 'Neste >',
    previous: '< Forrige',
    wizardProgressStatus: 'Question {current} of {total}',
    
    /**
     * 
     * @param {Object} key
     * @param {Object} [replaceMap]
     */
    _: function (key, replaceMap) {
        return namespace.util.substitute(this[key], replaceMap || {});
    }
};

// --- util namespace -------------------------------------


/**
 * Does variable substitution on a string. It scans through the string 
 * looking for expressions enclosed in { } braces. If an expression 
 * is found, it is used a key on the object.
 * 
 * Hacked from http://developer.yahoo.com/yui/docs/Lang.js.html
 * 
 * @method substitute
 * @param {String} s The string that will be modified.
 * @param {Object} o An object containing the replacement values
 * @return {String} the substituted string
 */
namespace.util.substitute = function (s, o) {
    var i, j, k, key, v, meta, saved=[], token, 
        DUMP='dump', SPACE=' ', LBRACE='{', RBRACE='}',
        dump;


    for (;;) {
        i = s.lastIndexOf(LBRACE);
        if (i < 0) {
            break;
        }
        j = s.indexOf(RBRACE, i);
        if (i + 1 >= j) {
            break;
        }

        //Extract key and meta info 
        token = s.substring(i + 1, j);
        key = token;
        meta = null;
        k = key.indexOf(SPACE);
        if (k > -1) {
            meta = key.substring(k + 1);
            key = key.substring(0, k);
        }

        // lookup the value
        v = o[key];

        if (v === undefined) {
            // This {block} has no replace string. Save it for later.
            v = "~-" + saved.length + "-~";
            saved[saved.length] = token;
        }

        s = s.substring(0, i) + v + s.substring(j + 1);
    }

    // restore saved {block}s
    for (i=saved.length-1; i>=0; i=i-1) {
        s = s.replace(new RegExp("~-" + i + "-~"), "{"  + saved[i] + "}", "g");
    }

    return s;
};


/**
 * Groups objects into separate arrays based on the value of a common field name
 *  
 * @param {Array} objectArray An array of objects
 * @param {String} fieldName The name of the field to group objects by
 * @return {Object} An object where each field is an array of objects sharing the same value in the specified field
 */
namespace.util.groupArrayObjectsByField = function (objectArray, fieldName) {
    
    var groupedObjects = {};
    
    for (var i = 0, len = objectArray.length; i < len; ++i) {
        
        if (!groupedObjects[objectArray[i][fieldName]]) {
            groupedObjects[objectArray[i][fieldName]] = [];
        }
        
        groupedObjects[objectArray[i][fieldName]].push(objectArray[i]);
    }
    
    return groupedObjects;
};

/**
 * Creates a function (usually to be used as an event handler) which wraps another function, setting the scope of its
 * "this" variable. Use case is to set an object's member function as the handler for an event, thus maintaining the
 * scope within the object's members.
 *  
 * @param {Object} scope The object used for "this" variable
 * @param {Function} handler The function to execute
 * @return {Function} A callback wrapper function
 */
namespace.util.scopedHandler = function (scope, handler) {
    
    return function () { handler.apply(scope, arguments) };
};

/**
 * Checks whether a group of radio buttons has a selection
 * 
 * @param {Array} radioElements An array of same-name HTML radio elements
 * @return {Bool} True if one of the radio buttons is selected
 */
namespace.util.validateRadioGroup = function (radioElements) {

    for (var i = 0, len = radioElements.length; i < len; ++i) {
        
        if (radioElements[i].checked) {
         
            return true;
        }
    }
    
    return false;
};

/**
 * Creates an HTML <button> element
 * 
 * @param {String} title Button title
 * @param {Function} [clickHandler] Callback handler for button click
 * @return {HTMLElement} The button
 */
namespace.util.createButton = function (title, clickHandler) {
    
    var button = document.createElement('button');
    
    button.appendChild(document.createTextNode(title));
    
    if (clickHandler) {
        $(button).click(clickHandler);
    }
    
    return button;
};



// --- Wizard object ---------------------------------------

(function () {
    
    // Closure for shortcuts
    
    var I18N = namespace.i18n,
        UTIL = namespace.util,
		DISABLED_COLOUR = '#abacae',
		ENABLED_COLOUR = '#5f6062';
        
    /**
     * A wizard object; presents each <fieldset> within a container as steps
     * 
     * @constructor
     * @param {String|HTMLElement} wizardContainer The container of the <fieldsets>
     * @param {String|HTMLElement} submitButton The submit button for the form
     */
    namespace.ui.wizard = function(wizardContainer, submitButton) {
		
		if (!$(wizardContainer).size()) {
			return;
		}
        
        var changeHandler = UTIL.scopedHandler(this, this.updateUIStatus),
            progressBarTextContainer = document.createElement('div');
        
        this._currentStep     = 0;
        this._progressBar     = document.createElement('div');
        this._progressBarText = document.createTextNode('');
        this._wizardSteps     = $(wizardContainer).find('fieldset');
        this._submitButton    = $(submitButton).get()[0];
        this._nextButton      = UTIL.createButton(I18N.next, UTIL.scopedHandler(this, this.goNext));
        this._previousButton  = UTIL.createButton(I18N.previous, UTIL.scopedHandler(this, this.goPrevious));
        
        // Hide all steps
        this._wizardSteps.css('display', 'none');

        // Place the submit button inside the last step
        this._wizardSteps[this._wizardSteps.length - 1].appendChild(this._submitButton);
        
        // Listen to events and update the UI accordingly
        $(wizardContainer).
            find('input, textarea, button').
            focus(changeHandler).
            blur(changeHandler).
            click(changeHandler);
            
        // Initialise progress bar
		/*
        progressBarTextContainer.className = 'progressBarText';
        progressBarTextContainer.appendChild(this._progressBarText);
        
        $(wizardContainer).prepend(this._progressBar);
        $(this._progressBar).progressbar({ value: 0 });
        $(this._progressBar).append(progressBarTextContainer);
        */
        // Start by rendering the first step
        this.renderStep(0);
    };
    
    namespace.ui.wizard.prototype = {
        
        /**
         * Go to the next step in the wizard
         * 
         * @param {Event} [e] UI event that triggered the request
         */
        goNext: function (e) {
    
            !e || e.preventDefault();
            this.renderStep(this._currentStep + 1);
        },
        
        /**
         * Go to the previous step
         * 
         * @param {Event} [e] UI event that triggered the request
         */
        goPrevious: function (e) {
    
            !e || e.preventDefault();
            this.renderStep(this._currentStep - 1);
        },
        
        /**
         * Renders a specific step on the wizard
         * 
         * @param {Int} stepNumber The step number
         */
        renderStep: function (stepNumber) {
            
            var step = this._wizardSteps[stepNumber];
            
            this._wizardSteps[this._currentStep].style.display = 'none';
            this._wizardSteps[stepNumber].style.display = 'block';
            
            this._currentStep = stepNumber;
            
            step.appendChild(this._nextButton);
            step.appendChild(this._previousButton);
            
            // Assume nothing about the previous state...
            
            this._submitButton.style.display   = 'none';
            this._previousButton.style.display = 'inline';
            this._nextButton.style.display     = 'inline';
			this._previousButton.style.backgroundColor = ENABLED_COLOUR;
            this._nextButton.style.backgroundColor     = ENABLED_COLOUR;
			this._submitButton.style.backgroundColor   = DISABLED_COLOUR;
            this._previousButton.disabled      = false;
            this._nextButton.disabled          = false;
            
            if (stepNumber === 0) {
                
                // This is the first step
                
                this._previousButton.disabled = true;
				this._previousButton.style.backgroundColor = DISABLED_COLOUR;
            }
                    
            if (stepNumber == (this._wizardSteps.length - 1)) {
                
                // This is the last step
                
                this._submitButton.style.display = 'inline';
				this._previousButton.style.backgroundColor = ENABLED_COLOUR;
                this._nextButton.style.display = 'none';
            }
            
            this.updateUIStatus();
        },
        
        /**
         * Updates the UI elements to reflect the state of the current step (button states and progress bar) 
         */
        updateUIStatus: function () {
            
            var progress = ((this._currentStep + 1) * 100) / this._wizardSteps.length,
                i18nKeys;
    
            if (this.validateCurrentStep()) {
                
                this._nextButton.disabled = false;
				this._nextButton.style.backgroundColor   = ENABLED_COLOUR;
				this._submitButton.style.backgroundColor = ENABLED_COLOUR;
                this._submitButton.disabled = false;
            }
            else {
                
                this._nextButton.disabled = true;
				this._nextButton.style.backgroundColor = DISABLED_COLOUR;
                this._submitButton.disabled = true;
            }
            /*
            i18nKeys = {
                current: this._currentStep + 1,
                total: this._wizardSteps.length
            };
            
            $(this._progressBar).progressbar('option', 'value', progress);
            this._progressBarText.data = I18N._('wizardProgressStatus', i18nKeys);*/
        },
        
        /**
         * Validates the currently-displayed wizard step (at the moment only radio button groups)
         * 
         * @return {Bool} True if valid, False otherwise
         */
        validateCurrentStep: function () {
    
            var step         = this._wizardSteps[this._currentStep],
                radioButtons = $(step).find("input[type='radio']").get(),
                radioGroups  = UTIL.groupArrayObjectsByField(radioButtons, 'name');
                
            for (var i in radioGroups) {
                
                if (!UTIL.validateRadioGroup(radioGroups[i])) {
                    
                    return false;
                }
            }
            
            return true;
        }
    };
    
}());



// --- Page initialisation --------------------------------

$(document).ready(function () {

	var wizard = new namespace.ui.wizard('#questionnaire', '#questionnaire .submitButton');
	
});