if(!document.getElementById) alert('Your browser doesn\'t support all the necessary functions to use the calculator.\\nPlease use a different browser.');

//utility functions
//--------------------------------

function byId(id) {
  return document.getElementById(id);
}
function crEl(el) {
  return document.createElement(el);
}

buttonQueue = [];
oldClassQueue = [];
function press(button) {
  if(errorDisplayed && button != 'c' && button != 'ac') return;
  var button = byId(button);
  if(button.className.indexOf('disabled') == -1) {// don't highlight the dot when it's disabled.
    if(button.className.indexOf(' pressed') == -1) {
      buttonQueue.push(button);
      oldClassQueue.push(button.className);
      button.className += ' pressed';
      setTimeout('restoreButton()', 200);
    }
    button.click();
  }
}
function restoreButton() {
  buttonQueue.shift().className = oldClassQueue.shift();
}

//keyboard stuff
//--------------------------------

// onkeypress fires when characters are typed. Other keys such as arrows and
// backspace don't fire this event.
function keypressKeys(evt) {
  if(this.activeElement.id == 'q') return true; //ignore input to the search box
  if(!evt) var evt = window.event;
  // find the code of the pressed key.
  if(evt.altKey || evt.ctrlKey) return true; // We don't want Ctrl- or Alt-modified keys
  var code;
  if(evt.keyCode) code = evt.keyCode;
  else if(evt.charCode) code = evt.charCode;
  else code = 0;
  var key = String.fromCharCode(code);
  
  // Decide which button to press
  if(key.match(/^[0-9]$/)) press('b' + key);
  else if(key.match(/^[s:S ]$/)) press('colon');
  else if(key == '.') {
    if(dotAsColon) press('colon');
    else press('dot');
  }
  else if(key.match(/^[vV,]$/)) press('dot');
  else if(key.match(/^[pP+]$/)) press('plus');
  else if(key.match(/^[-mM]$/)) press('sub');
  else if(key.match(/^[*tT]$/)) press('mul');
  else if(key.match(/^[dD/]$/)) press('div');
  else if(key.match(/^[eE=]$/)) press('eq');
  else if(key.match(/^[cC]$/)) press('c');
  else if(key.match(/^[aA]$/)) press('ac');
  else if(key.match(/^[bB<]$/)) press('bs');
  else if(key.match(/^[qQ]$/)) press('m+');
  else if(key.match(/^[wW]$/)) press('m-');
  else if(key.match(/^[rR]$/)) press('mr');
  else if(key.match(/^[xX]$/)) press('mc');
  else if(key.match(/^[hH?]$/)) {
    try {
      switchHelp('help-intro');
      help();
    }
    catch(e) {
      if(console) console.error('Help error: ' + e);
      else alert('Help error: ' + e);
    }
  }
  else if(key.match(/^[kK]$/)) keys();
  else {
    if(console) console.info('No keybinding for "' + key + '"');
    return true; // The key wasn't meant for us. Perhaps someone else is interested.
  }
  return false; // We've processed the key. No one else need mess with it.
}

function keyupKeys (evt) {
  if(this.activeElement.id == 'q') return true; //ignore input to the search box
  if(!evt) var evt = window.event;
  var code = evt.keyCode;
  
  //handle special keys:
  switch(code) {
    case  9: // tab
    case 39: // right arrow
      press('colon'); break;
    case 37: // left arrow
      press('colon');
      press('colon'); break;
    case 13: // enter
      press('eq'); break;
    case  8: // backspace
      press('bs'); break;
    case 46: // delete
    case 144:// numpad clear
      press('c'); break;
    case 27: // esc
      press('ac'); break;
    default: // Not a key this function handles
      return true;
  }
  return false;
};

