//
// Created by martin on 11.02.22.
//

#include "AnimGraphEditor.h"

#include "AnimGraphResource.h"
#include "imnodes.h"

using namespace AniGraph;

ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
  switch (socket_type) {
    case SocketType::SocketTypeAnimation:
      return ImNodesPinShape_QuadFilled;
    case SocketType::SocketTypeFloat:
      return ImNodesPinShape_CircleFilled;
    case SocketType::SocketTypeVec3:
      return ImNodesPinShape_TriangleFilled;
    case SocketType::SocketTypeQuat:
      return ImNodesPinShape_Triangle;
    case SocketType::SocketTypeBool:
      return ImNodesPinShape_Circle;
    default:
      break;
  }

  return ImNodesPinShape_Quad;
}

void AnimGraphEditorRenderSidebar(AnimNodeResource& node_resource) {
  ImGui::Text("[%s]", node_resource.m_type_name.c_str());

  char node_name_buffer[256];
  memset(node_name_buffer, 0, sizeof(node_name_buffer));
  strncpy(
      node_name_buffer,
      node_resource.m_name.c_str(),
      std::min(node_resource.m_name.size(), sizeof(node_name_buffer)));

  if (ImGui::InputText("Name", node_name_buffer, sizeof(node_name_buffer))) {
    node_resource.m_name = node_name_buffer;
  }

  int num_properties = node_resource.m_socket_accessor->m_properties.size();
  for (int i = 0; i < num_properties; i++) {
    Socket& property = node_resource.m_socket_accessor->m_properties[i];
    if (property.m_type == SocketType::SocketTypeFloat) {
      ImGui::SliderFloat(
          property.m_name.c_str(),
          reinterpret_cast<float*>(property.m_value.ptr),
          -100.f,
          100.f);
    } else if (property.m_type == SocketType::SocketTypeBool) {
      ImGui::Checkbox(
          property.m_name.c_str(),
          reinterpret_cast<bool*>(property.m_value.ptr));
    } else if (property.m_type == SocketType::SocketTypeString) {
      std::string* property_string =
          reinterpret_cast<std::string*>(property.m_value.ptr);
      char string_buf[256];
      memset(string_buf, 0, sizeof(string_buf));
      strncpy(
          string_buf,
          property_string->c_str(),
          std::min(property_string->size(), sizeof(string_buf)));
      if (ImGui::InputText(
              property.m_name.c_str(),
              string_buf,
              sizeof(string_buf))) {
        (*property_string) = string_buf;
      }
    }
  }
}

