// ----------------------------------------------------------------------
// Morse tutor
// ----------------------------------------------------------------------
//  Copyright 1998-2008 "Nosey" Nick Waterman, Ward Cunningham and Jim Wilson
//  Distributed under the GNU GPL V2 license.
//  See http://noseynick.net/va3nnw/cw/ and http://c2.com/morse
//
//  This file is part of the Nilex Morse Tutor.
//
//  The Nilex Morse Tutor is free software; you can redistribute it
//  and/or modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 2 of
//  the License, or (at your option) any later version.
//
//  The Nilex Morse Tutor is distributed in the hope that it will be
//  useful, but WITHOUT ANY WARRANTY; without even the implied warranty
//  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  General Public License for more details.
//
//  You should have received a copy of the GNU General Public License along
//  with the Nilex Morse Tutor: http://noseynick.net/va3nnw/cw/license.txt
//  If not, write to the Free Software Foundation, Inc., 59 Temple
//  Place, Suite 330, Boston, MA  02111-1307 USA
// ----------------------------------------------------------------------
// common stuff:

var vol   = 100;
var speed = 30;
var freq  = 440;

var bad   = 200;  // Height of a 100% full bar ("bad" score for that letter)
var overall = 0;
var vgood_overall = 0.1 * bad;
var good_overall  = 0.3 * bad;
var good_letter   = 0.4 * bad;

var answer = 0;
var give = 3500; // timeout for lesson failure, milliseconds
var resting = true;
var sent_time = new Date();
var grade = false;

var timer = null;

var idmap = {
  "0": "_0",  "_0": "0",      "1": "_1",  "_1": "1",
  "2": "_2",  "_2": "2",      "3": "_3",  "_3": "3",
  "4": "_4",  "_4": "4",      "5": "_5",  "_5": "5",
  "6": "_6",  "_6": "6",      "7": "_7",  "_7": "7",
  "8": "_8",  "_8": "8",      "9": "_9",  "_9": "9",
  ".": "stop", "stop": ".",   "/": "slash", "slash": "/",
  "=": "equal", "equal": "=", "?": "question", "question": "?",
  ",": "comma", "comma": ","
};

function chg_all() { // called during setup
  chg_order(); // which also calls chg_types();
  chg_tone();
}

function chg_order() { // called from the HTML
  // var order = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,/=?"
  // var order = "Q7ZG098O1JPW.LRAM6B/XD=YCKN23?FU45VHSITE";
  // var order = "KMURESNAPTLWI.JZ=FOY,VG5/Q92H38B?47C1D60X";
  var order = _("order").value
  var toprow = '<table class="bars" id="bars"><tr>';
  var botrow = '</tr><tr>';
  for (var i=0; i < order.length; i++) {
    var c = order.charAt(i);
    toprow += '<td'; botrow += '<th';
    if ("0123456789".indexOf(c) >= 0) {
      toprow += ' class="nrs"';
      botrow += ' class="nrs"';
    } else if ("ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(c) < 0) {
      toprow += ' class="syms"';
      botrow += ' class="syms"';
    }
    toprow += '><div id="' + (idmap[c] || c) + '"'
           + (i > 3 ? ' class="later"' : '')
   	   + ' onclick="togglater(this)"></div></td>\n';
    botrow += '>' + c + '</th>\n';
  }
  _("barsdiv").innerHTML = toprow + botrow + '</tr></table>';
  debug("Table rebuilt");
  chg_types(); // so style.visibility can be trusted
}

function togglater(ob) {
  if (ob.className) {
    ob.className = "";
    debug("Toggle " + (idmap[ob.id] || ob.id) + " inactive");
  } else {
    ob.className = "later";
    debug("Toggle " + (idmap[ob.id] || ob.id) + " active");
  }
}

function chg_types() { // called from the HTML
  var symdisp = "none";
  var nrsdisp = "none";
   
  // IE doesn't like explicit "table-cell" but "" seems to work  :-/
  if (_("nrs").checked ) { nrsdisp = ""; }
  if (_("syms").checked ) { symdisp = ""; }

  debug("Setting nums to [" + nrsdisp + "] and syms to [" + symdisp + "]");
   
  var tds = _("bars").getElementsByTagName("*");
  for (var i=0; i < tds.length; i++) {
    if (tds[i].className == "syms") {
      tds[i].style.display = symdisp;
    } else if (tds[i].className == "nrs") {
      tds[i].style.display = nrsdisp;
    }
  }
}

