/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <vlib/parse.h>
#include <vlib/unix/unix.h>

static u8 * format_value_v4_address (u8 * s, va_list * args)
{
  vlib_parse_value_t *v = va_arg (*args, vlib_parse_value_t *);
  u32 a = v->value.as_uword;

  s = format (s, "%d.%d.%d.%d",
	      (a>>24) & 0xFF,
	      (a>>16) & 0xFF,
	      (a>>8)  & 0xFF,
	      (a>>0)  & 0xFF);

  return s;
}

static vlib_parse_match_t
v4_address_match (vlib_parse_main_t *pm, vlib_parse_type_t *type, 
		  vlib_lex_token_t *t, vlib_parse_value_t *valuep)
{
  u32 digit;
  u32 value = 0;
  int i;

  if (vec_len (pm->tokens) - (t - pm->tokens) < 7)
    return VLIB_PARSE_MATCH_FAIL;

  /* NUMBER DOT NUMBER DOT NUMBER DOT NUMBER */

  for (i = 0; i < 7; i++) {
    if ((i & 1) == 0) {
      if (t[i].token != VLIB_LEX_number)
	return VLIB_PARSE_MATCH_FAIL;
      if (t[i].value.as_uword > 0xff)
	return VLIB_PARSE_MATCH_FAIL;
      digit = t[i].value.as_uword;
      value = (value << 8) | digit;
    } else {
      if (t[i].token != VLIB_LEX_dot)
	return VLIB_PARSE_MATCH_FAIL;
    }
  }
  /* note: caller advances by 1 */
  pm->current_token_index += 6;
  valuep->value.as_uword = value;
  return VLIB_PARSE_MATCH_VALUE;
}

PARSE_TYPE_INIT (v4_address, v4_address_match, 0, format_value_v4_address)

static u8 * format_value_v4_address_and_mask (u8 * s, va_list * args)
{
  vlib_parse_value_t *v = va_arg (*args, vlib_parse_value_t *);
  u32 * a = v->value.as_pointer;

  s = format (s, "%d.%d.%d.%d",
	      (a[0]>>24) & 0xFF,
	      (a[0]>>16) & 0xFF,
	      (a[0]>>8)  & 0xFF,
	      (a[0]>>0)  & 0xFF);
  s = format (s, "/%d", a[1]);

  return s;
}

static vlib_parse_match_t
v4_address_and_mask_match (vlib_parse_main_t *pm, vlib_parse_type_t *type, 
			   vlib_lex_token_t *t, vlib_parse_value_t *valuep)
{
  u32 digit;
  u32 address = 0;
  u32 *rv = 0;
  int i;

  if (vec_len (pm->tokens) - (t - pm->tokens) < 9)
    return VLIB_PARSE_MATCH_FAIL;

  /* NUMBER DOT NUMBER DOT NUMBER DOT NUMBER */

  for (i = 0; i < 7; i++) {
    if ((i & 1) == 0) {
      if (t[i].token != VLIB_LEX_number)
	return VLIB_PARSE_MATCH_FAIL;
      if (t[i].value.as_uword > 0xff)
	return VLIB_PARSE_MATCH_FAIL;
      digit = t[i].value.as_uword;
      address = (address << 8) | digit;
    } else {
      if (t[i].token != VLIB_LEX_dot)
	return VLIB_PARSE_MATCH_FAIL;
    }
  }

  if (t[7].token != VLIB_LEX_slash || t[8].token != VLIB_LEX_number)
    return VLIB_PARSE_MATCH_FAIL;

  vec_add1 (rv, address);
  vec_add1 (rv, t[8].value.as_uword);

  /* note: caller advances by 1 */
  pm->current_token_index += 8;
  valuep->value.as_pointer = rv;
  return VLIB_PARSE_MATCH_VALUE;
}

void v4_address_and_mask_cleanup (vlib_parse_value_t *valuep)
{
  u32 * trash = valuep->value.as_pointer;
  vec_free (trash);
}

PARSE_TYPE_INIT (v4_address_and_mask, v4_address_and_mask_match, 
                 v4_address_and_mask_cleanup, 
                 format_value_v4_address_and_mask)

