/* Copyright (C) 2003 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#ifndef CPCD_PARSER_HPP
#define CPCD_PARSER_HPP

#include "Vector.hpp"
#include "Properties.hpp"
#include "InputStream.hpp"
#include "NdbOut.hpp"

class ParserImpl;
template<class T> class ParserRow;

//#define PARSER_DEBUG
#ifdef PARSER_DEBUG
#define DEBUG(x) \
  ndbout_c("%s:%d:%s", __FILE__, __LINE__, x);
#else 
#define DEBUG(x)
#endif

/**
 * A generic parser
 */
template<class T>
class Parser {
public:
  /**
   * Status for parser
   */
  enum ParserStatus {
    Ok                     = 0,
    Eof                    = 1,
    NoLine                 = 2,
    EmptyLine              = 3,
    UnknownCommand         = 4,
    UnknownArgument        = 5,
    TypeMismatch           = 6,
    InvalidArgumentFormat  = 7,
    UnknownArgumentType    = 8,
    CommandWithoutFunction = 9,
    ArgumentGivenTwice     = 10,
    ExternalStop           = 11,
    MissingMandatoryArgument = 12
  };

  /**
   * Context for parse
   */
  struct Context {
    ParserStatus m_status;
    const ParserRow<T> * m_currentCmd;
    const ParserRow<T> * m_currentArg;
    char * m_currentToken;
    char m_tokenBuffer[512];

    Vector<const ParserRow<T> *> m_aliasUsed;
  };
  
  /**
   * Initialize parser
   */
  Parser(const ParserRow<T> rows[], class InputStream & in = Stdin,
	 bool breakOnCommand = false, 
	 bool breakOnEmptyLine = true, 
	 bool breakOnInvalidArg = false);
  ~Parser();
  
  /**
   * Run parser
   */
  bool run(Context &, T &, volatile bool * stop = 0) const;

  /**
   * Parse only one entry and return Properties object representing
   * the message
   */
  const Properties *parse(Context &, T &);

  bool getBreakOnCommand() const;
  void setBreakOnCommand(bool v);
  
  bool getBreakOnEmptyLine() const;
  void setBreakOnEmptyLine(bool v);

  bool getBreakOnInvalidArg() const;
  void setBreakOnInvalidArg(bool v);
  
private:
  ParserImpl * impl;
};

template<class T>
struct ParserRow {
public:
  enum Type { Cmd, Arg, CmdAlias, ArgAlias };
  enum ArgType { String, Int, Properties };
  enum ArgRequired { Mandatory, Optional };
  enum ArgMinMax { CheckMinMax, IgnoreMinMax };
  
  const char * name;
  const char * realName; 
  Type type;
  ArgType argType;
  ArgRequired argRequired;
  ArgMinMax argMinMax;
  int minVal;
  int maxVal;
  void (T::* function)(typename Parser<T>::Context & ctx, 
		       const class Properties& args);
  const char * description;
  void *user_value;
};

/**
 * The void* equivalent implementation
 */
class ParserImpl {
  class Dummy {};
  typedef ParserRow<Dummy> DummyRow;
  typedef Parser<Dummy>::Context Context;
  template<class T> friend class Parser;
private:
  
  ParserImpl(const DummyRow rows[], class InputStream & in,
	     bool b_cmd, bool b_empty, bool b_iarg);
  ~ParserImpl();
  
  bool run(Context *ctx, const class Properties **, volatile bool *) const ;
  
  static const DummyRow* matchCommand(Context*, const char*, const DummyRow*);
  static const DummyRow* matchArg(Context*, const char *, const DummyRow *);
  static bool parseArg(Context*, char*, const DummyRow*, Properties*);
  static bool checkMandatory(Context*, const Properties*);
private:
  const DummyRow * const m_rows;
  class ParseInputStream & input;
  bool m_breakOnEmpty;
  bool m_breakOnCmd;
  bool m_breakOnInvalidArg;
};

template<class T>
inline
Parser<T>::Parser(const ParserRow<T> rows[], class InputStream & in,
		  bool b_cmd, bool b_empty, bool b_iarg){
  impl = new ParserImpl((ParserImpl::DummyRow *)rows, in,
			b_cmd, b_empty, b_iarg);
}

template<class T>
inline
Parser<T>::~Parser(){
  delete impl;
}

template<class T>
inline
bool
Parser<T>::run(Context & ctx, T & t, volatile bool * stop) const {
  const Properties * p;
  DEBUG("Executing Parser<T>::run");
  if(impl->run((ParserImpl::Context*)&ctx, &p, stop)){
    const ParserRow<T> * cmd = ctx.m_currentCmd; // Cast to correct type
    if(cmd == 0){
      /**
       * Should happen if run returns true
       */
      abort();
    }

    for(unsigned i = 0; i<ctx.m_aliasUsed.size(); i++){
      const ParserRow<T> * alias = ctx.m_aliasUsed[i];
      if(alias->function != 0){
	/**
	 * Report alias usage with callback (if specified by user)
	 */
	DEBUG("Alias usage with callback");
	(t.* alias->function)(ctx, * p);
      }
    }

    if(cmd->function == 0){
      ctx.m_status = CommandWithoutFunction;
      DEBUG("CommandWithoutFunction");
      delete p;
      return false;
    }
    (t.* cmd->function)(ctx, * p); // Call the function
    delete p;
    return true;
  }
  DEBUG("");
  return false;
}

template<class T>
inline
const Properties *
Parser<T>::parse(Context &ctx, T &t) {
  const Properties * p;
  volatile bool stop = false;
  DEBUG("Executing Parser<T>::parse");

  if(impl->run((ParserImpl::Context*)&ctx, &p, &stop)){
    const ParserRow<T> * cmd = ctx.m_currentCmd; // Cast to correct type
    if(cmd == 0){
      /**
       * Should happen if run returns true
       */
      abort();
    }

    for(unsigned i = 0; i<ctx.m_aliasUsed.size(); i++){
      const ParserRow<T> * alias = ctx.m_aliasUsed[i];
      if(alias->function != 0){
	/**
	 * Report alias usage with callback (if specified by user)
	 */
	DEBUG("Alias usage with callback");
	(t.* alias->function)(ctx, * p);
      }
    }

    if(cmd->function == 0){
      DEBUG("CommandWithoutFunction");
      ctx.m_status = CommandWithoutFunction;
      return p;
    }
    return p;
  }
  DEBUG("");
  return NULL;
}

template<class T>
inline
bool 
Parser<T>::getBreakOnCommand() const{
  return impl->m_breakOnCmd;
}

template<class T>
inline
void
Parser<T>::setBreakOnCommand(bool v){
  impl->m_breakOnCmd = v;
}

template<class T>
inline
bool
Parser<T>::getBreakOnEmptyLine() const{
  return impl->m_breakOnEmpty;
}
template<class T>
inline
void
Parser<T>::setBreakOnEmptyLine(bool v){
  impl->m_breakOnEmpty = v;
}

template<class T>
inline
bool
Parser<T>::getBreakOnInvalidArg() const{
  return impl->m_breakOnInvalidArg;
}

template<class T>
inline
void
Parser<T>::setBreakOnInvalidArg(bool v){
  impl->m_breakOnInvalidArg;
}

#endif