function keypress(e) { // Called from the HTML
  var key = String.fromCharCode(e.keyCode || e.which).toUpperCase();
  
  // allow tab-navigation, also ESC:
  if (key == "\t") { debug("tab"); return true; }
  if (key == "\x1B") { debug("esc"); _("text").blur(); return false; }
  if (still_sending()) { debug("still sending: ignore "+key); return false; }
  if (!key || key != answer) { debug("wrong key "+key); return false; }
  
  // We got the right key, in time!
  if (timer) { clearTimeout(timer); timer = null; }

  press_time = new Date();
  debug("Time taken " + (press_time - sent_time) + " grade=" + grade);
  
  append(key);
  
  var elem = _(idmap[answer] || answer);
  var height = elem.offsetHeight;
  if (grade) {                            // If student passed the test,
    give = 0.875*give + 0.25 * (press_time - sent_time);  // Update slack.
    if (give > 6000) { give = 6000; }
     // lower the bar:
     height *= 0.875;
     overall *= 0.875;
     if (overall < vgood_overall) {
       // If overall error rate is VERY low, accelerate char decay rate
       height *= 0.875;
     }
     graduate();
  } else {
     // raise the bar:
     height = (height * 0.875) + (0.125 * bad);
     overall = (overall * 0.875) + (0.125 * bad);
  }
  debug("Bar " + answer + ": " + elem.offsetHeight + " to " + height + " (overall " + overall + ")");
  elem.style.height = height + "px";
  
  grade = true;
  sending();
  send_cw(answer = select_letter());
  return false;
}

function append(key) {
  var text = _("text");
  text.value += key;
  while (text.value.length > 20) { text.value = text.value.substring(1); }
}

function txtfocus() { // called from the HTML
  debug("focus");
  _("text").value = "";
  resting = false;
  grade = true;
  sending();
  send_cw(answer = select_letter());
}

function txtblur() { // called from the HTML
  debug("blur");
  if (timer) { clearTimeout(timer); timer = null; }
  resting = true;
  _("text").value = "Click in this box to resume";
}

function chg_tone() { // called from the HTML
  vol   = _("vol").value;
  speed = _("speed").value;
  freq  = _("freq").value;
  
  debug(freq + "Hz " + vol + "% vol, " + speed + "wpm");
  test_cq();
}

function sending() {
  sent_time = new Date();
}

function done_sending() {
  sending();

  debug("done sending");
  if (!resting) {
    debug("set timer for " + give + "ms");
    if (timer) { clearTimeout(timer); }
    timer = setTimeout( "too_slow()", give );
  }
}

function too_slow() {
  grade = false;
  append(answer);
  sending();
  send_cw(answer);
}

function select_letter() {
  var divs = _("bars").getElementsByTagName("div");
  var ret = "SELECT:";
  var sum = 0;
  var result = "";
  
  for(var i=0; i < divs.length; i++) {
    if (divs[i].parentNode.style.display == "none" ||
        divs[i].className == "later") {
      ret += " (" + divs[i].id + "=" + divs[i].offsetHeight + ")";
    } else {
      ret += " " + divs[i].id + "=" + divs[i].offsetHeight;
      sum += divs[i].offsetHeight;
    }
  }
  
  ret += " total=" + sum;
  sum *= Math.random();
  ret += " rand=" + sum;
  
  for(var i=0; i < divs.length; i++) {
    if (divs[i].parentNode.style.display == "none" ||
        divs[i].className == "later") {
      // ignore
    } else {
      result = divs[i].id;
      sum -= divs[i].offsetHeight;
      if (sum < 0) { break; }
    }
  }
  
  ret += " result=" + result;
  // debug(ret);
  return idmap[result] || result;
}

function graduate() {
   // If overall error rate too high, do not graduate.
  if (overall > good_overall) { return; }
  
  var next_letter = null; // Candidate for new letter
  var divs = _("bars").getElementsByTagName("div");
  
  for(var i=0; i < divs.length; i++) {
    if (divs[i].parentNode.style.display != "none") {
      //   If in course outline:
      if (divs[i].className != "later") {
        // If unlearned, hold 'em back.
        if (divs[i].offsetHeight > good_letter) { return; }
      } else if (!next_letter) {
        // Note first untaught letter:
        next_letter = divs[i];
      }
    }
  }
  if (next_letter) {
    next_letter.className = ""; // Activate next letter
  }
}
