//
// controller.js
//
// Written by Matthew K. Coughlin
//

//=========================================================
//
// Persistent constructor
//
// Data types supported:
//   "boolean"
//      false, true -> 0, 1
//   "string"
//   "integer"
//   "index"
//       0..4 -> 1..5
//       A special integer type, for saving a 0-based index with
//       the equivalent 1-based value; otherwise, use "integer"
//       for index values.
//
// Methods:
//   defineType(name, type)
//   getValue(name)
//   setValue(name, value)
//
//   setCookiesPath(path)
//   setCookiesExpDays(days)
//
//   read()
//   updateCookies()
//   generateLiveLink()
//
function Persistent() {

    // Private properties

    var _this = this;   // Alias of "this", for use by private methods

    var types = new Array();
    var values = new Array();
    var urlParams = new Array();

    var cookiesPath = '/';
    var cookiesExpDays = 1;


    // Private methods

    function setCookie(name, value) {
        $.cookie(name, value, { path: cookiesPath, expires: cookiesExpDays });
    }


    // Privileged methods

    this.defineType = function(name, type)  { types[name] = type; }
    this.getValue   = function(name)        { return values[name]; }
    this.setValue   = function(name, value) { values[name] = value; }


    this.read = function() {
        urlParams = $fp.getUrlParams();

        for (var name in types) {
            switch (types[name]) {
                case 'boolean':
                    if (urlParams[name]) {
                        values[name] = urlParams[name] == 1;
                    } else if ($.cookie(name)) {
                        values[name] = $.cookie(name) == 1;
                    } else {
                        values[name] = -1;
                    }
                    break;

                case 'index':
                    if (urlParams[name]) {
                        values[name] = urlParams[name] - 1;
                    } else if ($.cookie(name)) {
                        values[name] = $.cookie(name) - 1;
                    } else {
                        values[name] = -1;
                    }
                    break;

                default:
                    if (urlParams[name]) {
                        values[name] = urlParams[name];
                    } else {
                        values[name] = $.cookie(name);
                    }
                    break;
            }
        }
        //var s = ''; for(var name in values) {s+=name+', urlParam = '+urlParams[name]+', cookie = '+$.cookie(name)+', type = '+types[name]+', value = '+values[name]+'\n';} alert(s);
    }


    this.setCookiesPath    = function(path) { cookiesPath    = path; }
    this.setCookiesExpDays = function(days) { cookiesExpDays = days; }

    this.updateCookies = function() {
        for (var name in types) {
            switch (types[name]) {
                case 'boolean': setCookie(name, values[name] ? 1 : 0); break;
                case 'index':   setCookie(name, values[name] + 1); break;
                default:        setCookie(name, values[name]); break;
            }
        }
    }


    this.generateLiveLink = function() {
        var params = '';
        for (var name in types) {
            if (params) { params += '&'; }

            switch (types[name]) {
                case 'boolean':
                    params += name + '=' + (values[name] ? 1 : 0);
                    break;
                case 'index':
                    params += name + '=' + (values[name] + 1);
                    break;
                default:
                    params += name + '=' +  values[name];
                    break;
            }
        }
        var url = $fp.getBaseUrl() + '?' + params;
        return url;
    }
}


