//
// Created by martin on 04.02.22.
//

#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H
#define ANIMTESTBED_ANIMGRAPHRESOURCE_H

#include <cstring>
#include <iostream>
#include <map>
#include <string>
#include <type_traits>
#include <vector>

#include "SyncTrack.h"

namespace AniGraph {

//
// Data types
//

struct AnimData {
  float m_bone_transforms[16];
};

typedef float Vec3[3];
typedef float Quat[4];

inline int GenerateInputAttributeId(int node_id, int input_index) {
  return ((input_index + 1) << 14) + node_id;
}

inline void
SplitInputAttributeId(int attribute_id, int* node_id, int* input_index) {
  *node_id = attribute_id & ((1 << 14) - 1);
  *input_index = (attribute_id >> 14) - 1;
}

inline int GenerateOutputAttributeId(int node_id, int output_index) {
  return ((output_index + 1) << 23) + node_id;
}

inline void
SplitOutputAttributeId(int attribute_id, int* node_id, int* output_index) {
  *node_id = attribute_id & ((1 << 14) - 1);
  *output_index = (attribute_id >> 23) - 1;
}

enum class SocketType {
  SocketTypeUndefined = 0,
  SocketTypeBool,
  SocketTypeAnimation,
  SocketTypeFloat,
  SocketTypeVec3,
  SocketTypeQuat,
  SocketTypeString,
  SocketTypeLast
};

static const char* SocketTypeNames[] =
    {"", "Bool", "Animation", "Float", "Vec3", "Quat", "String"};

enum SocketFlags { SocketFlagAffectsTime = 1 };

struct Socket {
  std::string m_name;
  SocketType m_type = SocketType::SocketTypeUndefined;
  union SocketValue {
    void* ptr;
    void** ptr_ptr;
  };
  SocketValue m_value = {nullptr};
  int m_flags = 0;
  size_t m_type_size = 0;
};

struct AnimNode;
struct NodeSocketAccessorBase;

struct AnimNodeResource {
  std::string m_name;
  std::string m_type_name;
  AnimNode* m_anim_node = nullptr;
  NodeSocketAccessorBase* m_socket_accessor = nullptr;
  float m_position[2] = {0.f, 0.f};
};

struct NodeInput {
  AnimNode* m_node;
  SocketType m_type = SocketType::SocketTypeUndefined;
  std::string m_input_name;
};

enum class AnimNodeEvalState {
  Undefined,
  Deactivated,
  Activated,
  SyncTrackUpdated,
  TimeUpdated,
  Evaluated
};

struct AnimNode {
  std::string m_name;
  std::string m_node_type_name;
  float m_time_now = 0.f;
  float m_time_last = 0.f;
  size_t m_index = -1;
  AnimNodeEvalState m_state = AnimNodeEvalState::Undefined;
  SyncTrack m_sync_track;

  virtual ~AnimNode(){};

  virtual void MarkActiveInputs(const std::vector<NodeInput>& inputs) {
    for (size_t i = 0, n = inputs.size(); i < n; i++) {
      AnimNode* input_node = inputs[i].m_node;
      if (input_node != nullptr) {
        input_node->m_state = AnimNodeEvalState::Activated;
      }
    }
  }

  virtual void CalcSyncTrack(const std::vector<NodeInput>& inputs) {
    for (size_t i = 0, n = inputs.size(); i < n; i++) {
      AnimNode* input_node = inputs[i].m_node;
      if (input_node != nullptr
          && inputs[i].m_type == SocketType::SocketTypeAnimation
          && input_node->m_state != AnimNodeEvalState::Deactivated) {
        m_sync_track = input_node->m_sync_track;
        return;
      }
    }
  }

  virtual void UpdateTime(float time_last, float time_now) {
    m_time_last = time_last;
    m_time_now = time_now;
    m_state = AnimNodeEvalState::TimeUpdated;
  }

  virtual void Evaluate(){};
};

struct NodeSocketAccessorBase {
  std::vector<Socket> m_properties;
  std::vector<Socket> m_inputs;
  std::vector<Socket> m_outputs;