document.onkeypress = keypressKeys;
document.onkeyup = keyupKeys;
function loadCalculator() {
  setBlur(document.getElementsByTagName('BODY')[0].children);
  var selections = ['h', 'm', 's'];
  var clears = ['c', 'ac'];
  var specialNumbers = ['dot', 'colon'];
  var i = null;
  for(i = 0; i < 10; i++) {
    byId('b' + i).onclick = function () { insertNumber(this.value); };
  }
  for(i = 0; i < selections.length; i++) {
    byId(selections[i]).onclick = function () { setSelection(this.id); };
  }
  for(i = 0; i < clears.length; i++) {
    byId(clears[i]).onclick = function () { clearX(this.value); };
  }
  for(i = 0; i < specialNumbers.length; i++) {
    byId(specialNumbers[i]).onclick = function () { insertNumber(this.value); };
  }
  byId('plus').onclick = function () { calc('+', false); };
  byId('sub').onclick = function () { calc('-', false); };
  byId('mul').onclick = function () { calc('*', false); };
  byId('div').onclick = function () { calc('/', false); };
  byId('bs').onclick = function () { backspace(); };
  byId('eq').onclick = function () { equals(); };
  byId('c').ondblclick = function () { clearX('AC'); };
  byId('m+').onclick = function () { memSet('+'); };
  byId('m-').onclick = function () { memSet('-'); };
  byId('mr').onclick = function () { memRecall(); };
  byId('mc').onclick = function () { memClear(); };
  byId('dotascolon').onchange = function () {
    if(this.checked) setDotAsColon(true);
    else setDotAsColon(false);
  };
  setDotAsColon(getDotAsColon());
  //BigDecimal constants
  MINUS_ONE = new BigDecimal('-1');
  ZERO = new BigDecimal('0');
  TEN = new BigDecimal('10');
  SIXTY = new BigDecimal('60');
}
function getDotAsColon() {
  var status = readCookie('dotAsColon');
  if(status) return (status == 'true') ? true : false;
  else return false;
}
function setDotAsColon(status) {
  var box = byId('dotascolon');
  if(status != dotAsColon) {
    dotAsColon = status;
    var str = (status==true)?'true':'false';
    createCookie('dotAsColon',str,3650,'/html/time_calculator.htm');
  }
  if(box.checked != status) {
    box.checked = status;
  }
}
function setBlur(items) {
  var item;
  for(var i = 0; i < items.length; i++) {
    item = items[i];
    if(item.id == 'search-box-wrapper') continue;
    if(item.children) setBlur(item.children);
    try {
      item.onfocus = function () { this.blur(); };
    }
    catch (e) {
      if(console) console.warn(e);
    }
  }
}

function extraOnload() {
  loadCalculator();
}

//calculator functions
//--------------------------------
dotAsColon = false;
selection = 'h';
operation = ''; //+-*/=E or empty
//stored values
v1 = null,v2 = null;
memory = null;
error = null;
errorDisplayed = false;
clearOnNext = false;
operatorPressedLast = false;

function hms2s(hms) { // hms is an array of [neg, h, m, s]
  var s = hms[3];
  var sixty_sq = SIXTY.multiply(SIXTY);
  s = s.add(hms[2].multiply(SIXTY)).add(hms[1].multiply(sixty_sq));
  if(hms[0]) s = s.multiply(MINUS_ONE);
  return s;
}

function s2hms(s) {
  var neg = false;
  var h, m;
  if(s.compareTo(ZERO) < 0) {
    neg = true;
    s = s.multiply(MINUS_ONE);
  }
  m = new BigDecimal(parseInt(s.divideInteger(SIXTY)) + '');
  s = s.remainder(SIXTY);
  h = new BigDecimal(parseInt(m.divideInteger(SIXTY)) + '');
  m = m.remainder(SIXTY);
  return [neg, h, m, s];
}

function getScreenValue() {
  var neg = false;
  var h = byId('h').innerHTML;
  if(h.indexOf('Error') != -1) return false;
  if(h.indexOf('-') == 0) {
    neg = true;
    h = new BigDecimal(h);
    if(h.compareTo(ZERO) < 0) h = h.multiply(MINUS_ONE);
  }
  else h = new BigDecimal(h);
  var m = new BigDecimal(byId('m').innerHTML);
  var s = new BigDecimal(byId('s').innerHTML);
  return [neg, h, m, s];
}

function setSelection(s) {
  if(errorDisplayed) return;
  if(s != selection) {
    var oldSelection = selection;
    selection = s;
    byId(oldSelection).className = 'not-selected';
    byId(selection).className = 'selected';
    var x = byId('dot');
    if(s == 's') {
      x.removeAttribute('disabled');
      x.className = 'number';
    }
    else {
      x.setAttribute('disabled','disabled');
      x.className = 'disabled';
    }
  }
}

function setOperator(op) {
  operation = op;
  var x = byId('operator');
  switch(op) {
    case '':
      x.innerHTML = '&nbsp;';
      break;
    case '/':
      x.innerHTML = '&divide;';
      break;
    case '*':
      x.innerHTML = '&times;';
      break;
    case '-':
      x.innerHTML = '&minus;';
      break;
    default:
      x.innerHTML = op;
  }
  //writeHistory('operation');
}

function formatDisplay(hms) {
  var neg = hms[0];
  var h = hms[1];
  var m = hms[2];
  var s = hms[3];
  if(h.indexOf && h.indexOf('Error') != -1) return [h, m, s];
  if(m.compareTo(TEN) < 0) m = '0' + m;
  else m = m + '';
  if(s.compareTo(TEN) < 0) s = '0' + s;
  else s = stripTrailingZeroes(s + '');
  if(neg) h = '-' + h;
  else h = h + '';
  return [h, m, s];
}

function stripTrailingZeroes(n) {
  if(n.indexOf('.') == -1) return n;
  var extras = n.match(/[0.]+$/);
  if(extras) return n.slice(0, n.length - extras[0].length);
  else return n;
}

function setDisplay(hms) {
  if(errorDisplayed) return;
  hms = formatDisplay(hms);
  byId('h').innerHTML = hms[0];
  byId('m').innerHTML = hms[1];
  byId('s').innerHTML = hms[2];
}

