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


//=========================================================
//
// Model constructor
//
// Constants:  (static properties)
//   DIFF_EASY
//   DIFF_MEDIUM
//   DIFF_HARD
//   DIFF_RANDOM
//
// Methods:
//   init()
//
//   letterIsValid(letter, guess)
//   isSolution(guess)
//
//   nextWord()
//   scrambleWord()
//   useScrambledWord(scrambled)
//   getLetters()
//
//   getNumSolutions()
//   getSolutions()
//
//   getScrambled()
//   setScrambled(scrambled)
//
//   getDifficulty()
//   setDifficulty(i)
//
//   getUiPref(name)
//   setUiPref(name, value)
//   toggleUiPref(name)
//
function Model() {

    // Private properties

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

    var DIFF_MIN_MAX = [[4, 5], [6, 7], [8, 12], [4, 12]];
    var difficulty = Model.DIFF_RANDOM;

    var letters = "";
    var prevLetters = "";
    var solutions = "";
    var numSolutions = 0;

    var scrambled = "";
    var prevScrambled = "";

    var uiPref = new Array();


    // Private methods

    function countWords() {
        wordCountByLength = new Array();
        wordCountByDifficulty = [0, 0, 0, 0];

        for (var i = 0; i < lettersTable.length; i++) {
            var wordLength = i + 4;
            wordCountByLength[wordLength] = lettersTable[i].length;

            for (var diff = 0; diff <= 3; diff++) {
                if ((wordLength >= DIFF_MIN_MAX[diff][0]) &&
                    (wordLength <= DIFF_MIN_MAX[diff][1]))
                {
                    wordCountByDifficulty[diff] += lettersTable[i].length;
                }
            }
        }
    }


    function chooseWord(num) {
        // Identify where in the table to pick a word

        var wordLength = DIFF_MIN_MAX[difficulty][0];
        while (num >= wordCountByLength[wordLength]) {
            num -= wordCountByLength[wordLength];
            wordLength++;
        }

        // Get the word

        // Get the hash from the indexed array
        letters = lettersTable[wordLength - 4][num];
        // Use hash to get data from the associative array
        solutions = solutionsTable[wordLength - 4][letters];

        // Number of valid anagrams
        numSolutions = Math.round(solutions.length / wordLength);
    }


    // Privileged methods

    this.init = function() {
        countWords();
    }


    this.nextWord = function() {
        var wordCount = wordCountByDifficulty[difficulty];
        prevLetters = letters;
        do {
            var num = Math.round(Math.random() * (wordCount - 1));
            chooseWord(num);
        } while (letters == prevLetters);

        prevScrambled = "";
        this.scrambleWord();
    }


    // letterIsValid
    //
    // See if the letter can be used in the word

    this.letterIsValid = function(letter, guess) {
        var isValid = false;
        var unmatchedLetters = this.getLetters();
    
        // Determine which of the letters have not already been used
        
        // For each letter in the guess
        for (var i = 0; i < guess.length; i++) {

            // Find a matching letter in the anagram
            for (var j = 0; j < unmatchedLetters.length; j++) {
                if (guess.charAt(i) == unmatchedLetters.charAt(j)) {

                    // Mark the letter as having been used
      	            unmatchedLetters = unmatchedLetters.substr(0, j) + " " +
                                       unmatchedLetters.substr(j + 1);
                    break;
                }
            }
        }
        
        // If the letter has not already been used
        for (var i = 0; i < unmatchedLetters.length; i++) {
            if (letter == unmatchedLetters.charAt(i)) {
                isValid = true;
                break;
            }
        }
        
        return isValid;
    }

   
    this.isSolution = function(guess) {
        var result = false;
        var wordLength = letters.length;
        for (var i = 0; i < numSolutions; i++) {
            if (guess == solutions.substring(i * wordLength,
                                             (i * wordLength) + wordLength)) {
                result = true;
                break;
            }
        }
        return result;
    }
    
    
    this.scrambleWord = function() {
        do {
            var remainingLetters = letters;
            scrambled = "";
            for (var i = letters.length; i > 1; i--) {
                var charNum =
                    Math.round(Math.random() * (remainingLetters.length - 1));
                scrambled += remainingLetters.charAt(charNum);
                remainingLetters = remainingLetters.substring(0, charNum) +
                                   remainingLetters.substring(charNum + 1, i);
            }
            scrambled += remainingLetters;
        } while ((scrambled == prevScrambled) || this.isSolution(scrambled));
    }


    this.useScrambledWord = function(tempScrambled) {
        if (tempScrambled) {
            var tempLetters = (tempScrambled ? $fp.sort(tempScrambled) : '');
            var wordLength = tempLetters.length;

            // If the word is a valid length
            if ((wordLength >= DIFF_MIN_MAX[Model.DIFF_RANDOM][0]) &&
                (wordLength <= DIFF_MIN_MAX[Model.DIFF_RANDOM][1]))
            {
                // If the word is in the vocabulary
                var tempSolutions =
                    solutionsTable[wordLength - 4][tempLetters];
                if (tempSolutions) {
                    solutions = tempSolutions;
                    numSolutions = Math.round(solutions.length / wordLength);
                    letters = tempLetters;
                    prevScrambled = "";
                    scrambled = tempScrambled;

                    if (this.isSolution(scrambled)) {
                        this.scrambleWord();
                    }
                    return true;
                }
            }
            return false;
        }
    }


    this.getLetters = function() { return letters; }

    this.getNumSolutions = function() { return numSolutions; }
    this.getSolutions = function()    { return solutions; }

    this.getScrambled = function()          { return scrambled; }
    this.setScrambled = function(scrambled) { scrambled = scrambled; }

    this.getDifficulty = function()  { return difficulty; }
    this.setDifficulty = function(i) { difficulty = i; }

    this.getUiPref = function(name) {return uiPref[name];}
    this.setUiPref = function(name, value) {uiPref[name] = value;}
    this.toggleUiPref = function(name) {uiPref[name] = !uiPref[name];}
}
    

// Static Model properties

Model.DIFF_EASY   = 0;
Model.DIFF_MEDIUM = 1;
Model.DIFF_HARD   = 2;
Model.DIFF_RANDOM = 3;




