#include "tex.hh"
#include "bad_value.hh"
#include "skip-t.hh"
#include "clone_ptr-t.hh"
#include "ctype.h"

#define top stack.back()

namespace afilter {

  template <typename T>
  bool TeXSkip<T>::Commands::add(const char * value) {
    int p1 = 0;
    while (!isspace(value[p1])) {
      if (value[p1] == '\0') 
	throw BadValue(value,"","a string of o,O,p, or P");
      ++p1;
    }
    int p2 = p1 + 1;
    while (isspace(value[p2])) {
      if (value[p2] == '\0') 
	throw BadValue(value,"","a string of o,O,p, or P");
      ++p2;
    }
    string t1(value,p1);
    string t2(value+p2);
    return StringMap::replace(t1.c_str(), t2.c_str());
  }

  template <typename T>
  bool TeXSkip<T>::Commands::remove(const char * value) {
    int p1 = 0;
    while (!isspace(value[p1]) && value[p1] != '\0') ++p1;
    string temp(value,p1);
    return StringMap::remove(temp.c_str());
  }

  template <typename T>
  inline void TeXSkip<T>::push_command(InWhat w) {
    stack.push_back(Command(w));
  }

  template <typename T>
  inline void TeXSkip<T>::pop_command() {
    stack.pop_back();
    if (stack.empty())
      push_command(Parm);
  }

  template <typename T>
  TeXSkip<T>::TeXSkip(ConfigData & opts) 
    : in_comment(false), prev_backslash(false)
  {
    opts.retrieve_list("tex-command", parms->commands);
    parms->check_comments = opts.retrieve_bool("tex-check-comments");
    push_command(Parm);
  }

  template <typename T>
  bool TeXSkip<T>::skip(char c, const Itr *) {
    // deal with comments
    if (c == '%' && !prev_backslash) in_comment = true;
    if (in_comment && c == '\n')     in_comment = false;
    if (in_comment)                  return !parms->check_comments;

    if (top.in_what == Name) {
      if (isalpha(c)) {

	top.name += c;
	return true;

      } else {

	if (top.name.empty() && (c == '@')) {
	  top.name += c;
	  return true;
	}
	  
	top.in_what = Other;

	if (top.name.empty()) {
	  top.name = c;
	  top.do_check = parms->commands.lookup(top.name.c_str());
	  if (top.do_check == 0) top.do_check = "";
	  return !isspace(c);
	}

	top.do_check = parms->commands.lookup(top.name.c_str());
	if (top.do_check == 0) top.do_check = "";

	if (isspace(c)) { // swallow extra spaces
	  top.in_what = Swallow;
	  return true;
	} else if (c == '*') { // ignore * at end of commands
	  return true;
	}
	
	// continue o...
      }

    } else if (top.in_what == Swallow) {

      if (isspace(c))
	return true;
      else
	top.in_what = Other;
    }

    if (c == '{')
      while (*top.do_check == 'O' || *top.do_check == 'o') 
	++top.do_check;

    if (*top.do_check == '\0')
      pop_command();

    if (c == '{') {

      if (top.in_what == Parm || top.in_what == Opt || top.do_check == '\0')
	push_command(Parm);

      top.in_what = Parm;
      return true;
    }

    if (top.in_what == Other) {

      if (c == '[') {

	top.in_what = Opt;
	return true;

      } else if (isspace(c)) {

	return true;

      } else {
	
	pop_command();

      }

    } 

    if (c == '\\') {
      push_command(Name);
      return true;
    }

    if (top.in_what == Parm) {

      if (c == '}')
	return end_option('P','p');
      else
	return *top.do_check == 'p';

    } else if (top.in_what == Opt) {

      if (c == ']')
	return end_option('O', 'o');
      else
	return *top.do_check == 'o';

    }

    return false;
  }

  template <typename T>
  bool TeXSkip<T>::end_option(char u, char l) {
    top.in_what = Other;
    if (*top.do_check == u || *top.do_check == l)
      ++top.do_check;
    return true;
  }

}

#undef top