vlib_lex_main_t vlib_lex_main;



vlib_parse_match_t eval_factor0 (vlib_parse_main_t *pm, 
                                 vlib_parse_item_t *item,
                                 vlib_parse_value_t *value)
{
  clib_warning ("%U", format_vlib_parse_value, pm);
  return VLIB_PARSE_MATCH_RULE;
}
vlib_parse_match_t eval_factor1 (vlib_parse_main_t *pm, 
                                 vlib_parse_item_t *item,
                                 vlib_parse_value_t *value)
{
  clib_warning ("%U", format_vlib_parse_value, pm);
  return VLIB_PARSE_MATCH_RULE;
}
vlib_parse_match_t eval_factor2 (vlib_parse_main_t *pm, 
                                 vlib_parse_item_t *item,
                                 vlib_parse_value_t *value)
{
  word a;
  int index = vec_len (pm->parse_value)-1;
  
  a = pm->parse_value [index].value.as_word;
  
  pm->parse_value[index].value.as_word = -a;
  return VLIB_PARSE_MATCH_RULE;
}
vlib_parse_match_t eval_term0 (vlib_parse_main_t *pm, 
                               vlib_parse_item_t *item,
                               vlib_parse_value_t *value)
{
  clib_warning ("%U", format_vlib_parse_value, pm);
  return VLIB_PARSE_MATCH_RULE;
}
vlib_parse_match_t eval_term1 (vlib_parse_main_t *pm, 
                               vlib_parse_item_t *item,
                               vlib_parse_value_t *value)
{
  uword a, b;
  int index = vec_len (pm->parse_value)-2;
  
  a = pm->parse_value [index].value.as_uword;
  b = pm->parse_value [index+1].value.as_uword;
  
  pm->parse_value[index].value.as_uword = a * b;
  _vec_len (pm->parse_value) -= 1;
  clib_warning ("%U", format_vlib_parse_value, pm);
  
  return VLIB_PARSE_MATCH_RULE;
}
vlib_parse_match_t eval_term2 (vlib_parse_main_t *pm, 
                               vlib_parse_item_t *item,
                               vlib_parse_value_t *value)
{
  uword a, b;
  int index = vec_len (pm->parse_value)-2;
  
  a = pm->parse_value [index].value.as_uword;
  b = pm->parse_value [index+1].value.as_uword;
  
  pm->parse_value[index].value.as_uword = a / b;
  _vec_len (pm->parse_value) -= 1;
  clib_warning ("%U", format_vlib_parse_value, pm);
  
  return VLIB_PARSE_MATCH_RULE;
}
vlib_parse_match_t eval_exp0 (vlib_parse_main_t *pm, 
                              vlib_parse_item_t *item,
                              vlib_parse_value_t *value)
{
  return VLIB_PARSE_MATCH_RULE;
}
vlib_parse_match_t eval_exp1 (vlib_parse_main_t *pm, 
                              vlib_parse_item_t *item,
                              vlib_parse_value_t *value)
{
  uword a, b;
  int index = vec_len (pm->parse_value)-2;
  
  a = pm->parse_value [index].value.as_uword;
  b = pm->parse_value [index+1].value.as_uword;
  
  pm->parse_value[index].value.as_uword = a + b;
  _vec_len (pm->parse_value) -= 1;
  clib_warning ("%U", format_vlib_parse_value, pm);
  
  return VLIB_PARSE_MATCH_RULE;
}
vlib_parse_match_t eval_exp2 (vlib_parse_main_t *pm, 
                              vlib_parse_item_t *item,
                              vlib_parse_value_t *value)
{
  uword a, b;
  int index = vec_len (pm->parse_value)-2;
  
  a = pm->parse_value [index].value.as_uword;
  b = pm->parse_value [index+1].value.as_uword;
  
  pm->parse_value[index].value.as_uword = a - b;
  _vec_len (pm->parse_value) -= 1;
  clib_warning ("%U", format_vlib_parse_value, pm);
  
  return VLIB_PARSE_MATCH_RULE;
}

