/**
 *  input control extension that provides an input mask
 *
 *  @author   Tobias Hettinger
 *  @version  $Id: mask.js,v 1.1 2009/02/03 15:02:56 tobias Exp $
 *
 *
 *  syntax of an input mask
 *
 *  - basic structure (defines the number of required characters)
 *    "({1st. character}{2nd. character})...{last character}"
 *
 *  - () defines a block. If a required character is entered but before the required position,
 *    the characters are filled (4. parameter from the beginning of the block)
 *
 *  - structure of a character definition
 *    "{possible characters|character that is inserted if no valid character was pressed
 *      or empty if one of the possible characters is required|command that is executed if the
 *      character to insert is not valid: i - ignore (DEFAULT), s - shift (try to insert the character at the
 *      next position|fill character}"
 *
 *  - {possible character,possible character,[span of possible characters (e.g. 6-8)],do not care: [.]|}
 *    (use regular expressions)
 *
 *  - {|character or string that is required (no possible characters defined)}
 */


/**
 *  function to get the mask definition on the defined cursor position
 *
 *  @param int index  index of the character (the index of the first character is 0)
 *
 *  @return array  the function returns an array with information about the mask
 *                 - valid -
 *                   true, if the result is valid
 *                 - char_def -
 *                   complete character definition
 *                 - pos_chars -
 *                   definition of possible characters
 *                 - alt_chars -
 *                   definition of the alternative character
 *                 - command -
 *                   command that is executed if the character to insert is not valid, default: ignore ('i')
 *                 - fill -
 *                   fill character that is inserted if a required character has to be inserted at a fixed position
 *                 - def_start_pos -
 *                   position within the mask string where the character position starts (position of the '{')
 *                 - def_end_pos -
 *                   position within the mask string where the character position ends (position of the '}')
 */
WATFormField.prototype.wat_form_get_mask = function(char_index)
{
  //  initialize the result array
  var result = new Array();
  result['valid'] = false;
  result['char_def'] = '';
  result['pos_chars'] = '';
  result['alt_chars'] = '';
  result['command'] = 'i';
  result['fill'] = '';
  result['def_start_pos'] = null;
  result['def_end_pos'] = null;
  
  //  get the character definition (of the 'start pos' character)
  var index = this.mask.indexOf('{');
  var count = 1;
  //  return if no mask or no valid input mask definition was found
  if (index == -1) return result;

  //  search the character definition
  while(index > -1 && count < (char_index+1))
  {
    index = this.mask.indexOf('{', index+1);
    count++;
  }
  //  return if no character definition for the actual character was found
  if (index == -1) return result;
  //  get the end index of the character definition
  var end_index = this.mask.indexOf('}', index);
  //  get the complete character definition
  result['def_start_pos'] = index;
  result['def_end_pos'] = end_index;
  result['char_def'] = this.mask.substr(index+1, end_index-index-1);
  
  //  split the character definition
  var defs = result['char_def'].split('|');
  if (defs.length > 0) result['pos_chars'] = defs[0];
  if (defs.length > 1) result['alt_chars'] = defs[1];
  if (defs.length > 2) result['command'] = defs[2];
  if (defs.length > 3) result['fill'] = defs[3];
  
  //  return the result now
  result['valid'] = true;
  return result;
}

/**
 *  function to get the index of the first and the last character of a block to which
 *  the specified character belongs to
 *
 *  @param int char_index  index of the character which block is searched
 *
 *  @return array  the function returns a hash array with the keys
 *                 - first_index -
 *                   index of the first character of the block (default: 0)
 *                 - last_index -
 *                   index of the last character of the block (default: last character of the mask or
 *                                                                      -1 in case of an error)
 */
WATFormField.prototype.wat_form_get_block = function(char_index)
{
  //  initialize the result array
  var result = new Array();
  result['first_index'] = 0;
  result['last_index'] = -1;
  
  //  get the mask of the specified character
  var char_def = this.wat_form_get_mask(char_index);
  if (char_def['valid'])
  {
    //  get the block
    var block_end_index = this.mask.indexOf(')', char_def['def_start_pos']);
    if (block_end_index == -1) block_end_index = this.mask.length-1;
    var temp_mask = this.mask.substr(0, block_end_index);
    var block_start_index = temp_mask.lastIndexOf(')');
    if (block_start_index == -1) block_start_index = 0;

    //  search the first character of the block
    var found = false;
    var search_index = char_index-1;
    var search_char = null;
    while (search_index > 0 && !found)
    {
      search_char = this.wat_form_get_mask(search_index);
      if (search_char['def_start_pos'] <= block_start_index)
        found = true;
      else
        search_index--;
    }
    result['first_index'] = search_index;

    //  search the last character of the block
    found = false;
    search_index = char_index+1;
    search_char = this.wat_form_get_mask(search_index);
    while(search_char['valid'] && !found)
    {
      if (search_char['def_end_pos'] >= block_end_index)
        found = true;
      else
        search_index++;
        
      search_char = this.wat_form_get_mask(search_index);
    }
    result['last_index'] = search_index-1;
  }

  //  return the result array
  return result;
}

WATFormField.prototype.wat_form_mask_cursor_pos = function(pos)
{
  if (this.control.setSelectionRange)
  {
    this.control.focus();
    this.control.setSelectionRange(pos, pos);
  }
  else if (this.control.createTextRange)
  {
    var range = this.control.createTextRange();
    range.collapse(true);
    range.moveEnd('character', pos);
    range.moveStart('character', pos);
    range.select();
  }
}

