// Copyright (C) 2020-2023 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/>.

#ifndef RUST_HIR_PATH_PROBE_H
#define RUST_HIR_PATH_PROBE_H

#include "rust-hir-type-check-base.h"
#include "rust-hir-full.h"
#include "rust-tyty.h"
#include "rust-substitution-mapper.h"
#include "rust-hir-type-bounds.h"

namespace Rust {
namespace Resolver {

struct PathProbeCandidate
{
  enum CandidateType
  {
    ERROR,

    ENUM_VARIANT,

    IMPL_CONST,
    IMPL_TYPE_ALIAS,
    IMPL_FUNC,

    TRAIT_ITEM_CONST,
    TRAIT_TYPE_ALIAS,
    TRAIT_FUNC,
  };

  struct EnumItemCandidate
  {
    const TyTy::ADTType *parent;
    const TyTy::VariantDef *variant;
  };

  struct ImplItemCandidate
  {
    HIR::ImplItem *impl_item;
    HIR::ImplBlock *parent;
  };

  struct TraitItemCandidate
  {
    const TraitReference *trait_ref;
    const TraitItemReference *item_ref;
    HIR::ImplBlock *impl;
  };

  CandidateType type;
  TyTy::BaseType *ty;
  Location locus;
  union Candidate
  {
    EnumItemCandidate enum_field;
    ImplItemCandidate impl;
    TraitItemCandidate trait;

    Candidate (EnumItemCandidate enum_field);
    Candidate (ImplItemCandidate impl);
    Candidate (TraitItemCandidate trait);
  } item;

  PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
		      EnumItemCandidate enum_field);

  PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
		      ImplItemCandidate impl);

  PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
		      TraitItemCandidate trait);

  std::string as_string () const;

  bool is_enum_candidate () const;

  bool is_impl_candidate () const;

  bool is_trait_candidate () const;

  bool is_full_trait_item_candidate () const;

  static PathProbeCandidate get_error ();

  bool is_error () const;

  DefId get_defid () const;

  bool operator< (const PathProbeCandidate &c) const;
};

class PathProbeType : public TypeCheckBase, public HIR::HIRImplVisitor
{
public:
  static std::set<PathProbeCandidate>
  Probe (const TyTy::BaseType *receiver,
	 const HIR::PathIdentSegment &segment_name, bool probe_impls,
	 bool probe_bounds, bool ignore_mandatory_trait_items,
	 DefId specific_trait_id = UNKNOWN_DEFID);

  void visit (HIR::TypeAlias &alias) override;
  void visit (HIR::ConstantItem &constant) override;
  void visit (HIR::Function &function) override;

protected:
  void process_enum_item_for_candiates (const TyTy::ADTType *adt);

  void process_impl_items_for_candidates ();

  void process_impl_item_candidate (HirId id, HIR::ImplItem *item,
				    HIR::ImplBlock *impl);

  void
  process_associated_trait_for_candidates (const TraitReference *trait_ref,
					   HIR::ImplBlock *impl,
					   bool ignore_mandatory_trait_items);

  void
  process_predicate_for_candidates (const TyTy::TypeBoundPredicate &predicate,
				    bool ignore_mandatory_trait_items);

protected:
  PathProbeType (const TyTy::BaseType *receiver,
		 const HIR::PathIdentSegment &query, DefId specific_trait_id);

  std::vector<std::pair<const TraitReference *, HIR::ImplBlock *>>
  union_bounds (
    const std::vector<std::pair</*const*/ TraitReference *, HIR::ImplBlock *>>
      a,
    const std::vector<std::pair<const TraitReference *, HIR::ImplBlock *>> b)
    const;

  bool is_reciever_generic () const;

  const TyTy::BaseType *receiver;
  const HIR::PathIdentSegment &search;
  std::set<PathProbeCandidate> candidates;
  HIR::ImplBlock *current_impl;
  DefId specific_trait_id;
};

class ReportMultipleCandidateError : private TypeCheckBase
{
public:
  static void Report (std::set<PathProbeCandidate> &candidates,
		      const HIR::PathIdentSegment &query, Location query_locus)
  {
    RichLocation r (query_locus);
    for (auto &c : candidates)
      r.add_range (c.locus);

    rust_error_at (r, "multiple applicable items in scope for: %s",
		   query.as_string ().c_str ());
  }
};

class PathProbeImplTrait : public PathProbeType
{
public:
  static std::set<PathProbeCandidate>
  Probe (const TyTy::BaseType *receiver,
	 const HIR::PathIdentSegment &segment_name,
	 const TraitReference *trait_reference);

private:
  PathProbeImplTrait (const TyTy::BaseType *receiver,
		      const HIR::PathIdentSegment &query,
		      const TraitReference *trait_reference);

  void process_trait_impl_items_for_candidates ();

  const TraitReference *trait_reference;
};

} // namespace Resolver
} // namespace Rust

#endif // RUST_HIR_PATH_PROBE_H