  NodeSocketAccessorBase() {}
  virtual ~NodeSocketAccessorBase() {}

  virtual void UpdateFlags(){};

  Socket* FindSocket(std::vector<Socket>& sockets, const std::string& name) {
    Socket* result = nullptr;
    for (size_t i = 0, n = sockets.size(); i < n; i++) {
      if (sockets[i].m_name == name) {
        result = &sockets[i];
        break;
      }
    }

    return result;
  }

  const Socket* FindSocket(
      const std::vector<Socket>& sockets,
      const std::string& name) const {
    const Socket* result = nullptr;
    for (size_t i = 0, n = sockets.size(); i < n; i++) {
      if (sockets[i].m_name == name) {
        result = &sockets[i];
        break;
      }
    }

    return result;
  }

  SocketType GetSocketType(
      const std::vector<Socket>& sockets,
      const std::string& name) {
    const Socket* socket = FindSocket(sockets, name);
    if (socket == nullptr) {
      return SocketType::SocketTypeUndefined;
    }
    return socket->m_type;
  }

  size_t GetSocketIndex(
      const std::vector<Socket>& sockets,
      const std::string& name) const {
    for (size_t i = 0, n = sockets.size(); i < n; i++) {
      if (sockets[i].m_name == name) {
        return i;
      }
    }

    return -1;
  }

  template <typename T>
  T GetSocketValue(
      const std::vector<Socket>& sockets,
      const std::string& name,
      T default_value) {
    const Socket* socket = FindSocket(sockets, name);
    if (socket == nullptr) {
      return default_value;
    }

    return *static_cast<T*>(socket->m_value.ptr);
  }

  template <typename T>
  void SetSocketValue(
      const std::vector<Socket>& sockets,
      const std::string& name,
      const T& value) {
    const Socket* socket = FindSocket(sockets, name);
    if (socket == nullptr) {
      std::cerr << "Error: could not set value of socket with name " << name
                << ": no socket found." << std::endl;
      return;
    }

    *static_cast<T*>(socket->m_value.ptr) = value;
  }

  template <typename T>
  bool RegisterSocket(
      std::vector<Socket>& sockets,
      const std::string& name,
      T* value_ptr,
      int flags = 0) {
    Socket* socket = FindSocket(sockets, name);
    if (socket != nullptr) {
      std::cerr << "Socket " << name << " already registered." << std::endl;
      return false;
    }

    sockets.push_back(Socket());
    socket = &sockets[sockets.size() - 1];
    socket->m_name = name;
    socket->m_type_size = sizeof(T);
    socket->m_flags = flags;

    if constexpr (std::is_same<T, float>::value) {
      socket->m_type = SocketType::SocketTypeFloat;
    } else if constexpr (std::is_same<T, bool>::value) {
      socket->m_type = SocketType::SocketTypeBool;
    } else if constexpr (std::is_same<T, Vec3>::value) {
      socket->m_type = SocketType::SocketTypeVec3;
    } else if constexpr (std::is_same<T, Quat>::value) {
      socket->m_type = SocketType::SocketTypeQuat;
    } else if constexpr (std::is_same<T, AnimData>::value) {
      socket->m_type = SocketType::SocketTypeAnimation;
    } else if constexpr (std::is_same<T, std::string>::value) {
      socket->m_type = SocketType::SocketTypeString;
    } else if constexpr (std::is_same<T, float*>::value) {
      socket->m_type = SocketType::SocketTypeFloat;
    } else if constexpr (std::is_same<T, bool*>::value) {
      socket->m_type = SocketType::SocketTypeBool;
    } else if constexpr (std::is_same<T, Vec3*>::value) {
      socket->m_type = SocketType::SocketTypeVec3;
    } else if constexpr (std::is_same<T, Quat*>::value) {
      socket->m_type = SocketType::SocketTypeQuat;
    } else if constexpr (std::is_same<T, AnimData*>::value) {
      socket->m_type = SocketType::SocketTypeAnimation;
    } else if constexpr (std::is_same<T, std::string*>::value) {
      socket->m_type = SocketType::SocketTypeString;
    } else {
      std::cerr << "Cannot register socket, invalid type." << std::endl;
      return false;
    }

    socket->m_value.ptr = value_ptr;
    return true;
  }

