#ifndef IS_BUILDING_MB
#error "SetIterator.hpp isn't supposed to be included into an application"
#endif

#include "moab/SetIterator.hpp"
#include "moab/Core.hpp"
#include "moab/WriteUtilIface.hpp"
#include "MeshSet.hpp"
#include "Internals.hpp"
#include "moab/CN.hpp"
#include "moab/Error.hpp"

namespace moab 
{
    
SetIterator::~SetIterator() 
{
  myCore->remove_set_iterator(this);
}

RangeSetIterator::RangeSetIterator(Core *core, EntityHandle eset, int chunk_sz, 
                          EntityType ent_tp, int ent_dim, bool check_valid) 
        : SetIterator(core, eset, chunk_sz, ent_tp, ent_dim, check_valid),
          iterPos(0), pairPtr(NULL), numPairs(0)
{
  if (!eset) {
      // special case for the root set, have to keep a local array
    ErrorCode rval = build_pair_vec();
    assert(MB_SUCCESS == rval);

      // empty statement to avoid warning
    (void)(rval);
  }
}

RangeSetIterator::~RangeSetIterator() 
{
  if (pairPtr) delete [] pairPtr;
  numPairs = 0;
}

ErrorCode RangeSetIterator::build_pair_vec() 
{
    // shouldn't be here unless we're iterating the root set
  assert(!entSet);
  
  Range all_ents;
  ErrorCode rval = myCore->get_entities_by_handle(0, all_ents);
  if (MB_SUCCESS != rval) return rval;

  if (pairPtr) delete [] pairPtr;
  pairPtr = new EntityHandle[2*all_ents.psize()];
  Range::const_pair_iterator pi;
  int i;
  for (pi = all_ents.const_pair_begin(), i = 0;
       pi != all_ents.const_pair_end(); pi++, i+=2) {
    pairPtr[i] = (*pi).first;
    pairPtr[i+1] = (*pi).second;
  }
  numPairs = all_ents.psize();

  return MB_SUCCESS;
}
    
ErrorCode RangeSetIterator::get_next_arr(std::vector<EntityHandle> &arr,
                                         bool &atend) 
{
  atend = false;  

  int count;
  const EntityHandle *ptr;
  WriteUtilIface *iface;
  std::vector<EntityHandle> tmp_arr;
  std::vector<EntityHandle> *tmp_ptr = &arr;
  if (checkValid) tmp_ptr = &tmp_arr;
  ErrorCode rval;
  if (!pairPtr) {
    Interface *mbImpl = dynamic_cast<Interface*>(myCore);
    rval = mbImpl->query_interface(iface);
    if (MB_SUCCESS != rval) return rval;
  
    rval = iface->get_entity_list_pointers( &entSet, 1, &ptr, WriteUtilIface::CONTENTS, &count);
    if (MB_SUCCESS != rval) return rval;
    mbImpl->release_interface(iface);
  }
  else {
    if (checkValid) {
      rval = build_pair_vec();
      if (MB_SUCCESS != rval) return rval;
    }
    ptr = pairPtr;
    count = 2*numPairs;
  }
  assert(!(count%2));
  if (!count) {
    atend = true;
    return MB_SUCCESS;
  }

  if (-1 == entDimension) rval = get_next_by_type(ptr, count, *tmp_ptr, atend);
  else rval = get_next_by_dimension(ptr, count, *tmp_ptr, atend);
  if (MB_SUCCESS != rval) return rval;
  
  if (checkValid) {
    for (std::vector<EntityHandle>::iterator vit = tmp_ptr->begin(); vit != tmp_ptr->end(); vit++) {
      if (myCore->is_valid(*vit)) arr.push_back(*vit);
    }
  }
    
  return MB_SUCCESS;
}

ErrorCode RangeSetIterator::get_next_by_type(const EntityHandle *&ptr, int count,
                                             std::vector<EntityHandle> &arr, bool &atend)
{
  unsigned int num_ret = 0;
  bool max_type = (entType == MBMAXTYPE);
  size_t idx = 0;
    // initialize to first relevant handle
  while ((int)idx < count &&
         (iterPos > ptr[idx+1] ||
          (!max_type && !iterPos && CREATE_HANDLE(entType, ID_FROM_HANDLE(iterPos)) > ptr[idx+1])))
    idx += 2;
  if ((int)idx == count || TYPE_FROM_HANDLE(ptr[idx]) > entType) {
    atend = true;
    return MB_SUCCESS;
  }
  if (!iterPos && max_type) iterPos = ptr[idx];
  else if (!iterPos && 
           TYPE_FROM_HANDLE(ptr[idx]) <= entType &&
           TYPE_FROM_HANDLE(ptr[idx+1]) >= entType) {
    iterPos = std::max(CREATE_HANDLE(entType,1), ptr[idx]);
  }
  
    // idx points to start of subrange, iterPos in that subrange
  do {
    EntityHandle next = ptr[idx+1];
    if (TYPE_FROM_HANDLE(next) != entType && !max_type) next = LAST_HANDLE(entType);
    unsigned int this_ret = chunkSize-num_ret;
    unsigned int to_end = next - iterPos + 1;
    if (to_end < this_ret) this_ret = to_end;
    std::copy(MeshSet::hdl_iter(iterPos), MeshSet::hdl_iter(iterPos + this_ret),
              std::back_inserter(arr));
    if (this_ret == to_end) {
      idx += 2;
      iterPos = ((int)idx < count ? ptr[idx] : 0);
    }
    else iterPos += this_ret;

    num_ret += this_ret;
  }
  while ((int)idx < count && num_ret < chunkSize && 
         iterPos && (max_type || TYPE_FROM_HANDLE(iterPos) == entType));

  if (!iterPos || (!max_type && TYPE_FROM_HANDLE(iterPos) != entType)) atend = true;

  return MB_SUCCESS;
}

ErrorCode RangeSetIterator::get_next_by_dimension(const EntityHandle *&ptr, int count,
                                                  std::vector<EntityHandle> &arr, bool &atend) 
{
    // iterating by dimension - type should be maxtype
  if (entType != MBMAXTYPE) {
    MB_SET_ERR(MB_FAILURE, "Both dimension and type should not be set on an iterator");
  }
    
  unsigned int num_ret = 0;
  size_t idx = 0;
    // initialize to first relevant handle
  while ((int)idx < count &&
         (iterPos > ptr[idx+1] ||
          (!iterPos && entDimension > CN::Dimension(TYPE_FROM_HANDLE(ptr[idx+1])))))
    idx += 2;
  if ((int)idx == count || CN::Dimension(TYPE_FROM_HANDLE(ptr[idx])) > entDimension) {
    atend = true;
    return MB_SUCCESS;
  }
  if (!iterPos) iterPos = ptr[idx];
  else if (CN::Dimension(TYPE_FROM_HANDLE(ptr[idx])) < entDimension)
    iterPos = CREATE_HANDLE(CN::TypeDimensionMap[entDimension].first,1);
  
    // idx points to start of subrange, iterPos in that subrange
  do {
    EntityHandle next = ptr[idx+1];
    if (CN::Dimension(TYPE_FROM_HANDLE(next)) != entDimension) 
      next = LAST_HANDLE(CN::TypeDimensionMap[entDimension].second);
    unsigned int this_ret = chunkSize-num_ret;
    unsigned int to_end = next - iterPos + 1;
    if (to_end < this_ret) this_ret = to_end;
    std::copy(MeshSet::hdl_iter(iterPos), MeshSet::hdl_iter(iterPos + this_ret),
              std::back_inserter(arr));
    if (this_ret == to_end) {
      idx += 2;
      iterPos = ((int)idx < count ? ptr[idx] : 0);
    }
    else iterPos += this_ret;

    num_ret += this_ret;
  }
  while ((int)idx < count && num_ret < chunkSize && 
         iterPos && CN::Dimension(TYPE_FROM_HANDLE(iterPos)) == entDimension);

  if (!iterPos || CN::Dimension(TYPE_FROM_HANDLE(iterPos)) != entDimension) atend = true;

  return MB_SUCCESS;
}

ErrorCode RangeSetIterator::reset() 
{
  iterPos = 0; 
  return MB_SUCCESS;
}
  
ErrorCode VectorSetIterator::get_next_arr(std::vector<EntityHandle> &arr,
                                          bool &atend)
{
  int count;
  const EntityHandle *ptr;
  WriteUtilIface *iface;
  Interface *mbImpl = dynamic_cast<Interface*>(myCore);
  ErrorCode rval = mbImpl->query_interface(iface);
  if (MB_SUCCESS != rval) return rval;
  
  rval = iface->get_entity_list_pointers( &entSet, 1, &ptr, WriteUtilIface::CONTENTS, &count);
  if (MB_SUCCESS != rval) return rval;
  mbImpl->release_interface(iface);
  
  if (!count || iterPos >= count) {
    atend = true;
    return MB_SUCCESS;
  }
  
  std::vector<EntityHandle> tmp_arr;
  std::vector<EntityHandle> *tmp_ptr = &arr;
  if (checkValid) tmp_ptr = &tmp_arr;

    // just get the next chunkSize entities, or as many as you can
  int this_ct = 0;
  while (this_ct < (int)chunkSize && iterPos < count) {
    if ((MBMAXTYPE == entType || TYPE_FROM_HANDLE(ptr[iterPos]) == entType) &&
        (-1 == entDimension || CN::Dimension(TYPE_FROM_HANDLE(ptr[iterPos])) == entDimension)) {
      arr.push_back(ptr[iterPos]);
      this_ct++;
    }
    iterPos++;
  }
  
  atend = (iterPos == count);

  if (checkValid) {
    for (std::vector<EntityHandle>::iterator vit = tmp_ptr->begin(); vit != tmp_ptr->end(); vit++) {
      if (myCore->is_valid(*vit)) arr.push_back(*vit);
    }
  }

    // step along list, adding entities
  return MB_SUCCESS;
}

ErrorCode VectorSetIterator::reset() 
{
  iterPos = 0; 
  return MB_SUCCESS;
}
  
}
