#ifndef _GRTTREEWALKER_H_
#define _GRTTREEWALKER_H_


#include "grts/structs.db.h"

using namespace bec;

template <class T>
struct CatalogIterator
{
  typedef int (T::*CGrtCb)(GrtObjectRef);

#define WB_ITERATOR_SUPPORT_OBJECT_TYPE(type) \
  typedef int (T::*C##type##Cb)(type);\
  std::vector<C##type##Cb> type##Cb;\
  std::vector<CGrtCb> type##GrtCb;\
  void append(C##type##Cb cb)   {type##Cb.push_back(cb);}\
  void append_##type##GrtCb(CGrtCb cb)   {type##GrtCb.push_back(cb);}

WB_ITERATOR_SUPPORT_OBJECT_TYPE(db_Schema);
WB_ITERATOR_SUPPORT_OBJECT_TYPE(db_Table);
WB_ITERATOR_SUPPORT_OBJECT_TYPE(db_Column);
WB_ITERATOR_SUPPORT_OBJECT_TYPE(db_Index);
WB_ITERATOR_SUPPORT_OBJECT_TYPE(db_ForeignKey);
WB_ITERATOR_SUPPORT_OBJECT_TYPE(db_View);
WB_ITERATOR_SUPPORT_OBJECT_TYPE(db_Routine);
WB_ITERATOR_SUPPORT_OBJECT_TYPE(db_RoutineGroup);
WB_ITERATOR_SUPPORT_OBJECT_TYPE(db_Role);
WB_ITERATOR_SUPPORT_OBJECT_TYPE(db_User);
WB_ITERATOR_SUPPORT_OBJECT_TYPE(db_RolePrivilege);

#define EXIST_CB(type) \
   (!type##Cb.empty() || !type##GrtCb.empty())

#define CALL_CB(type,obj) \
  {\
    for(size_t i= 0, cb_count= type##Cb.size(); i < cb_count; i++)\
    {\
      res&= (Self.*(type##Cb[i]))(obj);\
      if (!res && breakOnError)\
        return res;\
    }\
    for(size_t i= 0, cb_count= type##GrtCb.size(); i < cb_count; i++)\
    {\
      res&= (Self.*(type##GrtCb[i]))(obj);\
      if (!res && breakOnError)\
        return res;\
    }\
  }
#define ITERATE_LIST(type,list,object) \
  {\
    grt::ListRef<type> list= object.list();\
    if (EXIST_CB(type))\
      for(size_t i= 0, count= list.count(); i < count; i++)\
      {\
        type item= list.get(i);\
        CALL_CB(type, item);\
      }\
  }

#define ITERATE_LIST_DEEP(type,list,object) \
  {\
    grt::ListRef<type> list= object.list();\
    /*if (EXIST_CB(type)) TODO ?? */ \
      for(size_t i= 0, count= list.count(); i < count; i++)\
      {\
        type item= list.get(i);\
        CALL_CB(type, item);\
        res&= iterate(Self, item, breakOnError);\
        if (!res && breakOnError)\
          return res;\
      }\
  }

int iterate(T &Self, workbench_physical_ModelRef &model, bool breakOnError= false)
{
  int res= 1;
  res&= iterate_physical_views(Self, model, breakOnError);
  if (!res && breakOnError)
    return res;

  res&= iterate(Self, model.catalog(), breakOnError);
  if (!res && breakOnError)
    return res;

  return res;
}

int iterate_physical_views(T &Self, workbench_physical_ModelRef model, bool breakOnError= false)
{
  int res= 1;
  for(size_t i= 0, count= model.views().count(); i < count; i++)
  {
    workbench_physical_ViewRef view= model.views().get(i);
    for(size_t i= 0, count= view.figures().count(); i < count; i++)
    {
      model_FigureRef figure= view.figures().get(i);
      if (EXIST_CB(db_Table) && workbench_physical_TableFigureRef::can_wrap(figure))
      {
        workbench_physical_TableFigureRef table_figure(workbench_physical_TableFigureRef::cast_from(figure));
        if (table_figure.table().is_valid())
          CALL_CB(db_TableRef, table_figure.table());
      }
      if (EXIST_CB(db_View) && workbench_physical_ViewFigureRef::can_wrap(figure))
      {
        workbench_physical_ViewFigureRef view_figure(workbench_physical_ViewFigureRef::cast_from(figure));
        if (view_figure.view().is_valid())
          CALL_CB(db_ViewRef, view_figure.view());
      }
      if ((EXIST_CB(db_RoutineGroup) || EXIST_CB(db_Routine)) 
        && workbench_physical_RoutineGroupFigureRef::can_wrap(figure))
      {
        workbench_physical_RoutineGroupFigureRef rgroup_figure(workbench_physical_RoutineGroupFigureRef::cast_from(figure));
        if (rgroup_figure.routineGroup().is_valid())
        {
          db_RoutineGroupRef routineGroup= rgroup_figure.routineGroup();
          CALL_CB(db_RoutineGroupRef, routineGroup);
          for(size_t i= 0, count= routineGroup.routines().count(); i < count; i++)
          {
            db_RoutineRef routine= routineGroup.routines().get(i);
            CALL_CB(db_RoutineRef, routine);
          }
        }
      }
    }
  }
  return res;
}

int iterate(T &Self, db_TableRef &table, bool breakOnError= false)
{
  if (!EXIST_CB(db_Table)
    && !EXIST_CB(db_Column)
    && !EXIST_CB(db_Index)
    && !EXIST_CB(db_ForeignKey))
    return 1;

  int res= 1;

  ITERATE_LIST(db_ColumnRef, columns, table);
  ITERATE_LIST(db_IndexRef, indices, table);
  ITERATE_LIST(db_ForeignKeyRef, foreignKeys, table);

  return res;
}

int iterate(T &Self, db_SchemaRef &schema, bool breakOnError= false)
{
  int res= 1;
  ITERATE_LIST_DEEP(db_TableRef, tables, schema);
  ITERATE_LIST(db_ViewRef, views, schema);
  ITERATE_LIST(db_RoutineRef, routines, schema);
  ITERATE_LIST(db_RoutineGroupRef, routineGroups, schema);
  return res;
}

int iterate(T &Self, db_CatalogRef &catalog, bool breakOnError= false)
{
  int res= 1;

  ITERATE_LIST_DEEP(db_SchemaRef, schemata, catalog);
  ITERATE_LIST(db_UserRef, users, catalog);
  ITERATE_LIST_DEEP(db_RoleRef, roles, catalog);

  return res;
}

int iterate(T &Self, db_RoleRef &role, bool breakOnError= false)
{
  int res= 1;
  
  ITERATE_LIST(db_RolePrivilegeRef, privileges, role);

  return res;
}

#define CASE_ITERATE(type) \
  if (typeRef::can_wrap(object))\
    return iterate(Self, typeRef::cast_from(object), breakOnError);

int iterate(T &Self, GrtObjectRef &object, bool breakOnError= false)
{
  CASE_ITERATE(workbench_physical_Model);
  CASE_ITERATE(db_Catalog);
  CASE_ITERATE(db_Schema);
  CASE_ITERATE(db_Table);
  CASE_ITERATE(db_Role);
throw 1;
}

};


#endif