  template <typename T>
  bool RegisterProperty(const std::string& name, T* value) {
    return RegisterSocket(m_properties, name, value);
  }
  template <typename T>
  void SetProperty(const std::string& name, const T& value) {
    SetSocketValue(m_properties, name, value);
  }
  template <typename T>
  T GetProperty(const std::string& name, T default_value) {
    return GetSocketValue(m_properties, name, default_value);
  }
  SocketType GetPropertyType(const std::string& name) {
    return GetSocketType(m_properties, name);
  }

  template <typename T>
  bool RegisterInput(const std::string& name, T* value, int flags = 0) {
    return RegisterSocket(m_inputs, name, value, flags);
  }
  template <typename T>
  T* GetInput(const std::string& name, T* value) {
    return GetSocketValue(m_inputs, name, value);
  }
  Socket* FindInputSocket(const std::string& name) {
    return FindSocket(m_inputs, name);
  }
  SocketType GetInputType(const std::string& name) {
    return GetSocketType(m_inputs, name);
  }
  size_t GetInputIndex(const std::string& name) {
    return GetSocketIndex(m_inputs, name);
  }

  template <typename T>
  bool RegisterOutput(const std::string& name, T** value, int flags = 0) {
    return RegisterSocket(m_outputs, name, value, flags);
  }
  SocketType GetOutputType(const std::string& name) {
    return GetSocketType(m_outputs, name);
  }
  Socket* FindOutputSocket(const std::string& name) {
    return FindSocket(m_outputs, name);
  }
  size_t GetOutputIndex(const std::string& name) {
    return GetSocketIndex(m_outputs, name);
  }
};

template <typename T>
struct NodeSocketAccessor : public NodeSocketAccessorBase {
  virtual ~NodeSocketAccessor() {}
};

struct NodeRegistry {
  AnimNode* createNode(const std::string& node_type);
  NodeSocketAccessorBase* createNodeAccessor(
      const std::string& node_type,
      AnimNode* node);
};

//
// BlendTreeNode
//
struct BlendTreeNode : public AnimNode {};

template <>
struct NodeSocketAccessor<BlendTreeNode> : public NodeSocketAccessorBase {
  NodeSocketAccessor(AnimNode* node_) {}
};

//
// Blend2Node
//
struct Blend2Node : public AnimNode {
  AnimData m_input0;
  AnimData m_input1;
  AnimData* m_output = nullptr;
  float m_blend_weight = 0.f;
  bool m_sync_blend = false;

  virtual void MarkActiveInputs(const std::vector<NodeInput>& inputs) override {
    for (size_t i = 0, n = inputs.size(); i < n; i++) {
      AnimNode* input_node = inputs[i].m_node;
      if (input_node == nullptr) {
        continue;
      }

      if (inputs[i].m_input_name == "Input0" && m_blend_weight < 0.999) {
        input_node->m_state = AnimNodeEvalState::Activated;
        continue;
      }

      if (inputs[i].m_input_name == "Input1" && m_blend_weight > 0.001) {
        input_node->m_state = AnimNodeEvalState::Activated;
        continue;
      }
    }
  }

  virtual void UpdateTime(float dt, std::vector<NodeInput>& inputs) {
    if (!m_sync_blend) {
      m_time_now = m_time_now + dt;
    }

    for (size_t i = 0, n = inputs.size(); i < n; i++) {
      AnimNode* input_node = inputs[i].m_node;
      if (input_node == nullptr) {
        continue;
      }

      if (input_node->m_state != AnimNodeEvalState::Deactivated) {
        if (!m_sync_blend) {
          input_node->m_time_now = m_time_now;
        }
        input_node->m_state = AnimNodeEvalState::TimeUpdated;
        continue;
      }
    }
  }
};

template <>
struct NodeSocketAccessor<Blend2Node> : public NodeSocketAccessorBase {
  NodeSocketAccessor(AnimNode* node_) {
    Blend2Node* node = dynamic_cast<Blend2Node*>(node_);
    RegisterInput("Input0", &node->m_input0);
    RegisterInput("Input1", &node->m_input1);
    RegisterInput(
        "Weight",
        &node->m_blend_weight,
        SocketFlags::SocketFlagAffectsTime);

    RegisterOutput("Output", &node->m_output);

    RegisterProperty("Sync", &node->m_sync_blend);
  }

