// Copyright (C) 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-derive-default.h"
#include "rust-ast.h"
#include "rust-diagnostics.h"
#include "rust-path.h"
#include "rust-system.h"

namespace Rust {
namespace AST {

DeriveDefault::DeriveDefault (location_t loc)
  : DeriveVisitor (loc), expanded (nullptr)
{}

std::unique_ptr<Item>
DeriveDefault::go (Item &item)
{
  item.accept_vis (*this);

  rust_assert (expanded);

  return std::move (expanded);
}

std::unique_ptr<Expr>
DeriveDefault::default_call (std::unique_ptr<Type> &&type)
{
  auto default_trait = builder.type_path ({"core", "default", "Default"}, true);

  auto default_fn
    = builder.qualified_path_in_expression (std::move (type), default_trait,
					    builder.path_segment ("default"));

  return builder.call (std::move (default_fn));
}

std::unique_ptr<AssociatedItem>
DeriveDefault::default_fn (std::unique_ptr<Expr> &&return_expr)
{
  auto self_ty
    = std::unique_ptr<Type> (new TypePath (builder.type_path ("Self")));

  auto block = std::unique_ptr<BlockExpr> (
    new BlockExpr ({}, std::move (return_expr), {}, {}, tl::nullopt, loc, loc));

  return builder.function ("default", {}, std::move (self_ty),
			   std::move (block));
}

std::unique_ptr<Item>
DeriveDefault::default_impl (
  std::unique_ptr<AssociatedItem> &&default_fn, std::string name,
  const std::vector<std::unique_ptr<GenericParam>> &type_generics)
{
  auto default_path = builder.type_path ({"core", "default", "Default"}, true);

  auto trait_items = vec (std::move (default_fn));

  auto generics = setup_impl_generics (name, type_generics,
				       builder.trait_bound (default_path));

  return builder.trait_impl (default_path, std::move (generics.self_type),
			     std::move (trait_items),
			     std::move (generics.impl));
}

void
DeriveDefault::visit_struct (StructStruct &item)
{
  if (item.is_unit_struct ())
    {
      auto unit_ctor
	= builder.struct_expr_struct (item.get_struct_name ().as_string ());
      expanded = default_impl (default_fn (std::move (unit_ctor)),
			       item.get_struct_name ().as_string (),
			       item.get_generic_params ());
      return;
    }

  auto cloned_fields = std::vector<std::unique_ptr<StructExprField>> ();
  for (auto &field : item.get_fields ())
    {
      auto name = field.get_field_name ().as_string ();
      auto expr = default_call (field.get_field_type ().clone_type ());

      cloned_fields.emplace_back (
	builder.struct_expr_field (std::move (name), std::move (expr)));
    }

  auto ctor = builder.struct_expr (item.get_struct_name ().as_string (),
				   std::move (cloned_fields));

  expanded = default_impl (default_fn (std::move (ctor)),
			   item.get_struct_name ().as_string (),
			   item.get_generic_params ());
}

void
DeriveDefault::visit_tuple (TupleStruct &tuple_item)
{
  auto defaulted_fields = std::vector<std::unique_ptr<Expr>> ();

  for (auto &field : tuple_item.get_fields ())
    {
      auto type = field.get_field_type ().clone_type ();

      defaulted_fields.emplace_back (default_call (std::move (type)));
    }

  auto return_expr
    = builder.call (builder.identifier (
		      tuple_item.get_struct_name ().as_string ()),
		    std::move (defaulted_fields));

  expanded = default_impl (default_fn (std::move (return_expr)),
			   tuple_item.get_struct_name ().as_string (),
			   tuple_item.get_generic_params ());
}

void
DeriveDefault::visit_enum (Enum &enum_item)
{
  // This is no longer the case in later Rust versions where you can choose a
  // default variant to emit using the `#[default]` attribute:
  //
  // ```rust
  // #[derive(Default)]
  // enum Baz {
  //     #[default]
  //     A,
  //     B(i32),
  //     C { a: i32 }
  // }
  // ```
  //
  // will emit the following impl
  //
  // ```rust
  // impl ::core::default::Default for Baz {
  //     #[inline]
  //     fn default() -> Baz { Self::A }
  // }
  // ```
  rust_error_at (loc, ErrorCode::E0665,
		 "%<Default%> cannot be derived for enums, only structs");
}

void
DeriveDefault::visit_union (Union &enum_item)
{
  rust_error_at (loc, "derive(Default) cannot be used on unions");
}

} // namespace AST
} // namespace Rust