//=========================================================
//
// Controller constructor
//
// Properties:
//   buttonHasFocus
//
// Methods:
//   init(view, model)
//
//   keyEvent(e)
//   scramble()
//   skip()
//   playAgain()
//
//   tilesChanged()
//
//   diffAction()
//   themeAction()
//   autoStartAction()
//   optionsShownAction()
//
function Controller() {

    // Private properties

    var _this = this;   // Alias of "this", for use by private methods

    var gameSuspended = false;
    var buttonHasFocus = false;
    var persistent = undefined;


    // Private methods

    function paramWordExists() {

        // Get the persistent settings (via URL parameters, cookies, ...)
        persistent.read();
        var scrambled = persistent.getValue(View.OPT_WORD);
        var diff      = persistent.getValue(View.OPT_DIFFICULTY);
        var theme     = persistent.getValue(View.OPT_THEME);
        var auto      = persistent.getValue(View.OPT_AUTO_START);
        var opt       = persistent.getValue(View.OPT_OPTIONS_SHOWN);

        // If the difficulty is valid
        if ((diff >= Model.DIFF_EASY) && (diff <= Model.DIFF_RANDOM)) {
            model.setDifficulty(diff);
        }
        view.updateDiff();

        // Automatically start the next word
        if (auto != -1) {
            model.setUiPref(View.OPT_AUTO_START, auto);
        }
        view.updateAutoStart();

        // If the theme is valid
        if ((theme >= View.THEME_MIN) && (theme <= View.THEME_MAX)) {
            model.setUiPref(View.OPT_THEME, theme);
        }
        view.updateTheme();

        // Options shown
        if (opt != -1) {
            model.setUiPref(View.OPT_OPTIONS_SHOWN, opt);
        }
        view.updateOptionsShown();

        // Word
        return model.useScrambledWord(scrambled);
    }


    function startGame() {
        var letters = model.getScrambled();
        view.setTiles( letters );
        view.updateWordDimensions(letters);
        view.setStatus('');
        updatePersistentSettings();
    }


    function showNumSolutions() {
        view.showExtraInfo("Number of solutions: " + model.getNumSolutions());
    }
    

    function showSolutions() {
        var numSolutions = model.getNumSolutions();
        var solutions = model.getSolutions();
        var wordLength = model.getLetters().length;
        var info = "";
        for (var i = 0; i < numSolutions; i++) {
            if (i > 0) {
                info += ", ";
            }
            info += solutions.substring(i * wordLength,
                                        (i * wordLength) + wordLength);
        }
        view.showExtraInfo("Solutions: " + info);
    }
    

    function solved() {
        if (model.getUiPref(View.OPT_AUTO_START)) {
            autoStartIntro();
        }
        else {
            playAgainPrompt();
        }
    }


    function autoStartIntro() {
        view.showSolved();
        gameSuspended = true;

        setTimeout( autoStartConclusion, 1200);
    }


    function autoStartConclusion() {
        view.hideSolved();
        gameSuspended = false;

        model.nextWord();
        startGame();
    }


    function playAgainPrompt() {
        view.updateButtons(false);
        view.showSolved();
        gameSuspended = true;

        // To do: Abstract the "playAgainBtn" id
        $("#playAgainBtn").focus();
    }


    function textChanged() {
        view.hideHint();
        view.setStatus('');

        var guess = view.getLettersTyped();

        if (model.isSolution(guess)) {
            solved();
        }
        else if (guess.length == model.getLetters().length) {
            view.showIncorrect();
        }
    }
    

    function updatePersistentSettings() {

        // Set the values that should be saved
        persistent.setValue(
            View.OPT_WORD,
            model.getScrambled());

        persistent.setValue(
            View.OPT_DIFFICULTY,
            model.getDifficulty());

        persistent.setValue(
            View.OPT_THEME,
            model.getUiPref(View.OPT_THEME));

        persistent.setValue(
            View.OPT_AUTO_START,
            model.getUiPref(View.OPT_AUTO_START));

        persistent.setValue(
            View.OPT_OPTIONS_SHOWN,
            model.getUiPref(View.OPT_OPTIONS_SHOWN));

        // Update the cookies
        persistent.updateCookies();

        // Update the live link
        var url = persistent.generateLiveLink();
        view.updateLiveLink(url);
    }


    // Privileged methods

    this.init = function(paramView, paramModel) {
        view = paramView;
        model = paramModel;

        // UI Preference defaults
        model.setUiPref(View.OPT_THEME, 1);
        model.setUiPref(View.OPT_AUTO_START, false);
        model.setUiPref(View.OPT_OPTIONS_SHOWN, false);

        // Persistent settings (via live link, cookies, ...)
        persistent = new Persistent();
        persistent.setCookiesPath('/apps/mixup/');
        persistent.setCookiesExpDays(100);
        persistent.defineType(View.OPT_WORD,          'string');
        persistent.defineType(View.OPT_DIFFICULTY,    'index');
        persistent.defineType(View.OPT_THEME,         'index');
        persistent.defineType(View.OPT_AUTO_START,    'boolean');
        persistent.defineType(View.OPT_OPTIONS_SHOWN, 'boolean');

        // Add event handlers (they're in the closure; "this" refers
        // to the related DOM element)
        document.onkeydown  = function(event) {
            var result = _this.keyEvent(event);
            buttonHasFocus = false;
            return result;
        }
        document.onkeypress = function(event) {
            var result = _this.keyEvent(event);
            buttonHasFocus = false;
            return result;
        }

        $("#scrambleBtn").click(function(){ _this.scramble(); });
        $("#scrambleBtn").keydown(function(){ buttonHasFocus = true; });
        $("#scrambleBtn").keypress(function(){ buttonHasFocus = true; });

        $("#skipBtn").click(function(){ _this.skip(); });
        $("#skipBtn").keydown(function(){ buttonHasFocus = true; });
        $("#skipBtn").keypress(function(){ buttonHasFocus = true; });

        $("#playAgainBtn").click(function(){ _this.playAgain(); });

        $("#optionsTitle").click(function(){
            _this.optionsShownAction();
            return false;
        });
        $('#options input[id^="diff"]').click(function(){
            _this.diffAction();
        });
        $('#options input[id^="theme"]').click(function(){
            _this.themeAction();
        });
        $('#autoCheckbox').click(function(){ _this.autoStartAction(); });

        // Reset the difficulty radio buttons
        view.updateDiff();

        // Reset the theme radio buttons
        view.updateTheme();

        // Get the current word
        if (!paramWordExists()) {
            model.nextWord();
        }
        // Now that the settings have been applied, display the game.
        // To do: Abstract the "game" id
        $("#game").show();

        startGame();
        view.showHint();
    }


    this.keyEvent = function(e) {
        if (gameSuspended) { return true; }
        e = (e == undefined ? event : e);

        var key = $fp.getKey(e);

        // Letter
        if (key.isLetter) {
            var letter = key.letter.toLowerCase();
            var guess = view.getLettersTyped();

            if (model.letterIsValid(letter, guess)) {
                var oldValue = guess;
                var newValue = oldValue + letter.toUpperCase();
                view.setLettersTyped(newValue);
                textChanged();
            }
        }

        // Backspace, Delete
        else if ((key.type == 'backspace') || (key.type == 'delete')) {

            var oldValue = view.getLettersTyped();
            if (oldValue.length > 0) {

                var newValue = oldValue.substring(0, oldValue.length - 1);
                view.setLettersTyped(newValue);
                textChanged();
            }
            return false;
        }

        // 1
        else if (key.ch === '1') { showNumSolutions(); }
        // !
        else if (key.ch === '!') { showSolutions(); }
        // Esc
        else if (key.type === 'esc') {
            view.setLettersTyped('');
            textChanged();
        }
        // Blankspace
        else if ((key.ch === ' ') && !buttonHasFocus) {
            _this.scramble();
        }

        return true;
    }


    this.scramble = function() {
        if (!gameSuspended) {
            model.scrambleWord();
            view.setTiles( model.getScrambled() );
            view.setLettersTyped('');
            view.setStatus('');
            updatePersistentSettings();
        }
    }
    
    
    this.skip = function() {
        if (!gameSuspended) {
            model.nextWord();
            startGame();
        }
    }


    this.playAgain = function() {
        view.updateButtons(true);
        view.hideSolved();
        gameSuspended = false;

        model.nextWord();
        startGame();
    }
    
    
    this.tilesChanged = function() {
        if (!gameSuspended) {
            view.hideHint();
            view.setLettersTyped('');
            view.setStatus('');

            var guess = view.getTiles();
            model.setScrambled(guess);
            updatePersistentSettings();

            if (model.isSolution(guess)) {
                solved();
            }
        }
    }


//    this.enterKeyAction = function() {
//    }


    this.diffAction = function() {
        for (var i = 0; i < 4; i++) {
            if (document.forms["pref"].elements["diffLevel"][i].checked) {
                model.setDifficulty(i);
            }
        }
        updatePersistentSettings();
    }


    this.themeAction = function() {
        var theme = view.getSelectedTheme();
        model.setUiPref(View.OPT_THEME, theme);
        view.updateTheme();
        updatePersistentSettings();
    }


    this.autoStartAction = function() {
        model.toggleUiPref(View.OPT_AUTO_START);
        updatePersistentSettings();
    }


    this.optionsShownAction = function() {
        model.toggleUiPref(View.OPT_OPTIONS_SHOWN);
        view.updateOptionsShown();
        updatePersistentSettings();
    }
}




