define(
    function () {
        "use strict";
        var online = (VLE && VLE.serverversion) || false,
            local = (typeof Storage !== 'undefined'),
            activityId = '',
            itemId = '',
            courseId = '',
            saveMsg = document.getElementById('save-msg') || null;

        var retrieve = function (cfg) {
            cfg.user = (cfg.user !== false); //true unless == false
            if (typeof cfg.callback != 'function') {
                cfg.callback = function () {
                };
            }
            if (online) {
                retrieveDataFromVLE(cfg);
            }
            else {
                if (local) {
                    retrieveDataFromLocalStorage(cfg);
                }
            }
        };

        var retrieveDataFromVLE = function (cfg) {
            var ok = function (data) {
                var outObj = {};
                for (var k in data) {
                    if (data.hasOwnProperty(k)) {
                        outObj[k] = decodeURIComponent(data[k]);
                    }
                }
                cfg.callback(outObj);
                console.log('Retrieved ' + ((cfg.user) ? 'user' : 'group') + ' data: ', outObj);
            };
            var error = function (msg) {
                if (msg !== null) {
                    console.log(msg);
                }
                cfg.callback({});
                console.log('get_server_data has failed.');
            };
            var names = [];
            var p;
            for (p in cfg.names) {
                if (cfg.names.hasOwnProperty(p)) {
                    names.push(p);
                }
            }
            console.log('Data to be retrieved: ' + names);
            VLE.get_server_data(cfg.user, names, ok, error, activityId, itemId, courseId);
        };

        var retrieveDataFromLocalStorage = function (cfg) {
            var outObj = {};
            var name, item, p;
            for (p in cfg.names) {
                if (cfg.names.hasOwnProperty(p)) {
                    name = p;
                    item = localStorage.getItem(activityId + name);
                    if (item === null) {
                        outObj[name] = '';
                    } else {
                        outObj[name] = decodeURIComponent(item);
                    }
                }
            }
            cfg.callback(outObj);
            console.log('Retrieved ' + (cfg.user ? 'user' : 'group') + ' data: ', outObj);
        };

        /*
         * @param {object} cfg
         * @param {boolean} cfg.user User, global, or group identifier
         * @param {object} cfg.values Data to be saved
         * @param {function} cfg.callback Callback function called on success/fail (returns true/false)
         * @param {object} cfg.previous Previously values
         * @param {function} cfg.retry Function that is called if previous values changed
         * @param {string} cfg.id
         */
        var save = function (cfg) {
            var saveObj = {};
            cfg.user = (cfg.user !== false); //true unless == false
            if (typeof cfg.callback != 'function') {
                cfg.callback = function () {
                };
            }
            if (saveMsg !== null) {
                fadeSaveMsg();
            }
            console.log(cfg.values);
            for (var p in cfg.values) {
                if (cfg.values.hasOwnProperty(p)) {
                    saveObj[p] = encodeURIComponent(cfg.values[p]);
                }
            }
            console.log((cfg.user ? 'User' : 'Group') + ' data to be saved: ', cfg.values);
            if (online) {
                saveDataToVLE(saveObj, cfg);
            }
            else { /* don't use local storage unless offline */
                if (local) {
                    saveToLocalStorage(saveObj, cfg);
                }
                else {
                    cfg.callback(false);
                    saveMsg.textContent = 'cannot save offline';
                }
            }
        };

        var fadeSaveMsg = function () {
            var opacity = 0;
            var fadeIn, fadeOut;
            fadeIn = (function fadeIn() {
                saveMsg.style.opacity = (opacity += (1 / 30));
                if (opacity < 1) {
                    requestAnimationFrame(fadeIn.bind(this));
                }
                else {
                    setInterval(fadeOut, 500);
                }
            }.bind(this))();
            fadeOut = function fadeOut() {
                saveMsg.style.opacity = (opacity -= (1 / 30));
                if (opacity > 0) {
                    requestAnimationFrame(fadeOut.bind(this));
                }
            }.bind(this);
        };

        var saveDataToVLE = function (saveObj, cfg) {
            var user = (cfg.user !== false);
            var ok = function () {
                cfg.callback(true);
                console.log('set_server_data (' + (user ? 'user' : 'group') + ') was successful');
            };
            var error = function (msg) {
                if (msg !== null) {
                    console.log(msg);
                }
                cfg.callback(false);
                console.log('set_server_data has failed');
            };
            var previousValues = cfg.previous;
            var retry = cfg.retry || null;
            console.log(saveObj);
            VLE.set_server_data(user, saveObj, ok, error, previousValues, retry, activityId, itemId, courseId);
        };

        var saveToLocalStorage = function (saveObj, cfg) {
            //TODO alert message on first save - Any data saved in this context will only be available later on this device. Continue?
            for (var p in saveObj) {
                if (saveObj.hasOwnProperty(p)) {
                    localStorage.setItem(activityId + p, saveObj[p]);
                }
            }
            cfg.callback(true);
        };

        var makeIdFromOrigin = function () {
            var theId = '';
            var theOrigin, i;
            /*
             takes the alpha-numeric characters from the origin and pathname and uses
             them to make an 'activity id' which should be safe for use with the VLE
             */
            if (!window.location.origin) { /* i.e. IE */
                window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
            }
            theOrigin = window.location.origin + window.location.pathname;
            for (i = 0; i < theOrigin.length; i++) {
                if (theOrigin.charCodeAt(i) > 96 && theOrigin.charCodeAt(i) < 123) {
                    theId += theOrigin.charAt(i);
                }
            }
            for (i = 0; i < theOrigin.length; i++) {
                if (theOrigin.charCodeAt(i) > 64 && theOrigin.charCodeAt(i) < 91) {
                    theId += theOrigin.charAt(i);
                }
            }
            for (i = 0; i < theOrigin.length; i++) {
                if (theOrigin.charCodeAt(i) > 47 && theOrigin.charCodeAt(i) < 58) {
                    theId += theOrigin.charAt(i);
                }
            }
            // since this id is only to be used for local storage it can be more than 20 characters
            return theId;
        };

        if (online) {
            activityId = VLE.get_param('activityId') || VLE.get_param('_a');
            itemId = VLE.get_param('documentId') || VLE.get_param('_i');
            courseId = VLE.get_param('courseId') || VLE.get_param('_c');
        }
        else {
            activityId = makeIdFromOrigin();
        }

        return {
            retrieve: retrieve,
            save: save
        };
    });