/**
 *  @return result
 *          - success -
 *            if true, the character is valid
 *          - shift -
 *            if true, the character should be shifted to the next position
 *          - required -
 *            true, if a required character was inserted (no possible characters defined)
 *          - no_char_def -
 *            true if there is no character definition for the specified index
 *          - char_index -
 *            (new) character index if it was modified
 
 *   - stop - stop immediately and do not look for the next characters
 */
WATFormField.prototype.wat_form_mask_char = function(char_index, character)
{
  var result = new Array();
  result['success'] = false;
  result['shift'] = false;
  result['required'] = false;
  result['prior_required'] = false;
  result['no_char_def'] = false;
  result['char_index'] = char_index;
  result['stop'] = false;
 
  //  get the character position for the caret position
  var char_def = this.wat_form_get_mask(char_index); 
  var char_to_insert = character;
  
  if (!char_def['valid'])
  {
    result['no_char_def'] = true;
    char_to_insert = '';
  }
  else
  {
    if (character.length > 0)
    {
      //  check, if there is a required string (possible characters is NULL)
      if (char_def['pos_chars'].length > 0)
      {
        //  there is a list of possible characters
        var pos_chars = char_def['pos_chars'].split(',');
        var i, passed = false;
        for (i=0; i<pos_chars.length; i++)
        { 
          var reg = new RegExp(pos_chars[i]);
          if (reg.test(character)) passed = true;
        }

        //  the character did not pass
        if (!passed)
        {
          //  check if the prior character is required and is equal to this prior character
          if (char_index > 0)
          {
            var prior_char_def = this.wat_form_get_mask(char_index-1);
            if (prior_char_def['pos_chars'] == '' && prior_char_def['alt_chars'] == character)
            {
              passed = true;
              char_to_insert = '';
              result['required'] = true;
              result['prior_required'] = true;
            }
          }
          
          //  if the character still don't pass, check if this character is required
          //  on a later position of the actual block
          if (!passed)
          {
            //  analyse the block
            var block = this.wat_form_get_block(char_index);
            var found = false;
            var search_index = char_index+1;
            while (block['last_index'] >= search_index && !found)
            {
              var block_char_def = this.wat_form_get_mask(search_index);
              if (block_char_def['pos_chars'] == '' && block_char_def['alt_chars'] == character)
                found = true; else search_index++;
            }

            if (found)
            {
              //  insert the fill characters
              var fill_count = search_index-char_index;
              var fill_index = block['first_index'];
              while(fill_count && fill_index <= block['last_index'])
              {
                var fill_char_def = this.wat_form_get_mask(fill_index);
                if (fill_char_def['fill'].length > 0)
                {
                  var new_value = '';
                  if (fill_index > 0) new_value = this.control.value.substr(0, fill_index);
                  new_value = new_value + fill_char_def['fill'];
                  if (this.control.value.length > fill_index+1) new_value = new_value + this.control.value.substr(fill_index);
                  this.control.value = new_value;
                  char_index++;
                }
                fill_index++;
              }
              
              //  the inserted character passed
              passed=true; 
              result['char_index'] = char_index;             
            }          
          }
        }
          
        if (!passed)
        {
          //  the character is not valid, get the command
          if (char_def['command'] == 's')
          {
            result['shift'] = true;
          }
      
          char_to_insert = char_def['alt_chars'];
        }
        else result['success'] = true;
      }
      else
      {
        //  no possible characters, insert the alternative string
        if (char_def['alt_chars'].length > 0)
        {
          char_to_insert = char_def['alt_chars'];
          result['required'] = true;
          if (char_def['command'] == 's')
          {
            result['shift'] = true;
          }
        }
      }
    }
    else
    {
      //  no character defined, only insert a required character without shifting
      if (char_def['pos_chars'] == '' && char_def['alt_chars'].length > 0)
      {
        char_to_insert = char_def['alt_chars'];
        result['required'] = true;
      }
    }
  } 
  
  //  insert the character
  var new_value = '';
  var new_char_pos = -1;
  if (char_index) new_value = this.control.value.substr(0, char_index);

  new_value = new_value + char_to_insert;
  if (this.control.value.length > char_index+2)
  {
    new_value = new_value + this.control.value.substr(char_index+2);
    new_char_pos = char_index+1;
  }
  this.control.value = new_value;
  
  if (new_char_pos > -1)
  {
    this.wat_form_mask_cursor_pos(new_char_pos);
    result['stop'] = true;
  }
  
  //  return the result
  return result;
}


/**
 *  function that is called when a user pressed a key
 */
WATFormField.prototype.wat_form_mask_keypress = function(event)
{
  var character_code = (event.which ? event.which : event.keyCode);
  if (character_code > 40)
  {
    this.maskNextChar = String.fromCharCode(character_code);
  }
  else this.maskNextChar = '';
}


WATFormField.prototype.wat_form_mask_keyup = function(event)
{
  if (this.maskNextChar.length > 0)
  {
    //  get the cursor position
    var caret_pos = watFormGetCaret(this.control); 
    var pos = caret_pos['start']-1;
    var finish = false;
    while(!finish)
    {
      var result = this.wat_form_mask_char(pos, this.maskNextChar);
      pos = result['char_index'];
      if (result['stop']) finish = true;
      if (!result['prior_required']) pos = pos + 1;
      if (result['no_char_def'] || (!result['success'] && !result['shift']) || (this.maskNextChar == '' && !result['required'])) finish = true;
      if (!result['shift']) this.maskNextChar = '';
    }
  }
}