  virtual void UpdateFlags() override {
    Socket* weight_input_socket = FindSocket(m_inputs, "Weight");
    assert(weight_input_socket != nullptr);

    if (GetProperty<bool>("Sync", false) == true) {
      weight_input_socket->m_flags = SocketFlags::SocketFlagAffectsTime;
    } else {
      weight_input_socket->m_flags = 0;
    }
  }
};

//
// SpeedScaleNode
//
struct SpeedScaleNode : public AnimNode {
  AnimData m_input;
  AnimData* m_output = nullptr;
  float m_speed_scale = 0.f;

  virtual void UpdateTime(float time_last, float time_now) {
    m_time_last = time_last;
    m_time_now = time_last + (time_now - time_last) * m_speed_scale;
    m_state = AnimNodeEvalState::TimeUpdated;
  }
};

template <>
struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase {
  NodeSocketAccessor(AnimNode* node_) {
    SpeedScaleNode* node = dynamic_cast<SpeedScaleNode*>(node_);
    RegisterInput(
        "SpeedScale",
        &node->m_speed_scale,
        SocketFlags::SocketFlagAffectsTime);
    RegisterInput("Input", &node->m_input);

    RegisterOutput("Output", &node->m_output);
  }
};

//
// AnimSamplerNode
//
struct AnimSamplerNode : public AnimNode {
  AnimData* m_output = nullptr;
  std::string m_filename;
};

template <>
struct NodeSocketAccessor<AnimSamplerNode> : public NodeSocketAccessorBase {
  NodeSocketAccessor(AnimNode* node_) {
    AnimSamplerNode* node = dynamic_cast<AnimSamplerNode*>(node_);
    RegisterOutput("Output", &node->m_output);

    RegisterProperty("Filename", &node->m_filename);
  }
};

//
// AnimGraphResource
//
struct AnimGraphConnection {
  const AnimNodeResource* m_source_node = nullptr;
  const Socket* m_source_socket = nullptr;
  const AnimNodeResource* m_target_node = nullptr;
  const Socket* m_target_socket = nullptr;
};

struct AnimGraphResource {
  std::string m_name;
  std::vector<AnimNodeResource> m_nodes;
  std::vector<AnimGraphConnection> m_connections;

  ~AnimGraphResource() {
    for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
      delete m_nodes[i].m_anim_node;
      delete m_nodes[i].m_socket_accessor;
    }
  }

  AnimGraphResource() { clear(); }

  void clear();
  void clearNodes();
  void initGraphConnectors();
  bool saveToFile(const char* filename) const;
  bool loadFromFile(const char* filename);

  AnimNodeResource& getGraphOutputNode() { return m_nodes[0]; }
  AnimNodeResource& getGraphInputNode() { return m_nodes[1]; }

  const AnimNodeResource& getGraphOutputNode() const { return m_nodes[0]; }
  const AnimNodeResource& getGraphInputNode() const { return m_nodes[1]; }

  size_t getNodeIndex(const AnimNodeResource& node_resource) const {
    for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
      if (&m_nodes[i] == &node_resource) {
        return i;
      }
    }

