/* ideas and remarks

key events: keypress also captures repeated input from a key being held but 
    does not destinguish between keys which do not produce text
*/


$().ready( function() {
    
    // some global variables
    var lastKeyPressCode = null;
    var currentCommand = null;
    var previousCommand = null;
    var currentSuggestions = null;
    var inCommand = false;
    var commandCache = {};
    var maxSuggestions = 10;
    var firstSuggestion = 0;
    var selection = null;
    var keyDownCode = null;
    
    var texGreekLetters = [
        "alpha", "beta", "gamma", "delta", "epsilon", "varepsilon", "zeta",
        "eta", "theta", "vartheta", "iota", "kappa", "lambda", "mu", "nu", "xi",
        "o", "pi", "varpi", "rho", "varrho", "sigma", "varsigma", "tau",
        "upsilon", "phi", "varphi", "chi", "psi", "omega", "Gamma", "Delta",
        "Theta", "Lambda", "Xi", "Pi", "Sigma", "Upsilon", "Phi", "Psi", "Omega"
        ];
    
    var texOrd = [
        "aleph", "hbar", "imath", "jmath", "ell", "wp", "Re", "Im", "partial",
        "infty", "prime", "emptyset", "nabla", "surd", "top", "bot", "|",
        "angle", "triangle", "backslash", "forall", "exists", "neg", "lnot",
        "flat", "natural", "sharp", "clubsuit", "diamondsuit", "heartsuit",
        "spadesuit"
        ];
        
    var texLargeOperators = [
        "sum", "prod", "coprod", "int", "oint", "bigcap", "bigcup", "bigsqcup",
        "bigvee", "bigwedge", "bigodot", "bigotimes", "bigoplus", "biguplus"
        ];
    
    var texBinaryOperations = [
        "pm", "mp", "setminus", "cdot", "times", "ast", "star", "diamond",
        "circ", "bullet", "div", "cap", "cup", "uplus", "sqcap", "sqcup",
        "triangleleft", "triangleright", "wr", "bigcirc", "bigtriangleup",
        "bigtriangledown", "vee", "lor", "wedge", "land", "oplus", "ominus",
        "otimes", "oslash", "odot", "dagger", "ddagger", "amalg"
        ]; 
    
    var texRelations = [
        "leq", "le", "prec", "preceq", "ll", "subset", "subseteq", "sqsubseteq",
        "in", "vdash", "smile", "frown", "propto", "geq", "ge", "succ",
        "succeq", "gg", "supset", "supseteq", "sqsupseteq", "notin", "dashv",
        "mid", "parallet", "equiv", "sim", "simeq", "asymp", "approx", "cong",
        "bowtie", "ni", "owns", "models", "doteq", "perp", "ne"
        ];
    
    var texSpecials = [
        "not", "left", "right"
        ];
    
    var autocompleteArray = new Array();
    autocompleteArray = autocompleteArray.concat(texGreekLetters);
    autocompleteArray = autocompleteArray.concat(texOrd);
    autocompleteArray = autocompleteArray.concat(texLargeOperators);
    autocompleteArray = autocompleteArray.concat(texBinaryOperations);
    autocompleteArray = autocompleteArray.concat(texRelations);
    autocompleteArray = autocompleteArray.concat(texSpecials);
    autocompleteArray.sort();
    
    // create a long string concatinating all commands
    // use this to verify if a command is in array
    // there should be a more efficient way to do this
    var allCommands = " "+autocompleteArray.join(" ")+" ";
    
    
    
    function fillCommandCache() {
        // loop through the array and create a lookup structure
        var i,j;
        for ( i=0; i < autocompleteArray.length; i++ ){
            for ( j=0; j<autocompleteArray[i].length-1; j++ ){
                prefix = autocompleteArray[i].substring(0,j+1);
                //$("#log").prepend(autocompleteArray[i].substring(0,j+1)+" ");
                if( !commandCache[prefix] ) 
                    commandCache[prefix] = [];
                commandCache[prefix].push(i);
            }
        }
    }
    
    function lookupInCommandCache() {
        if (currentCommand=="") {
            currentSuggestions = new Array();
            for (var i=0; i<autocompleteArray.length; i++ )
                currentSuggestions.push(i);
            return;
        }
        if ( currentCommand && commandCache[currentCommand] )
            currentSuggestions = commandCache[currentCommand];
        else
            currentSuggestions = null;
    }
    
    function presentSuggestions() {
        if ( currentSuggestions ) {
            var tableArray = new Array();
            var nr;
            tableArray.push("<table cellspacing=\"0\" cellpadding=\"0\">");
            if (firstSuggestion>0)
                tableArray.push("<tr><td colspan=\"2\">move up to see "+firstSuggestion+" more</td></tr>");
            else
                tableArray.push("<tr><td colspan=\"2\">&nbsp;</td></tr>");
            for ( var i=firstSuggestion; i<currentSuggestions.length && i<(maxSuggestions+firstSuggestion); i++) {
                tableArray.push("<tr><td>");
                
                if (i-firstSuggestion<9)
                    nr = i-firstSuggestion+1;
                if (i-firstSuggestion==9)
                    nr = 0;
                if (i-firstSuggestion>9)
                    nr = null;
                if (nr || nr==0)
                    tableArray.push(nr.toString()+"&nbsp;");
                    
                tableArray.push("</td><td>");
                tableArray.push(autocompleteArray[currentSuggestions[i]]);
                tableArray.push("</td></tr>");
            }
            if (i<currentSuggestions.length)
                tableArray.push("<tr><td colspan=\"2\">move down to see "+(currentSuggestions.length-maxSuggestions-firstSuggestion)+" more</td></tr>");
            else
                tableArray.push("<tr><td colspan=\"2\">&nbsp;</td></tr>");
            tableArray.push("</table>");
            $("#suggestions").html(tableArray.join(""));
            tableArray = null;
        }
        else
            $("#suggestions").html("");
    }
    
    function highlightSelection() {
        $('#suggestions tr').removeClass("selected");
        $('#suggestions tr').eq(selection+1).addClass("selected");
    }
    
    function showHelp() {
        if ( currentSuggestions!=null && selection!=null ) {
            $("#help").html("Some helpful information on the \""+autocompleteArray[currentSuggestions[selection+firstSuggestion]]+"\" command should appear here.");
            $("#help").append("<p><img src=\"http://www.mathtran.org/cgi-bin/mathtran?D=1;tex=%5C"+autocompleteArray[currentSuggestions[selection+firstSuggestion]]+"\"></p>")
        }
        else{
            if (currentCommand!=null) {
                if(allCommands.indexOf(" "+currentCommand+" ")==-1)
                    $("#help").html("\""+currentCommand+"\" does not exist");
                else {
                    $("#help").html("Some helpful information on the \""+currentCommand+"\" command should appear here.");
                    $("#help").append("<p><img src=\"http://www.mathtran.org/cgi-bin/mathtran?D=1;tex=%5C"+currentCommand+"\"></p>")
                }
            }
            else
                $("#help").html("");
        }
    }
    
    function insertCommand() {
        if (selection!=null) {
            var restOfCommand = autocompleteArray[currentSuggestions[selection+firstSuggestion]];
            restOfCommand = restOfCommand.substring(currentCommand.length,restOfCommand.length);
            $("#inputarea").replaceSelection(restOfCommand);
        }
    }
    
    function txtToCodeTxt(txt) {
        var out = "";
        for (var i=0; i<txt.length; i++)
            out += txt.charAt(i).charCodeAt(0)+" ";
        return out;
    }
    
    function boxToCodes() {
      var fieldText = $("#inputarea").val();
      $("#area").text("length "+fieldText.length+": "+txtToCodeTxt(fieldText));
    }

    // parse text from right to left until a space, newline or \ or \\ is seen
    function validateField() {
        //boxToCodes(); // for debugging; dump all char codes
        var fieldText = $("#inputarea").val();
        //$("#log").prepend(fieldText+" ");
        //$("#log").prepend(fieldText.length+" ");
        var lastWord = "";
        var txtSelection = $("#inputarea").getSelection();
        //$("#pos").text(txtSelection.start+" "+txtSelection.length+" "+txtSelection.end+" : "+txtToCodeTxt(txtSelection.text));
        
        var thisc = "";
        
        var pos = txtSelection.start;
        while (pos>0) {
            thisc = fieldText.charAt(pos-1).charCodeAt(0);
            if (thisc==92 && pos>1 && fieldText.charAt(pos-2).charCodeAt(0)==92) {
                lastWord = fieldText.charAt(pos-1)+lastWord;
                pos -= 1;
                break;
            }
            else 
                //if (thisc==92 || thisc==32 || thisc==10) // "\", " " or newline
                if ( thisc<65 || (thisc>90 && thisc<97) || thisc>122 ) // thisc is not in a-z or A-Z
                    break;
            lastWord = fieldText.charAt(pos-1)+lastWord;
            pos -= 1;
        }
        //$("#log").prepend("lastWord:\""+lastWord+"\" ");
        previousCommand = currentCommand;
        
        if (pos > 0 && fieldText.charAt(pos-1).charCodeAt(0)==92)
            currentCommand = lastWord;
        else
            currentCommand = null;
          
    }
    
    function update(e) {
        validateField();
        if (currentCommand!=previousCommand) {    
            lookupInCommandCache();
            selection = null;
            firstSuggestion=0;
            presentSuggestions();
            showHelp();
        }
    };

    fillCommandCache();
    
    $("#inputarea").keyup(update).mouseup(update);
    
    
    
    $("#inputarea").keydown(function(e) {
        keyDownCode = e.which;
        if (currentSuggestions) {
            if (keyDownCode>=48 && keyDownCode<=57) {
                e.preventDefault();
                var nr = keyDownCode - 48;
                if (nr==0)
                    selection = 9;
                if (nr>0 && nr<10)
                    selection = nr-1;
                if (selection>currentSuggestions.length-1 || selection>maxSuggestions-1)
                    selection = null;
                insertCommand();
                //validateField();
                return;
            }
            // catch up arrow and down arrow key events to navigate in the list
            // of suggested autocompletes
            if (keyDownCode==40 || keyDownCode==38) { // down or up
                e.preventDefault();
                if (selection==null)
                    selection = 0;
                else
                    selection += keyDownCode-39;
                // moved down in list beyond end but list is longer
                if (selection >= maxSuggestions && selection+firstSuggestion < currentSuggestions.length) {
                    firstSuggestion += 1;
                    selection = maxSuggestions-1;
                    presentSuggestions();
                    highlightSelection();
                    showHelp();
                    return;
                }
                // moved selection up beyond first element
                if (selection<0 && firstSuggestion>0) {
                    firstSuggestion -= 1;
                    selection = 0;
                    presentSuggestions();
                    highlightSelection();
                    showHelp();
                    return;
                }
                
                if (selection >= maxSuggestions || selection >= currentSuggestions.length) {
                    selection = 0;
                    firstSuggestion = 0;
                    presentSuggestions();
                }
                else
                    if (selection<0 && firstSuggestion==0) {
                        selection = Math.min(maxSuggestions,currentSuggestions.length)-1;
                        firstSuggestion = Math.max(currentSuggestions.length-maxSuggestions,0);
                        presentSuggestions();
                    }
                
                //$("#log").prepend("selection:"+selection+" ");
                //$("#log").prepend("firstSuggestion:"+firstSuggestion+" ");
                highlightSelection();
                showHelp();
                return;
            }
            if (keyDownCode==13) { // enter key
                e.preventDefault();
                insertCommand();
                return;
            }
        }
    });
/*    
    $("#inputarea").keypress(function(e) {
        if (currentSuggestions) {
            if (keyDownCode==40 || keyDownCode==38 || keyDownCode==13)
                e.preventDefault();
        }
    });
*/
    
        
    
});