vlib_parse_match_t eval_result (vlib_parse_main_t *pm, 
                                vlib_parse_item_t *item,
                                vlib_parse_value_t *value)
{
  clib_warning ("%U", format_vlib_parse_value, pm);
  return VLIB_PARSE_MATCH_DONE;
}

vlib_parse_match_t noop_match_rule (vlib_parse_main_t *pm, 
                               vlib_parse_item_t *item,
                               vlib_parse_value_t *value)
{
  clib_warning ("%U", format_vlib_parse_value, pm);
  return VLIB_PARSE_MATCH_RULE;
}

#if 0
PARSE_INIT (t1, "moo", eval0);
PARSE_INIT (t2, "moo cow mumble", eval1);
PARSE_INIT (t3, "moo cow", eval2);
PARSE_INIT (t4, "moo cow mumble grunch", eval3);
#endif

#if 0
PARSE_INIT (r1, "eval <exp>", eval_result);

PARSE_INIT (r2, "<exp> = <term><exp2>", eval_exp0);
PARSE_INIT (r3, "<exp2> = <plus> <exp>", eval_exp1);
PARSE_INIT (r4, "<exp2> = <minus> <exp>", eval_exp2);
PARSE_INIT (r5, "<exp2> = ", noop_match_rule);
PARSE_TYPE_INIT (exp, rule_match, 0, 0);
PARSE_TYPE_INIT (exp2, rule_match, 0, 0);

PARSE_INIT (r6, "<term> = <factor><term2>", eval_term0);
PARSE_INIT (r7, "<term2> = <star> <term>", eval_term1);
PARSE_INIT (r8, "<term2> = <slash> <term>", eval_term2);
PARSE_INIT (r9, "<term2> = ", noop_match_rule);
PARSE_TYPE_INIT (term, rule_match, 0, 0);
PARSE_TYPE_INIT (term2, rule_match, 0, 0);

PARSE_INIT (r11, "<factor> = <lpar> <exp> <rpar>", eval_factor1);
PARSE_INIT (r10, "<factor> = <number>", eval_factor0);
PARSE_INIT (r12, "<factor> = <minus> <factor>", eval_factor2);

PARSE_TYPE_INIT (factor, rule_match, 0, 0);
#endif

PARSE_INIT (r1, "eval <exp>", eval_result);

#if 1
PARSE_INIT (r2, "<exp> = <term><exp2>", eval_exp0);
PARSE_INIT (r3, "<exp2> = <plus> <exp>", eval_exp1);
PARSE_INIT (r4, "<exp2> = <minus> <exp>", eval_exp2);
PARSE_INIT (r5, "<exp2> = ", noop_match_rule);
PARSE_TYPE_INIT (exp, rule_match, 0, 0);
PARSE_TYPE_INIT (exp2, rule_match, 0, 0);

PARSE_INIT (r6, "<term> = <factor><term2>", eval_term0);
PARSE_INIT (r7, "<term2> = <star> <term>", eval_term1);
PARSE_INIT (r8, "<term2> = <slash> <term>", eval_term2);
PARSE_INIT (r9, "<term2> = ", noop_match_rule);
PARSE_TYPE_INIT (term, rule_match, 0, 0);
PARSE_TYPE_INIT (term2, rule_match, 0, 0);

PARSE_INIT (r11, "<factor> = <lpar> <exp> <rpar>", eval_factor1);
PARSE_INIT (r10, "<factor> = <number>", eval_factor0);
PARSE_INIT (r12, "<factor> = <minus> <factor>", eval_factor2);

PARSE_TYPE_INIT (factor, rule_match, 0, 0);
#endif

#if 0
PARSE_TYPE_INIT (exp, rule_match, 0, 0);
PARSE_INIT (r6, "<exp> = a b", eval_term0);
PARSE_INIT (r7, "<exp> = c d", eval_term1);
PARSE_INIT (r9, "<exp> = ", noop_match_rule);
#endif

#if 0
#define foreach_rule_evaluator                  \
_(0)                                            \
_(1)                                            \
_(2)						\
_(3)