    return -1;
  }

  size_t addNode(AnimNodeResource node_resource) {
    m_nodes.push_back(node_resource);
    return m_nodes.size() - 1;
  }

  bool connectSockets(
      const AnimNodeResource& source_node,
      const std::string& source_socket_name,
      const AnimNodeResource& target_node,
      const std::string& target_socket_name) {
    size_t source_index = -1;
    size_t target_index = -1;
    for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
      if (&source_node == &m_nodes[i]) {
        source_index = i;
      }

      if (&target_node == &m_nodes[i]) {
        target_index = i;
      }

      if (source_index < m_nodes.size() && target_index < m_nodes.size()) {
        break;
      }
    }

    if (source_index >= m_nodes.size() || target_index >= m_nodes.size()) {
      std::cerr << "Cannot connect nodes: could not find nodes." << std::endl;
      return false;
    }

    Socket* source_socket =
        source_node.m_socket_accessor->FindOutputSocket(source_socket_name);
    Socket* target_socket =
        target_node.m_socket_accessor->FindInputSocket(target_socket_name);

    if (source_socket == nullptr || target_socket == nullptr) {
      std::cerr << "Cannot connect nodes: could not find sockets." << std::endl;
      return false;
    }

    AnimGraphConnection connection;
    connection.m_source_node = &source_node;
    connection.m_source_socket = source_socket;
    connection.m_target_node = &target_node;
    connection.m_target_socket = target_socket;
    m_connections.push_back(connection);

    return true;
  }
};

static inline AnimNode* AnimNodeFactory(const std::string& name) {
  AnimNode* result;
  if (name == "Blend2") {
    result = new Blend2Node;
  } else if (name == "SpeedScale") {
    result = new SpeedScaleNode;
  } else if (name == "AnimSampler") {
    result = new AnimSamplerNode;
  } else if (name == "BlendTree") {
    result = new BlendTreeNode;
  }

  if (result != nullptr) {
    result->m_node_type_name = name;
    return result;
  }

  std::cerr << "Invalid node type: " << name << std::endl;
  return nullptr;
}

static inline NodeSocketAccessorBase* AnimNodeAccessorFactory(
    const std::string& node_type_name,
    AnimNode* node) {
  if (node_type_name == "Blend2") {
    return new NodeSocketAccessor<Blend2Node>(node);
  } else if (node_type_name == "SpeedScale") {
    return new NodeSocketAccessor<SpeedScaleNode>(node);
  } else if (node_type_name == "AnimSampler") {
    return new NodeSocketAccessor<AnimSamplerNode>(node);
  } else if (node_type_name == "BlendTree") {
    return new NodeSocketAccessor<BlendTreeNode>(node);
  } else {
    std::cerr << "Invalid node type name " << node_type_name << "."
              << std::endl;
  }
  return nullptr;
}

static inline AnimNodeResource AnimNodeResourceFactory(
    const std::string& node_type_name) {
  AnimNodeResource result;
  result.m_type_name = node_type_name;
  result.m_anim_node = AnimNodeFactory(node_type_name);
  result.m_socket_accessor =
      AnimNodeAccessorFactory(node_type_name, result.m_anim_node);
  return result;
}

//
// AnimGraph (Runtime)
//
struct AnimGraph {
  AnimData m_local_transforms;

  std::vector<AnimNode*> m_nodes;
  std::vector<AnimNode*> m_eval_ordered_nodes;
  std::vector<std::vector<NodeInput> > m_node_inputs;
  NodeSocketAccessorBase* m_socket_accessor;
  char* m_input_buffer = nullptr;
  char* m_output_buffer = nullptr;

  std::vector<Socket>& getGraphOutputs() { return m_socket_accessor->m_inputs; }
  std::vector<Socket>& getGraphInputs() { return m_socket_accessor->m_outputs; }

  ~AnimGraph() {
    delete[] m_input_buffer;
    delete[] m_output_buffer;

    for (int i = 0; i < m_nodes.size(); i++) {
      delete m_nodes[i];
    }

    delete m_socket_accessor;
  }

