#ifndef MYX_SQL_TREE_ITEM
#define MYX_SQL_TREE_ITEM

#include "myx_sql_parser_public_interface.h"
#include "sql_parser_symbols.h"
#include <list>
#include <set>
#include <iostream>
#include <stdlib.h>
#include <string.h>


namespace mysql_parser
{

extern void *new_simple_tree_item(sql::symbol name, const char *value, int stmt_boffset, int stmt_eoffset);
extern void *new_tree_item(sql::symbol name, const char *value, void *list);
extern void *set_item_name(void *item, sql::symbol name);
extern void delete_tree_item(void *tree_item_list);

extern void *tree_item_list_reuse(void *tree_item_from);
extern void *tree_item_reuse(void *item, sql::symbol name, void *list);
extern void *new_tree_item_list();
extern void *new_tree_item_list_reuse(void *tree_item_from);
extern void tree_item_list_add(void *tree_item_list, void *tree_item);
extern void tree_item_list_add_all(void *tree_item_list, void *tree_item);

} // namespace mysql_parser


namespace mysql_parser
{

#define ARR_CAPACITY(arr) (sizeof(arr)/sizeof(arr[0]))

bool are_strings_eq(const char *str1, const char *str2, bool case_sensitive);
bool are_strings_eq_ci(const char *str1, const char *str2);
inline const char* find_str_in_array_ci(const char *arr[], size_t arr_size, const char* str);


#ifdef __cplusplus

// pattern composite
class MyxSQLTreeItem
{
public:
  typedef std::list<MyxSQLTreeItem *> SubItemList;

  static void add_ast_branch(void *ast_branch);
  static void remove_ast_branch(void *ast_branch);
  static void make_items_cleanup();
  static MyxSQLTreeItem * tree() { return _tree; }
  static void tree(MyxSQLTreeItem *tree);

protected:
  static std::set<MyxSQLTreeItem *> _ast_branches;
  static MyxSQLTreeItem *_tree;

  mutable bool _del_list; // not a part of business object
  sql::symbol _name;      // _name is sql::symbol
  char *_value;           // _value is in utf8
  int _valuelen;          // _valuelen is in bytes
  SubItemList* _subitems;
  const MyxSQLTreeItem * left_most_subitem() const;
  const MyxSQLTreeItem * right_most_subitem() const;
  int _stmt_lineno;
  int _stmt_boffset;
  int _stmt_eoffset;

  const MyxSQLTreeItem *subitem_by_name(sql::symbol name, const MyxSQLTreeItem *start_item) const;
  const MyxSQLTreeItem *subitem_by_name(sql::symbol name, size_t position = 0) const;
  const MyxSQLTreeItem *rsubitem_by_name(sql::symbol name, size_t position = 0) const; // reverse
  const MyxSQLTreeItem *subitem_by_path(sql::symbol path[]) const;

  inline const MyxSQLTreeItem * subseq__(const MyxSQLTreeItem *start_subitem, sql::symbol name, va_list args) const;
  inline const MyxSQLTreeItem * find_subseq__(const MyxSQLTreeItem *start_subitem, sql::symbol name, va_list args) const;

  const MyxSQLTreeItem * check_words(sql::symbol words[], size_t words_count, const MyxSQLTreeItem *start_item= NULL) const;
  const MyxSQLTreeItem * find_words(sql::symbol words[], size_t words_count, const MyxSQLTreeItem *start_item= NULL) const;

  char * subitems_as_string(const char *delim = " ") const;

  void restore_sql_text(int &boffset, int &eoffset, const MyxSQLTreeItem *first_subitem, const MyxSQLTreeItem *last_subitem) const;

public:
  MyxSQLTreeItem(sql::symbol name, const char *value, int len, int stmt_lineno, int stmt_boffset, int stmt_eoffset, SubItemList *items);
  virtual ~MyxSQLTreeItem();

  virtual void set_name(sql::symbol name) { _name= name; }
  bool name_equals(sql::symbol to) const { return _name == to; }
  bool value_equals(const char *to, int len) const { return ((len == _valuelen) && (memcmp(_value, to, _valuelen) == 0)) ? true: false; }
  sql::symbol name() const { return _name; }
  const char * value() const { return _value; }
  int value_length() const { return _valuelen; }
  int stmt_lineno() const;
  int stmt_boffset() const;
  int stmt_eoffset() const;

  virtual SubItemList *subitems() const { return _subitems; }

#define subitem(...) subitem_(__VA_ARGS__, NULL)
  const MyxSQLTreeItem * subitem_(int position, ...) const;
  const MyxSQLTreeItem * subitem_(sql::symbol name, ...) const; // name1, name2, ...
  const MyxSQLTreeItem * subitem__(sql::symbol name, va_list args) const;

#define subseq(...) subseq_(__VA_ARGS__, NULL)
  const MyxSQLTreeItem * subseq_(const MyxSQLTreeItem *start_subitem, sql::symbol name, ...) const;
  const MyxSQLTreeItem * subseq_(sql::symbol name, ...) const;

#define find_subseq(...) find_subseq_(__VA_ARGS__, NULL)
  const MyxSQLTreeItem * find_subseq_(const MyxSQLTreeItem *start_subitem, sql::symbol name, ...) const;
  const MyxSQLTreeItem * find_subseq_(sql::symbol name, ...) const;

  const MyxSQLTreeItem * search_by_names(sql::symbol names[], size_t path_count) const;
  const MyxSQLTreeItem * search_by_paths(sql::symbol * paths[], size_t path_count) const;

  std::string restore_sql_text(const std::string &sql_statement, const MyxSQLTreeItem *first_subitem= NULL, const MyxSQLTreeItem *last_subitem= NULL) const;
  void build_sql(std::string &sql) const; // warning - produces incorrect sql now

  void del_list(bool del_list) const { _del_list= del_list; }

  friend void *new_tree_item_list();
};

MYX_PUBLIC_FUNC std::ostream& operator << (std::ostream&, const MyxSQLTreeItem&);

class MyxSQLSimpleTreeItem : public MyxSQLTreeItem 
{
  static SubItemList _empty_list;

public:
  MyxSQLSimpleTreeItem(sql::symbol name, const char *value, int valuelen, int stmt_lineno, int stmt_boffset, int stmt_eoffset)
    : MyxSQLTreeItem(name, value, valuelen, stmt_lineno, stmt_boffset, stmt_eoffset, 0) {} 

  virtual SubItemList *subitems() const { return &_empty_list; }

  friend void *new_tree_item_list_reuse(void *tree_item_from);
};


#endif // __cplusplus

} // namespace mysql_parser

#endif // MYX_SQL_TREE_ITEM
