/******************************************************************************
 * Top contributors (to current version):
 *   Andrew Reynolds, Clark Barrett, Mathias Preiner
 *
 * This file is part of the cvc5 project.
 *
 * Copyright (c) 2009-2025 by the authors listed in the file AUTHORS
 * in the top-level source directory and their institutional affiliations.
 * All rights reserved.  See the file COPYING in the top-level source
 * directory for licensing information.
 * ****************************************************************************
 *
 * Implementation of type set class.
 */
#include "theory/type_set.h"

using namespace std;
using namespace cvc5::internal::kind;

namespace cvc5::internal {
namespace theory {

TypeSet::~TypeSet()
{
  iterator it;
  for (it = d_typeSet.begin(); it != d_typeSet.end(); ++it)
  {
    delete (*it).second;
  }
  TypeToTypeEnumMap::iterator it2;
  for (it2 = d_teMap.begin(); it2 != d_teMap.end(); ++it2)
  {
    delete (*it2).second;
  }
}

void TypeSet::setTypeEnumeratorProperties(TypeEnumeratorProperties* tep)
{
  d_tep = tep;
}

void TypeSet::add(TypeNode t, TNode n)
{
  iterator it = d_typeSet.find(t);
  std::set<Node>* s;
  if (it == d_typeSet.end())
  {
    s = new std::set<Node>;
    d_typeSet[t] = s;
  }
  else
  {
    s = (*it).second;
  }
  s->insert(n);
}

std::set<Node>* TypeSet::getSet(TypeNode t) const
{
  const_iterator it = d_typeSet.find(t);
  if (it == d_typeSet.end())
  {
    return NULL;
  }
  return (*it).second;
}

Node TypeSet::nextTypeEnum(TypeNode t)
{
  TypeEnumerator* te;
  TypeToTypeEnumMap::iterator it = d_teMap.find(t);
  if (it == d_teMap.end())
  {
    te = new TypeEnumerator(t, d_tep);
    d_teMap[t] = te;
  }
  else
  {
    te = (*it).second;
  }
  if (te->isFinished())
  {
    return Node();
  }
  iterator itSet = d_typeSet.find(t);
  std::set<Node>* s;
  if (itSet == d_typeSet.end())
  {
    s = new std::set<Node>;
    d_typeSet[t] = s;
  }
  else
  {
    s = (*itSet).second;
  }
  Node n = **te;
  while (s->find(n) != s->end())
  {
    ++(*te);
    if (te->isFinished())
    {
      return Node();
    }
    n = **te;
  }
  s->insert(n);
  // add all subterms of n to this set as well
  // this is necessary for parametric types whose values are constructed from
  // other types to ensure that we do not enumerate subterms of other 
  // previously enumerated values
  std::unordered_set<TNode> visited;
  addSubTerms(n, visited);
  ++(*te);
  return n;
}

void TypeSet::addSubTerms(TNode n,
                          std::unordered_set<TNode>& visited,
                          bool topLevel)
{
  if (visited.find(n) == visited.end())
  {
    visited.insert(n);
    if (!topLevel)
    {
      add(n.getType(), n);
    }
    for (unsigned i = 0; i < n.getNumChildren(); i++)
    {
      addSubTerms(n[i], visited, false);
    }
  }
}

}  // namespace theory
}  // namespace cvc5::internal