  void updateOrderedNodes();
  void markActiveNodes();
  bool checkIsNodeActive(AnimNode* node) {
    return node->m_state != AnimNodeEvalState::Deactivated;
  }
  void evalSyncTracks();
  void updateTime(float dt);
  void evaluate();
  void reset() {
    for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
      m_nodes[i]->m_time_now = 0.f;
      m_nodes[i]->m_time_last = 0.f;
      m_nodes[i]->m_state = AnimNodeEvalState::Undefined;
    }
  }

  void* getOutput(const std::string& name) const;
  void* getInput(const std::string& name) const;

  int getNodeEvalOrderIndex(const AnimNode* node) {
    for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
      if (m_eval_ordered_nodes[i] == node) {
        return i;
      }
    }

    return -1;
  }
  AnimNode* getAnimNodeForInput(
      size_t node_index,
      const std::string& input_name) {
    assert(node_index < m_nodes.size());
    assert(node_index < m_node_inputs.size());

    std::vector<NodeInput>& node_inputs = m_node_inputs[node_index];
    for (size_t i = 0, n = node_inputs.size(); i < n; i++) {
      if (node_inputs[i].m_input_name == input_name) {
        return node_inputs[i].m_node;
      }
    }

    return nullptr;
  }

  AnimNode* getAnimNode(const char* name) {
    for (size_t i = 0; i < m_nodes.size(); i++) {
      if (m_nodes[i]->m_name == name) {
        return m_nodes[i];
      }
    }

    return nullptr;
  }

  static AnimGraph createFromResource(const AnimGraphResource& resource) {
    AnimGraph result;

    // create nodes
    for (int i = 0; i < resource.m_nodes.size(); i++) {
      const AnimNodeResource& node_resource = resource.m_nodes[i];
      AnimNode* node = AnimNodeFactory(node_resource.m_type_name.c_str());
      node->m_name = node_resource.m_name;
      node->m_node_type_name = node_resource.m_type_name;
      node->m_index = i;
      result.m_nodes.push_back(node);

      assert(node_resource.m_socket_accessor != nullptr);
      result.m_node_inputs.push_back(std::vector<NodeInput>());
      std::vector<NodeInput>& node_inputs = result.m_node_inputs.back();

      for (int j = 0, n = node_resource.m_socket_accessor->m_inputs.size();
           j < n;
           j++) {
        const Socket& input_socket =
            node_resource.m_socket_accessor->m_inputs[j];

        NodeInput input;
        input.m_node = nullptr;
        input.m_type = input_socket.m_type;
        input.m_input_name = input_socket.m_name;

        node_inputs.push_back(input);
      }
    }

    // Prepare graph inputs
    result.m_socket_accessor =
        AnimNodeAccessorFactory("BlendTree", result.m_nodes[0]);
    result.m_socket_accessor->m_outputs =
        resource.m_nodes[1].m_socket_accessor->m_outputs;
    result.m_socket_accessor->m_inputs =
        resource.m_nodes[0].m_socket_accessor->m_inputs;

    // inputs
    int input_block_size = 0;
    std::vector<Socket>& graph_inputs = result.getGraphInputs();
    for (int i = 0; i < graph_inputs.size(); i++) {
      input_block_size += sizeof(void*);
    }
    result.m_input_buffer = new char[input_block_size];
    memset(result.m_input_buffer, 0, input_block_size);

    int input_block_offset = 0;
    for (int i = 0; i < graph_inputs.size(); i++) {
      if (graph_inputs[i].m_type == SocketType::SocketTypeAnimation) {
      }
      graph_inputs[i].m_value.ptr =
          (void*)&result.m_input_buffer[input_block_offset];
      input_block_offset += sizeof(void*);
    }

    // outputs
    int output_block_size = 0;
    std::vector<Socket>& graph_outputs = result.getGraphOutputs();
    for (int i = 0; i < graph_outputs.size(); i++) {
      output_block_size += graph_outputs[i].m_type_size;
    }
    result.m_output_buffer = new char[output_block_size];
    memset(result.m_output_buffer, 0, output_block_size);

    int output_block_offset = 0;
    for (int i = 0; i < graph_outputs.size(); i++) {
      if (graph_outputs[i].m_type == SocketType::SocketTypeAnimation) {
      }
      graph_outputs[i].m_value.ptr =
          (void*)&result.m_output_buffer[output_block_offset];
      output_block_offset += graph_outputs[i].m_type_size;
    }

    // connect the nodes
    for (int i = 0; i < resource.m_connections.size(); i++) {
      const AnimGraphConnection& connection = resource.m_connections[i];
      std::string source_node_type = "";
      std::string target_node_type = "";
      std::string source_node_name = "";
      std::string target_node_name = "";
      AnimNode* source_node = nullptr;
      AnimNode* target_node = nullptr;
      NodeSocketAccessorBase* source_node_accessor = nullptr;
      NodeSocketAccessorBase* target_node_accessor = nullptr;
      SocketType source_type;
      SocketType target_type;
      size_t source_socket_index = -1;
      size_t target_socket_index = -1;

      if (connection.m_source_node != nullptr) {
        size_t node_index = resource.getNodeIndex(*connection.m_source_node);
        if (node_index == -1) {
          std::cerr << "Could not find source node index." << std::endl;
          continue;
        }
        source_node = result.m_nodes[node_index];
        source_node_name = source_node->m_name;
        source_node_type = source_node->m_node_type_name;
        if (node_index == 1) {
          source_node_accessor = result.m_socket_accessor;
        } else {
          source_node_accessor =
              AnimNodeAccessorFactory(source_node_type, source_node);
        }
      }

      if (connection.m_target_node != nullptr) {
        size_t node_index = resource.getNodeIndex(*connection.m_target_node);
        if (node_index == -1) {
          std::cerr << "Could not find source node index." << std::endl;
          continue;
        }
        target_node = result.m_nodes[node_index];
        target_node_name = target_node->m_name;
        target_node_type = target_node->m_node_type_name;
        if (node_index == 0) {
          target_node_accessor = result.m_socket_accessor;
        } else {
          target_node_accessor =
              AnimNodeAccessorFactory(target_node_type, target_node);
        }
      }

      assert(source_node != nullptr);
      assert(target_node != nullptr);

      //
      // Map resource node sockets to graph instance node sockets
      //
      if (connection.m_source_socket == nullptr) {
        std::cerr << "Invalid source socket for connection " << i << "."
                  << std::endl;
        continue;
      }

      if (connection.m_target_socket == nullptr) {
        std::cerr << "Invalid source socket for connection " << i << "."
                  << std::endl;
        continue;
      }

      source_socket_index = source_node_accessor->GetOutputIndex(
          connection.m_source_socket->m_name);
      if (source_socket_index == -1) {
        std::cerr << "Invalid source socket "
                  << connection.m_source_socket->m_name << " for node "
                  << connection.m_source_node->m_name << "." << std::endl;
        continue;
      }
      const Socket* source_socket =
          &source_node_accessor->m_outputs[source_socket_index];

      target_socket_index = target_node_accessor->GetInputIndex(
          connection.m_target_socket->m_name);
      if (target_socket_index == -1) {
        std::cerr << "Invalid target socket "
                  << connection.m_target_socket->m_name << " for node "
                  << connection.m_target_node->m_name << "." << std::endl;
        continue;
      }
      const Socket* target_socket =
          &target_node_accessor->m_inputs[target_socket_index];

      if (source_socket->m_type != target_socket->m_type) {
        std::cerr << "Cannot connect sockets: invalid types!" << std::endl;
      }

      //
      // Wire up outputs to inputs.
      //
      (*source_socket->m_value.ptr_ptr) = target_socket->m_value.ptr;

      size_t target_node_index = target_node->m_index;

      std::vector<NodeInput>& node_inputs =
          result.m_node_inputs[target_node_index];
      for (int j = 0, n = node_inputs.size(); j < n; j++) {
        if (node_inputs[j].m_input_name == target_socket->m_name) {
          node_inputs[j].m_node = source_node;
        }
      }

      if (target_node_accessor != result.m_socket_accessor) {
        delete target_node_accessor;
      }

      if (source_node_accessor != result.m_socket_accessor) {
        delete source_node_accessor;
      }
    }

    result.updateOrderedNodes();
    result.reset();

    return result;
  }
};

}  // namespace AniGraph

#endif  //ANIMTESTBED_ANIMGRAPHRESOURCE_H