void AnimGraphEditorUpdate() {
  static AnimGraphResource graph_resource = AnimGraphResource();

  ImGui::BeginMenuBar();
  if (ImGui::Button("Save")) {
    graph_resource.saveToFile("editor_graph.json");
  }
  if (ImGui::Button("Load")) {
    graph_resource.loadFromFile("editor_graph.json");

    for (size_t i = 0, n = graph_resource.m_nodes.size(); i < n; i++) {
      const AnimNodeResource& node_resource = graph_resource.m_nodes[i];
      ImNodes::SetNodeGridSpacePos(
          i,
          ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
    }
  }
  if (ImGui::Button("Clear")) {
    graph_resource.clear();
  }
  char graph_name_buffer[256];
  memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
  strncpy(
      graph_name_buffer,
      graph_resource.m_name.c_str(),
      sizeof(graph_name_buffer));
  if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
    graph_resource.m_name = graph_name_buffer;
  }

  ImGui::EndMenuBar();

  ImGui::Columns(2);

  //
  // Node editor canvas
  //
  ImNodes::BeginNodeEditor();

  // Popup menu
  {
    const bool open_popup =
        ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
        && ImNodes::IsEditorHovered()
        && ImGui::IsMouseReleased(ImGuiMouseButton_Right);

    ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
    if (!ImGui::IsAnyItemHovered() && open_popup) {
      ImGui::OpenPopup("add node");
    }

    if (ImGui::BeginPopup("add node")) {
      const ImVec2 click_pos = ImGui::GetMousePosOnOpeningCurrentPopup();
      std::string node_type_name = "";
      if (ImGui::MenuItem("AnimSampler")) {
        node_type_name = "AnimSampler";
      }

      if (ImGui::MenuItem("Blend2")) {
        node_type_name = "Blend2";
      }

      if (ImGui::MenuItem("SpeedScale")) {
        node_type_name = "SpeedScale";
      }

      if (node_type_name != "") {
        AnimNodeResource node_resource =
            AnimNodeResourceFactory(node_type_name);
        size_t node_id = graph_resource.m_nodes.size();
        ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos());
        graph_resource.m_nodes.push_back(node_resource);
      }

      ImGui::EndPopup();
    }

    ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding);
  }

  for (size_t i = 0, n = graph_resource.m_nodes.size(); i < n; i++) {
    AnimNodeResource& node_resource = graph_resource.m_nodes[i];

    ImNodes::BeginNode(i);

    // Header
    ImNodes::BeginNodeTitleBar();
    if (&node_resource == &graph_resource.getGraphOutputNode()) {
      ImGui::TextUnformatted("Graph Outputs");
    } else if (&node_resource == &graph_resource.getGraphInputNode()) {
      ImGui::TextUnformatted("Graph Inputs");
    } else {
      ImGui::TextUnformatted(node_resource.m_type_name.c_str());
    }
    ImNodes::EndNodeTitleBar();

    // Inputs
    const std::vector<Socket>& node_inputs =
        node_resource.m_socket_accessor->m_inputs;
    for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
      const Socket& socket = node_inputs[j];

      ImColor socket_color = ImColor(255, 255, 255, 255);
      if (socket.m_flags & SocketFlagAffectsTime) {
        socket_color = ImColor(255, 128, 128, 255);
      }

      ImNodes::BeginInputAttribute(
          GenerateInputAttributeId(i, j),
          sGetSocketShapeFromSocketType(socket.m_type),
          socket_color);
      ImGui::TextUnformatted(socket.m_name.c_str());

      ImNodes::PushAttributeFlag(
          ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
      ImNodes::EndInputAttribute();
    }

    // Outputs
    const std::vector<Socket>& node_outputs =
        node_resource.m_socket_accessor->m_outputs;
    for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
      const Socket& socket = node_outputs[j];
      ImNodes::BeginOutputAttribute(
          GenerateOutputAttributeId(i, j),
          sGetSocketShapeFromSocketType(socket.m_type),
          ImColor(255, 255, 255, 255));
      ImGui::TextUnformatted(socket.m_name.c_str());
      ImNodes::PushAttributeFlag(
          ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
      ImNodes::EndInputAttribute();
    }

    // Graph output node
    if (i == 0) {
      if (ImGui::Button("+Output")) {
        AnimNodeResource& graph_output_node =
            graph_resource.getGraphOutputNode();

        static float bla = 0.f;
        std::string socket_name = "Output";
        socket_name += std::to_string(
            graph_output_node.m_socket_accessor->m_inputs.size());
        graph_output_node.m_socket_accessor->RegisterInput<float>(
            socket_name,
            nullptr);
      }
    } else if (i == 1) {
      if (ImGui::Button("+Input")) {
        AnimNodeResource& graph_input_node = graph_resource.getGraphInputNode();

        static float bla = 0.f;
        std::string socket_name = "Input";
        socket_name += std::to_string(
            graph_input_node.m_socket_accessor->m_outputs.size());
        graph_input_node.m_socket_accessor->RegisterOutput<float>(
            socket_name,
            nullptr);
      }
    }

    // Save state in node resource
    ImVec2 node_pos = ImNodes::GetNodeGridSpacePos(i);
    node_resource.m_position[0] = node_pos[0];
    node_resource.m_position[1] = node_pos[1];
    ImNodes::EndNode();

    // Ensure flags such as SocketFlagAffectsTime are properly set.
    node_resource.m_socket_accessor->UpdateFlags();
  }

  for (size_t i = 0, n = graph_resource.m_connections.size(); i < n; i++) {
    const AnimGraphConnection& connection = graph_resource.m_connections[i];
    int start_attr, end_attr;
    start_attr = GenerateOutputAttributeId(
        connection.m_source_node_index,
        connection.m_source_socket_index);
    end_attr = GenerateInputAttributeId(
        connection.m_target_node_index,
        connection.m_target_socket_index);
    ImNodes::Link(i, start_attr, end_attr);
  }

  ImNodes::EndNodeEditor();

  // Handle newly created links.
  int start_attr, end_attr;
  if (ImNodes::IsLinkCreated(&start_attr, &end_attr)) {
    int node_start_id;
    int node_start_output_index;
    SplitOutputAttributeId(
        start_attr,
        &node_start_id,
        &node_start_output_index);

    int node_end_id;
    int node_end_input_index;
    SplitInputAttributeId(end_attr, &node_end_id, &node_end_input_index);

    AnimGraphConnection connection;
    connection.m_source_node_index = node_start_id;
    connection.m_source_socket_index = node_start_output_index;

    connection.m_target_node_index = node_end_id;
    connection.m_target_socket_index = node_end_input_index;
    graph_resource.m_connections.push_back(connection);
  }

  // Handle link detachements.
  int link_id = 0;
  if (ImNodes::IsLinkDestroyed(&link_id)) {
    graph_resource.m_connections.erase(
        graph_resource.m_connections.begin() + link_id);
  }

  int selected_nodes[ImNodes::NumSelectedNodes()];
  ImNodes::GetSelectedNodes(selected_nodes);

  //
  // Sidebar
  //
  ImGui::NextColumn();

  if (ImNodes::NumSelectedNodes() == 1) {
    if (selected_nodes[0] < graph_resource.m_nodes.size()) {
      AnimNodeResource& selected_node =
          graph_resource.m_nodes[selected_nodes[0]];
      AnimGraphEditorRenderSidebar(selected_node);
    }
  }

  ImGui::Columns(1);
}