/*
 * @author fenrir (http://fenrir.naruoka.org/)
 * 
 * reg_string.h
 * 一文字マッチなど
 */

#include "reg_core.h"
#include "reg_string.h"
#include "reg_char.h"

#ifdef __cplusplus
extern "C"{
#endif

/**
 * エスケープを考慮して一文字送ります。
 * 
 * @param regexp 正規表現文字列
 * @return 結果
 */
static char *shift(char *regexp){
  return reg_char_shift(((*regexp == ESCAPE_CHARACTER) ? regexp + 1 : regexp), 1);
}

/**
 * 空白文字かどうかを判定します。
 * 
 * @param target 文字
 * @return 空白文字の場合TRUE(0以外)、それ以外FALSE(0)
 */ 
static boolean is_white(const char *target){
  switch(*target){
    case '\n':
    case '\t':
    case '\r':
    case '\f':
    case ' ':
      return TRUE;
    default:
      return FALSE;
  }
}

/**
 * 正規表現エスケープ含めて1文字比較を行います。
 * 
 * @param regexp 正規表現文字
 * @param target 比較対象文字
 * @return マッチした場合MATCH(0)、それ以外MATCH(0)以外
 */
static int compare_char(char *regexp, char *target){
  if(*regexp == ANY_CHARACTER){
    return MATCH;
  }else if(*regexp == ESCAPE_CHARACTER){
    switch(*++regexp){
      case SPACE:
      case NOT_SPACE:
        return *regexp - (is_white(target) ? SPACE : NOT_SPACE);
      case DIGIT:
      case NOT_DIGIT:
        return *regexp - ((*target - '0') * (*target - '9') > 0 ? NOT_DIGIT : DIGIT);
      case HEX:
      case NOT_HEX:
        return *regexp - (reg_compare("[0-9A-Fa-f]", target) == MATCH ? HEX : NOT_HEX);
      case NORMAL_CHAR:
      case NOT_NORMAL_CHAR:
        return *regexp - (reg_compare("[0-9A-Za-z_]", target) == MATCH ? NORMAL_CHAR : NOT_NORMAL_CHAR);
    }
  }
  return reg_char_compare(regexp, target);
}

/**
 * 特定の文字以内に入っているか調べます。
 * 
 * @param low 辞書順で先にくる文字
 * @param high 辞書順で後にくる文字
 * @param target 比較対象文字
 * @return 入っている場合TRUE(0以外)、入っていない場合FALSE(0)
 */
static boolean compare_in_range(char *low, char *high, char *target){
  return reg_char_compare(low, target) * reg_char_compare(high, target) <= 0;
}

/**
 * トークン単位でマッチします。
 * 
 * @param regexp トークン
 * @param target 対象
 * @return マッチした場合MATCH(0)、しなかった場合MATCH(0)以外
 */
int reg_compare(char *regexp, char *target){
  
  if(*target == NULL_CHAR){return !MATCH;}
  if(*regexp == CHAR_CLASS_START){
    
    unsigned char match = !MATCH;
    if(*++regexp == CHAR_CLASS_INVERT){
      regexp++;
      match = !match;
    }
    
    do{
      if(*reg_char_shift(regexp, 1) == CHAR_CLASS_RANGE){
        char *low = regexp;
        char *high = reg_char_shift(regexp, 2);
        if(compare_in_range(low, high, target)){return !match;}
        regexp = high;
      }else{
        if(compare_char(regexp, target) == MATCH){return !match;}
      }
    }while(*(regexp = shift(regexp)) != CHAR_CLASS_END);
    return match;
  }else{return compare_char(regexp, target);}
}

/**
 * バックリファレンスのインデックスを返します。
 * 
 * @param *target 対象文字列
 * @return バックリファレンスを表す場合そのインデックス、それ以外-1
 */
int reg_backref_index(char *target){
  if(*target++ == ESCAPE_CHARACTER && ((*target - '0') * (*target - '9') <= 0)){
    return *target - '0';
  }
  return -1;
}

#ifdef __cplusplus
};
#endif