#define _(n)                                            \
vlib_parse_match_t eval##n (vlib_parse_main_t *pm,      \
                            vlib_parse_item_t *item,    \
                            vlib_parse_value_t *value)  \
{                                                       \
  clib_warning ("%U", format_vlib_parse_value, pm);     \
  return VLIB_PARSE_MATCH_DONE;                         \
}
foreach_rule_evaluator

#undef _

PARSE_INIT (r1, "eval <moo>", eval_result);

PARSE_INIT (r2, "<moo> = cow", eval0);
PARSE_INIT (r4, "<moo> = ", eval1);
PARSE_TYPE_INIT (moo, rule_match, 0, 0);
#endif 


clib_error_t *test_init (vlib_main_t *vm)
{
  clib_error_t *error;

  if ((error = vlib_call_init_function (vm, parse_init)))
    return error;

  return 0;
}

VLIB_INIT_FUNCTION (test_init);

clib_error_t *
vlib_stdlex_init (vlib_main_t * vm)
{
  vlib_lex_main_t * lm = &vlib_lex_main;
  u16 top_index;
  u16 slash_index, slash_star_index, slash_slash_index, slash_star_star_index;
  u16 slash_token;
  u16 word_index;
  u16 zero_index, octal_index, decimal_index, hex_index, binary_index;
    
  top_index = vlib_lex_add_table ("top");

#define foreach_top_level_single_character_token        \
  _('(', lpar)                                          \
  _(')', rpar)                                          \
  _(';', semi)                                          \
  _('[', lbrack)                                        \
  _(']', rbrack)                                        \
  _('{', lcurly)                                        \
  _('}', rcurly)                                        \
  _('+', plus)                                          \
  _('-', minus)                                         \
  _('*', star)                                          \
  _('%', percent)                                       \
  _('@', atsign)                                        \
  _(',', comma)                                         \
  _('.', dot)                                           \
  _('?', qmark)

#define _(c,t) \
  vlib_lex_set_action_range(top_index,c,c,VLIB_LEX_RETURN,vlib_lex_add_token(lm, #t), top_index);
  foreach_top_level_single_character_token;
#undef _

  /* Numbers */
  zero_index = vlib_lex_add_table ("zero");
  octal_index = vlib_lex_add_table ("octal");
  decimal_index = vlib_lex_add_table ("decimal");
  hex_index = vlib_lex_add_table ("hex");
  binary_index = vlib_lex_add_table ("binary");

  /* Support 0x 0b 0t and 0123 [octal] */
  vlib_lex_set_action_range (top_index, '0', '0', VLIB_LEX_START_NUMBER, 10, zero_index);
  vlib_lex_set_action_range (top_index, '1', '9', VLIB_LEX_START_NUMBER, 10, decimal_index);

  vlib_lex_set_action_range (zero_index, 0, 0x7F, VLIB_LEX_RETURN_AND_RESCAN, VLIB_LEX_number, top_index);

  vlib_lex_set_action_range (zero_index, 'x', 'x', VLIB_LEX_IGNORE, ~0, hex_index); 
  vlib_lex_set_action_range (zero_index, 'b', 'b', VLIB_LEX_IGNORE, ~0, binary_index); 
  vlib_lex_set_action_range (zero_index, 't', 't', VLIB_LEX_IGNORE, ~0, decimal_index); 
  vlib_lex_set_action_range (zero_index, '0', '7', VLIB_LEX_START_NUMBER, 8, octal_index);
    
  /* Octal */
  vlib_lex_set_action_range (octal_index, 0, 0x7f, VLIB_LEX_RETURN_AND_RESCAN,
			VLIB_LEX_number, top_index);
  vlib_lex_set_action_range (octal_index, '0', '7', VLIB_LEX_ADD_TO_NUMBER, 8, 
			octal_index);

  /* Decimal */
  vlib_lex_set_action_range (decimal_index, 0, 0x7f, VLIB_LEX_RETURN_AND_RESCAN,
			VLIB_LEX_number, top_index);
  vlib_lex_set_action_range (decimal_index, '0', '9', VLIB_LEX_ADD_TO_NUMBER, 10, 
			decimal_index);
    
  /* Hex */
  vlib_lex_set_action_range (hex_index, 0, 0x7f, VLIB_LEX_RETURN_AND_RESCAN,
			VLIB_LEX_number, top_index);
  vlib_lex_set_action_range (hex_index, '0', '9', VLIB_LEX_ADD_TO_NUMBER, 16, 
			hex_index);
  vlib_lex_set_action_range (hex_index, 'a', 'f', VLIB_LEX_ADD_TO_NUMBER, 16, 
			hex_index);
  vlib_lex_set_action_range (hex_index, 'A', 'F', VLIB_LEX_ADD_TO_NUMBER, 16, 
			hex_index);
    
  /* Binary */
  vlib_lex_set_action_range (binary_index, 0, 0x7f, VLIB_LEX_RETURN_AND_RESCAN,
			VLIB_LEX_number, top_index);
  vlib_lex_set_action_range (binary_index, '0', '1', VLIB_LEX_ADD_TO_NUMBER, 2, 
			binary_index);

  /* c/c++ comment syntax is the worst... */

  slash_index = vlib_lex_add_table ("slash");
  slash_star_index = vlib_lex_add_table ("slash_star");
  slash_star_star_index = vlib_lex_add_table ("slash_star_star");
  slash_slash_index = vlib_lex_add_table ("slash_slash");
  slash_token = vlib_lex_add_token (lm, "slash");

  /* Top level: see a slash, ignore, go to slash table */
  vlib_lex_set_action_range (top_index, '/', '/', VLIB_LEX_IGNORE, ~0, slash_index);

  /* default for slash table: return SLASH, go to top table */
  vlib_lex_set_action_range (slash_index, 1, 0x7F, VLIB_LEX_RETURN_AND_RESCAN, slash_token, 
			top_index);
  /* see slash-slash, go to s-s table */
  vlib_lex_set_action_range (slash_index, '/', '/', VLIB_LEX_IGNORE, ~0, 
			slash_slash_index);
  /* see slash-star, go to s-* table */
  vlib_lex_set_action_range (slash_index, '*', '*', VLIB_LEX_IGNORE, ~0, 
			slash_star_index);

  /* EOL in s-s table, ignore, go to top table */
  vlib_lex_set_action_range (slash_slash_index, '\n', '\n', VLIB_LEX_IGNORE, ~0, 
			top_index);

  /* slash-star blah blah star */
  vlib_lex_set_action_range (slash_star_index, '*', '*', VLIB_LEX_IGNORE, ~0,
			slash_star_star_index);

  /* slash star blah blah star slash */
  vlib_lex_set_action_range (slash_star_star_index, '/', '/', VLIB_LEX_IGNORE, ~0,
			top_index);

  /* LT, =, GT */
  vlib_lex_set_action_range (top_index, '<', '<', VLIB_LEX_RETURN, VLIB_LEX_lt, top_index);
  vlib_lex_set_action_range (top_index, '=', '=', VLIB_LEX_RETURN, VLIB_LEX_equals,
			top_index);
  vlib_lex_set_action_range (top_index, '>', '>', VLIB_LEX_RETURN, VLIB_LEX_gt, top_index);

  /* words, key and otherwise */
  word_index = vlib_lex_add_table ("word");

  vlib_lex_set_action_range (top_index, 'a', 'z', VLIB_LEX_ADD_TO_TOKEN, ~0, word_index);
  vlib_lex_set_action_range (top_index, 'A', 'Z', VLIB_LEX_ADD_TO_TOKEN, ~0, word_index);

  vlib_lex_set_action_range (word_index, 0, 0x7f, VLIB_LEX_KEYWORD_CHECK, ~0, top_index);

  vlib_lex_set_action_range (word_index, 'a', 'z', VLIB_LEX_ADD_TO_TOKEN, ~0, word_index);
  vlib_lex_set_action_range (word_index, 'A', 'Z', VLIB_LEX_ADD_TO_TOKEN, ~0, word_index);
  vlib_lex_set_action_range (word_index, '_', '_', VLIB_LEX_ADD_TO_TOKEN, ~0, word_index);
  vlib_lex_set_action_range (word_index, '0', '9', VLIB_LEX_ADD_TO_TOKEN, ~0, word_index);
    
  return 0;
}