/* SPDX-FileCopyrightText: 2023 Blender Authors
 *
 * SPDX-License-Identifier: GPL-2.0-or-later */

#include "node_function_util.hh"

#include "NOD_socket_search_link.hh"

#include "BLT_translation.hh"

#include "UI_interface.hh"
#include "UI_interface_layout.hh"
#include "UI_resources.hh"

#include "BLO_read_write.hh"

#include "BLF_api.hh"

namespace blender::nodes::node_fn_input_string_cc {

static void node_declare(NodeDeclarationBuilder &b)
{
  b.add_output<decl::String>("String"_ustr).custom_draw([](CustomSocketDrawParams &params) {
    params.layout.alignment_set(ui::LayoutAlign::Expand);
    params.layout.textbox_with_state(
        &params.node_ptr,
        "string",
        RNA_pointer_get(&params.node_ptr, "textbox_state").data_as<TextboxState>(),
        IFACE_("String"));
  });
}

static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
  const bNode &bnode = builder.node();
  NodeInputString *node_storage = static_cast<NodeInputString *>(bnode.storage);
  std::string string = std::string((node_storage->string) ? node_storage->string : "");
  builder.construct_and_set_matching_fn<mf::CustomMF_Constant<std::string>>(std::move(string));
}

static void node_init(bNodeTree * /*tree*/, bNode *node)
{
  NodeInputString *storage = MEM_new<NodeInputString>(__func__);
  storage->textbox_state.visible_lines = 1;
  node->storage = storage;
}

static void node_storage_free(bNode *node)
{
  NodeInputString *storage = static_cast<NodeInputString *>(node->storage);
  if (storage == nullptr) {
    return;
  }
  if (storage->string != nullptr) {
    MEM_delete(storage->string);
  }
  MEM_delete(storage);
}

static void node_storage_copy(bNodeTree * /*dst_ntree*/, bNode *dest_node, const bNode *src_node)
{
  NodeInputString *source_storage = static_cast<NodeInputString *>(src_node->storage);
  NodeInputString *destination_storage = static_cast<NodeInputString *>(
      MEM_dupalloc(source_storage));

  if (source_storage->string) {
    destination_storage->string = MEM_dupalloc(source_storage->string);
  }
  dest_node->storage = destination_storage;
}

static void node_blend_write(const bNodeTree & /*tree*/, const bNode &node, BlendWriter &writer)
{
  const NodeInputString *storage = static_cast<const NodeInputString *>(node.storage);
  writer.write_string(storage->string);
}

static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &reader)
{
  NodeInputString *storage = static_cast<NodeInputString *>(node.storage);
  BLO_read_string(&reader, &storage->string);
}

static void node_gather_link_searches(GatherLinkSearchOpParams &params)
{
  const eNodeSocketDatatype type = params.other_socket().type;
  if (type != SOCK_STRING) {
    return;
  }
  if (params.other_socket().in_out == SOCK_OUT) {
    return;
  }

  params.add_item(IFACE_("String"), [](LinkSearchOpParams &params) {
    bNode &node = params.add_node("FunctionNodeInputString"_ustr);
    params.update_and_connect_available_socket(node, "String"_ustr);

    /* Adapt width of the new node to its content. */
    const StringRef string = static_cast<NodeInputString *>(node.storage)->string;
    const uiFontStyle &fstyle = ui::style_get()->widget;
    BLF_size(fstyle.uifont_id, fstyle.points);
    const float width = BLF_width(fstyle.uifont_id, string.data(), string.size()) + 40.0f;
    node.width = std::clamp(width, 140.0f, 1000.0f);
  });
}

static void node_register()
{
  static bke::bNodeType ntype;

  fn_cmp_node_type_base(&ntype, "FunctionNodeInputString"_ustr, FN_NODE_INPUT_STRING);
  ntype.ui_name = "String";
  ntype.ui_description = "Provide a string value that can be connected to other nodes in the tree";
  ntype.enum_name_legacy = "INPUT_STRING";
  ntype.nclass = NODE_CLASS_INPUT;
  ntype.declare = node_declare;
  ntype.initfunc = node_init;
  bke::node_type_storage(ntype, "NodeInputString", node_storage_free, node_storage_copy);
  ntype.build_multi_function = node_build_multi_function;
  ntype.blend_write_storage_content = node_blend_write;
  ntype.blend_data_read_storage_content = node_blend_read;
  ntype.gather_link_search_ops = node_gather_link_searches;
  bke::node_register_type(ntype);
}
NOD_REGISTER_NODE(node_register)

}  // namespace blender::nodes::node_fn_input_string_cc
