/**
 * Handle input in Text field to fit in the given Mask
 * @todo add support for mask iterations like #{9}.99
 *       for advanced masks like signed/unsigned int,
 *       floats with precisions, etc.
 */
TMaskEdit = function(input, mask) {
  this.autocomplete = true;              // Allow auto complete with mask characters
  this.input = TDOM.getElement(input);   // Input edit to apply the mask
  this.mask = mask.toLowerCase();        // Mask
  this.value = this.input.value;         // Holds the last changed input value

  // Setup maskedit
  if (this.input && mask.length != 0) {
    TEvents.listen(this.input, 'keyup', this.onKeyUp, this);
    TEvents.listen(this.input, 'keypress', this.onKeyPress, this);
    this.fixInput({start: 0,end: 0});
  }
}
TMaskEdit.inherits(TEventDispatcher);

/**
 * Mozilla sends this keys on KeyPress and they must be allowed
 * All other control keys (except in IE) are discarded
 */
TMaskEdit.allow = {
  k8:  8,   // BACKSPACE
  k9:  9,   // TAB
  k13: 13,  // ENTER
  k27: 27,   // ESC
  // Some of this keys also map to valid shifter and unshifted character keys
  k33: 33,  // PUP
  k34: 34,  // PDOWN
  k35: 35,  // END
  k36: 36,  // HOME
  k37: 37,  // <-
  k38: 38,  // UP
  k39: 39,  // ->
  k40: 40,  // DOWN
  //k45: 45,  // INS
  k46: 46   // DEL
}

/**
 * Check if the given character is a valid mask character
 * # -> Signed numeric
 * 9 -> Numeric
 * a -> Alphanumeric
 * l -> Literal Character
 * c -> Any character
 *
 * @param {Char} chr: mask character
 */
TMaskEdit.prototype.isMask = function(chr) {
  return chr == '#' || chr == '9' || chr == 'a' || chr == 'l' || chr == 'c';
}

/**
 * Transform a mask, ie:
 * 99/99/9999 -> __/__/____
 * $#9999.99  -> $_____.__
 *
 * @param {String} mask
 */
TMaskEdit.prototype.getPlainMask = function(mask) {
  return mask.replace(/[#9alc]/g,"_");
}

/**
 * Check if the given key code is valid
 * for the given mask character
 *
 * @param {Char} mask: mask character
 * @param {Integer} key: ASCII key code
 */
TMaskEdit.prototype.validKey = function(mask,key) {
  switch (mask) {
    case '9': return key >= 48 && key <= 57;
    case '#': return key == 45 || (key >= 48 && key <= 57);
    case 'a': return (/[a-z0-9]/i).test(String.fromCharCode(key));
    case 'l': return (/[a-z]/i).test(String.fromCharCode(key));
    case 'c': return true;
  }
  return false;
}

/**
 * Fired when backspace or delete pressed
 * and in onChange event to fix malformed
 * input text. Also fix pasted text onExit
 *
 * @param {Object} sel: selection object
 */
TMaskEdit.prototype.fixInput = function(sel) {
  var val = '', pos = 0, result = '';
  var mask = this.mask.split('');
  var plain = this.getPlainMask(this.mask).split('');
  var values = this.input.value.split('');
  while (pos < mask.length && values.length > 0) {
    if (this.isMask(mask[pos])) {
      val = values.shift();
      //result += this.validKey(mask[pos], val.charCodeAt(0)) ? val : plain[pos];
      if (this.validKey(mask[pos], val.charCodeAt(0))) {
        result += val;
      } else {
        while (values.length > 0) {
          val = values.shift();
          if (this.validKey(mask[pos], val.charCodeAt(0))) {
            result += val;
            break;
          }
        }
      }
    } else {
      result += plain[pos];
    }
    pos++;
  }
  if (this.autocomplete) {
    plain = plain.join('');
    result += plain.substr(result.length, plain.length);
  }
  if (this.input.value != result) {
    this.input.value = result;
    TSelection.set(this.input, sel.start, 0);
  }
}

/**
 * onKeyPress Event handler, triggered before onKeyUp event
 * Apply mask to the pressed key
 *
 * @param {Event} e: event object
 */
TMaskEdit.prototype.onKeyPress = function(e) {
  var key = e.charCode;
  if ("k"+key in TMaskEdit.allow) return;                         // key is allowed?
  var chr = String.fromCharCode(key).toLowerCase();
  if (e.ctrlKey && (chr=='c' || chr=='x' || chr=='v')) return;    // Cut, copy, paste
  var sel = TSelection.get(this.input);                           // Get Selection
  // Start Masking
  var offset = sel.start;
  var mask = this.mask.split('');
  if (this.isMask(mask[offset])) {
    if (this.validKey(mask[offset], key)) {
      TSelection.set(this.input, offset, 1);
    } else {
      e.stopPropagation();
      e.preventDefault();
    }
  } else {
    var result = this.input.value.substr(0, offset);
    var remain = this.input.value.substr(offset, this.input.value.length);
    while (offset < mask.length && !this.isMask(mask[offset])) {
      result += mask[offset];
      remain = remain.substr(1, remain.length);
      offset++;
    }
    this.input.value = result + remain;
    if (this.validKey(mask[offset], key)) {
      TSelection.set(this.input, offset, 1);
    } else {
      TSelection.set(this.input, offset, 0);
      e.stopPropagation();
      e.preventDefault();
    }
  }
}

/**
 * onKeyUp Event Handler
 * Fix input text because some allowed
 * control keys are also mapped as regular
 * character key.
 * Also fix pasted text
 *
 * @param {Event} e: event object
 */
TMaskEdit.prototype.onKeyUp = function(e) {
  this.fixInput(TSelection.get(this.input));
}

