//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================

#ifndef __smoab_detail_UsageTable_h
#define __smoab_detail_UsageTable_h

#include <set>
#include <vector>

namespace smoab
{
namespace detail
{

namespace internal
{

struct KeyType
{
  moab::EntityHandle Handle;
  int ParentId;

  KeyType(const moab::EntityHandle& h, int p)
    : Handle(h)
    , ParentId(p)
  {
  }

  bool operator<(const KeyType& other) const
  {
    return ((this->Handle < other.Handle) ||
      ((this->Handle < other.Handle) && (this->ParentId < other.ParentId)));
  }
};

class key_inserter
{

protected:
  Range* container;

public:
  //constructor
  explicit key_inserter(Range& x)
    : container(&x)
  {
  }
  key_inserter& operator=(const KeyType& value)
  {
    container->insert(value.Handle);
    return *this;
  }

  key_inserter& operator*() { return *this; }
  key_inserter& operator++() { return *this; }
  key_inserter& operator++(int) { return *this; }

  typedef moab::EntityHandle value_type;
  typedef moab::EntityID difference_type;
  typedef std::output_iterator_tag iterator_category;
  typedef moab::EntityHandle* pointer;
  typedef moab::EntityHandle& reference;
};
}

//will store a collection of entities that are used only once with a given
//owner id
class UsageTable
{
public:
  void incrementUsage(
    const std::vector<smoab::EntityHandle>& entities, const std::vector<int>& usageId);

  smoab::Range multipleUsage() const;
  smoab::Range singleUsage() const;

private:
  std::vector<smoab::EntityHandle> MultipleUsageIds;
  std::set<internal::KeyType> SingleUsageIds;
};

void UsageTable::incrementUsage(
  const std::vector<smoab::EntityHandle>& entities, const std::vector<int>& usageId)
{
  typedef std::vector<smoab::EntityHandle>::const_iterator iterator;
  typedef std::vector<int>::const_iterator uiterator;

  uiterator usage = usageId.begin();
  for (iterator i = entities.begin(); i != entities.end(); ++i, ++usage)
  {
    internal::KeyType key(*i, *usage);
    if (this->SingleUsageIds.find(key) != this->SingleUsageIds.end())
    {
      this->SingleUsageIds.erase(key);
      this->MultipleUsageIds.push_back(*i);
    }
    else
    {
      this->SingleUsageIds.insert(key);
    }
  }
}

smoab::Range UsageTable::multipleUsage() const
{
  smoab::Range multiples;
  std::copy(this->MultipleUsageIds.rbegin(), this->MultipleUsageIds.rend(),
    moab::range_inserter(multiples));
  return multiples;
}

smoab::Range UsageTable::singleUsage() const
{
  smoab::Range single;
  std::copy(
    this->SingleUsageIds.rbegin(), this->SingleUsageIds.rend(), internal::key_inserter(single));
  return single;
}
}
}
#endif