function clearDisplay() {
  setDisplay([false, ZERO, ZERO, ZERO]);
}

function insertNumber(n) {
  if(errorDisplayed) return;
  if(clearOnNext && n != ':') {
    clearDisplay();
    clearOnNext = false;
  }
  if(n == ':') {
    switch(selection) {
      case 'h':
        setSelection('m');
        break;
      case 'm':
        setSelection('s');
        break;
      case 's':
        setSelection('h');
        break;
    }
    return;
  }
  var x = byId(selection);
  if((n == '.') && (x.innerHTML.indexOf('.') >= 0 || byId('dot').className.indexOf('disabled') != -1)) return;
  writeInsertedDigit(n, x);
  operatorPressedLast = false;
}

function writeInsertedDigit(digit, element) { // "element" can be an element node or the ID of an element.
  if(typeof(element) == 'string') var x = byId(element);
  else {
    var x = element;
    element = x.id;
  }
  var y = x.innerHTML;
  digit += '';
  if(y == 0 && y.substring(0, 4) != '00.0') {
    if(x.innerHTML.indexOf('.') == -1) y = digit;
    else y = '.' + digit;
  }
  else y += digit;
  while(y[0] == 0) y = y.substring(1);
  var minLength = (element == 'm' || element == 's') ? 2 : 1;
  while(y.split('.')[0].length < minLength) y = '0' + y;
  x.innerHTML = y;
}

function storeValues() {
  var s = hms2s(getScreenValue());
  if(v1 == null || operation == '=') v1 = s;
  else v2 = s;
}

function calc(op, equalsOp) { // equalsOp should be false unless the = key has been pressed.
  if(errorDisplayed) return;
  if(operatorPressedLast) {
    setOperator(op);
    tapeChangeLastOperator(op);
    if(operation == '/' || operation == '*') setSelection('s');
    else setSelection('h');
    return;
  }
  storeValues();
  if(v2 == null) {
    setOperator(op);
    appendToTape(getScreenValue(), op, equalsOp);
  }
  else {
    appendToTape(getScreenValue(), op, equalsOp);
    switch(operation) {
      case '+':
        v1 = v1.add(v2);
        break;
      case '-':
        v1 = v1.subtract(v2);
        break;
      case '*':
        v1 = v1.multiply(v2);
        break;
      case '/':
        if(v2.compareTo(ZERO) == 0) {
          error = 'Div. by 0!';
          break;
        }
        var precision = Math.max(Math.abs(v1.exp), Math.abs(v2.exp)) + 10;
        var form = MathContext.prototype.PLAIN;
        v1 = v1.divide(v2, new MathContext(precision, form));
        break;
    }
    v2 = null;
    setOperator(op);
    displayResult(v1);
  }
  clearOnNext = true;
  if(operation == '/' || operation == '*') setSelection('s');
  else setSelection('h');
  operatorPressedLast = true;
}

function equals() {
  if(errorDisplayed) return;
  operatorPressedLast = false;
  if(!error) {
    calc(operation, true);
    setOperator('=');
  }
  else setOperator('');
  appendToTape(getScreenValue(), '=', false);
  operatorPressedLast = false;
}

function displayResult(s) {
  if(error) setDisplay([false,' Error ',' Error ',' Error ']);
  else setDisplay(s2hms(s));
  setSelection('h');
}

function clearX(v) {
  if(v == 'C' && !(byId('h').innerHTML * 1) && !(byId('m').innerHTML * 1) && !(byId('s').innerHTML * 1)) v = 'AC';
  else if(operation == '=') v = 'AC';
  if(v == 'AC') {
    error = null;
    errorDisplayed = false;
  }
  clearDisplay();
  setSelection('h');
  if(v == 'AC') {
    setOperator('');
    v1 = null;
    v2 = null;
    operatorPressedLast = false;
    //appendToTape([false, 0, 0, 0], 'AC');
  }
  else operatorPressedLast = true;
}

function backspace() {
  if(errorDisplayed) return;
  var x = byId(selection);
  var y = x.innerHTML;
  if(y == 0 && y.indexOf('.') == -1) return;
  var z = y.substring(0,y.length-1);
  if(z.length == 0) z = 0;
  x.innerHTML = z;
  writeInsertedDigit('',selection);
}

function memSet(operation) {
  if(errorDisplayed) return;
  var s = hms2s(getScreenValue());
  if(memory == null) {
    memory = s;
    byId('memory-display').innerHTML = 'M';
  }
  else {
    if(operation == '+') memory += s;
    else memory -= s;
    setDisplay(s2hms(memory));
  }
  setOperator('');
  setSelection('h');
  appendToTape(getScreenValue(), 'M');
}

function memRecall() {
  if(errorDisplayed) return;
  if(memory == null) alert('Nothing in memory!');
  else {
    setDisplay(s2hms(memory));
    setSelection('h');
    operatorPressedLast = false;
  }
}

function memClear() {
  memory = null;
  byId('memory-display').innerHTML = '&nbsp;';
}

