// Copyright (C) 2020-2025 Free Software Foundation, Inc.

// This file is part of GCC.

// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.

// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.

// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3.  If not see
// <http://www.gnu.org/licenses/>.

#include "rust-rib.h"
#include "rust-name-resolution-context.h"

namespace Rust {
namespace Resolver2_0 {

Rib::Definition::Definition (NodeId id, Mode mode)
{
  switch (mode)
    {
    case Mode::SHADOWABLE:
      ids_shadowable.push_back (id);
      return;
    case Mode::NON_SHADOWABLE:
      ids_non_shadowable.push_back (id);
      return;
    case Mode::GLOBBED:
      ids_globbed.push_back (id);
      return;
    default:
      gcc_unreachable ();
    }
}

bool
Rib::Definition::is_ambiguous () const
{
  if (!ids_shadowable.empty ())
    return false;
  else if (!ids_non_shadowable.empty ())
    return ids_non_shadowable.size () > 1;
  else
    return ids_globbed.size () > 1;
}

std::string
Rib::Definition::to_string () const
{
  std::stringstream out;
  const char *headers[3] = {"(S)[", "] (NS)[", "] (G)["};
  const std::vector<NodeId> *id_lists[3]
    = {&ids_shadowable, &ids_non_shadowable, &ids_globbed};
  for (int i = 0; i < 3; i++)
    {
      out << headers[i];
      std::string sep;
      for (auto id : *id_lists[i])
	{
	  out << sep << id;
	  sep = ",";
	}
    }
  out << "]";
  return out.str ();
}

Rib::Definition
Rib::Definition::Shadowable (NodeId id)
{
  return Definition (id, Mode::SHADOWABLE);
}

Rib::Definition
Rib::Definition::NonShadowable (NodeId id)
{
  return Definition (id, Mode::NON_SHADOWABLE);
}

Rib::Definition
Rib::Definition::Globbed (NodeId id)
{
  return Definition (id, Mode::GLOBBED);
}

DuplicateNameError::DuplicateNameError (std::string name, NodeId existing)
  : name (name), existing (existing)
{}

Rib::Rib (Kind kind) : kind (kind) {}

Rib::Rib (Kind kind, std::string identifier, NodeId id)
  : Rib (kind, {{identifier, id}})
{}

Rib::Rib (Kind kind, std::unordered_map<std::string, NodeId> to_insert)
  : kind (kind)
{
  for (auto &value : to_insert)
    values.insert ({value.first, Definition::NonShadowable (value.second)});
}

tl::expected<NodeId, DuplicateNameError>
Rib::insert (std::string name, Definition def)
{
  auto it = values.find (name);
  if (it == values.end ())
    {
      /* No old value */
      values[name] = def;
    }
  else if (it->second.ids_non_shadowable.empty ()
	   || def.ids_non_shadowable.empty ())
    { /* No non-shadowable conflict */
      auto &current = values[name];
      for (auto id : def.ids_non_shadowable)
	{
	  if (std::find (current.ids_non_shadowable.cbegin (),
			 current.ids_non_shadowable.cend (), id)
	      == current.ids_non_shadowable.cend ())
	    current.ids_non_shadowable.push_back (id);
	  else
	    // TODO: should this produce an error?
	    return tl::make_unexpected (DuplicateNameError (name, id));
	}
      for (auto id : def.ids_shadowable)
	{
	  if (std::find (current.ids_shadowable.cbegin (),
			 current.ids_shadowable.cend (), id)
	      == current.ids_shadowable.cend ())
	    current.ids_shadowable.push_back (id);
	  else
	    // TODO: should this produce an error?
	    return tl::make_unexpected (DuplicateNameError (name, id));
	}
      for (auto id : def.ids_globbed)
	{
	  if (std::find (current.ids_globbed.cbegin (),
			 current.ids_globbed.cend (), id)
	      == current.ids_globbed.cend ())
	    current.ids_globbed.push_back (id);
	  else
	    // TODO: should this produce an error?
	    return tl::make_unexpected (DuplicateNameError (name, id));
	}
    }
  else /* Multiple non-shadowable */
    {
      return tl::make_unexpected (
	DuplicateNameError (name, it->second.ids_non_shadowable.back ()));
    }

  if (!def.ids_shadowable.empty ())
    return def.ids_shadowable.back ();
  else if (!def.ids_non_shadowable.empty ())
    return def.ids_non_shadowable.back ();
  rust_assert (!def.ids_globbed.empty ());
  return def.ids_globbed.back ();
}

tl::optional<Rib::Definition>
Rib::get (const std::string &name)
{
  auto it = values.find (name);

  if (it == values.end ())
    return tl::nullopt;

  return it->second;
}

const std::unordered_map<std::string, Rib::Definition> &
Rib::get_values () const
{
  return values;
}

} // namespace Resolver2_0
} // namespace Rust