text: #include "animation.h"
  #include "controller.h"
  #include "nodes.h"
  #include "engine/log.h"
  #include "engine/resource_manager.h"
  #include "renderer/model.h"
  #include "renderer/pose.h"
  
  
  namespace Lumix::Anim {
  
  const ResourceType Controller::TYPE = ResourceType("anim_controller");
  
  Controller::Controller(const Path& path, ResourceManager& resource_manager, IAllocator& allocator) 
  	: Resource(path, resource_manager, allocator)
  	, m_allocator(allocator)
  	, m_animation_slots(allocator)
  	, m_animation_entries(allocator)
  	, m_bone_masks(allocator)
  {}
  
  Controller::~Controller() {
  	ASSERT(isEmpty());
  }
  
  void Controller::destroy() {
  	unload();
  }
  
  void Controller::unload() {
  	LUMIX_DELETE(m_allocator, m_root);
  	m_root = nullptr;
  }
  
  void Controller::initEmpty() {
  	ASSERT(!m_root);
  	m_root = LUMIX_NEW(m_allocator, GroupNode)(nullptr, m_allocator);
  	m_root->m_name = "Root";
  }
  
  bool Controller::load(u64 size, const u8* mem) {
  	InputMemoryStream str(mem, size);
  	deserialize(str);
  	return true;
  }
  
  static u32 computeInputsSize(Controller& ctrl) {
  	u32 size = 0;
  	for (u32 i = 0; i < ctrl.m_inputs.inputs_count; ++i) {
  		switch (ctrl.m_inputs.inputs[i].type) {
  			case InputDecl::FLOAT: size += sizeof(float); break;
  			case InputDecl::BOOL: size += sizeof(bool); break;
  			case InputDecl::U32: size += sizeof(u32); break;
  			default: ASSERT(false); break;
  		}
  	}
  	return size;
  }
  	
  void Controller::destroyRuntime(RuntimeContext& ctx) {
  	LUMIX_DELETE(m_allocator, &ctx);
  }
  
  RuntimeContext* Controller::createRuntime(u32 anim_set) {
  	RuntimeContext* ctx = LUMIX_NEW(m_allocator, RuntimeContext)(*this, m_allocator);
  	ctx->inputs.resize(computeInputsSize(*this));
  	memset(ctx->inputs.begin(), 0, ctx->inputs.byte_size());
  	ctx->animations.resize(m_animation_slots.size());
  	memset(ctx->animations.begin(), 0, ctx->animations.byte_size());
  	for (AnimationEntry& anim : m_animation_entries) {
  		if (anim.set == anim_set) {
  			ctx->animations[anim.slot] = anim.animation;
  		}
  	}
  	m_root->enter(*ctx);
  	return ctx;
  }
  
  void Controller::update(RuntimeContext& ctx, Ref<LocalRigidTransform> root_motion) const {
  	ASSERT(&ctx.controller == this);
  	// TODO better allocation strategy
  	const Span<u8> mem = ctx.data.releaseOwnership();
  	ctx.data.reserve(mem.length());
  	ctx.input_runtime.set(mem.begin(), mem.length());
  	m_root->update(ctx, root_motion);
  	m_allocator.deallocate(mem.begin());
  	
  	auto root_bone_iter = ctx.model->getBoneIndex(ctx.root_bone_hash);
  	if (root_bone_iter.isValid()) {
  		const int root_bone_idx = root_bone_iter.value();
  		const Model::Bone& bone = ctx.model->getBone(root_bone_idx);
  		if (m_flags.isSet(Flags::XZ_ROOT_MOTION)) {
  			root_motion->rot = Quat::IDENTITY;
  			root_motion->pos = bone.transform.rot.rotate(Vec3(root_motion->pos.x, 0, root_motion->pos.z));
  		}
  		else {
  			root_motion->rot = bone.transform.rot * root_motion->rot * bone.transform.rot.conjugated();
  			root_motion->pos = bone.transform.rot.rotate(root_motion->pos);
  		}
  	}
  }
  
  void Controller::getPose(RuntimeContext& ctx, Ref<Pose> pose) {
  	ASSERT(&ctx.controller == this);
  	ctx.input_runtime.set(ctx.data.data(), ctx.data.size());
  	
  	LocalRigidTransform root_bind_pose;
  	auto root_bone_iter = ctx.model->getBoneIndex(ctx.root_bone_hash);
  	if (root_bone_iter.isValid()) {
  		const int root_bone_idx = root_bone_iter.value();
  		root_bind_pose.pos = pose->positions[root_bone_idx];
  		root_bind_pose.rot = pose->rotations[root_bone_idx];
  	}
  	
  	m_root->getPose(ctx, 1.f, pose, 0xffFFffFF);
  	
  	// TODO this should be in AnimationNode
  	if (root_bone_iter.isValid()) {
  		const int root_bone_idx = root_bone_iter.value();
  		if (m_flags.isSet(Flags::XZ_ROOT_MOTION)) {
  			pose->positions[root_bone_idx].x = root_bind_pose.pos.x;
  			pose->positions[root_bone_idx].z = root_bind_pose.pos.z;
  		}
  		else {
  			pose->positions[root_bone_idx] = root_bind_pose.pos;
  			pose->rotations[root_bone_idx] = root_bind_pose.rot;
  		}
  	}
  }
  
  struct Header {
  	enum class Version : u32 {
  		LATEST
  	};
  
  	u32 magic = MAGIC;
  	Version version = Version::LATEST;
  
  	static constexpr u32 MAGIC = '_LAC';
  };
  
  void Controller::serialize(OutputMemoryStream& stream) {
  	Header header;
  	stream.write(header);
  	stream.write(m_flags);
  	stream.write(m_root_motion_bone);
  	for (const InputDecl::Input& input : m_inputs.inputs) {
  		if (input.type == InputDecl::Type::EMPTY) continue;
  		stream.write(input.type);
  		stream.write(input.name);
  	}
  	stream.write(InputDecl::EMPTY);
  	stream.write((u32)m_animation_slots.size());
  	for (const String& slot : m_animation_slots) {
  		stream.writeString(slot.c_str());
  	}
  	stream.write((u32)m_animation_entries.size());
  	for (const AnimationEntry& entry : m_animation_entries) {
  		stream.write(entry.slot);
  		stream.write(entry.set);
  		stream.writeString(entry.animation ? entry.animation->getPath().c_str() : "");
  	}
  	stream.write(m_ik);
  	stream.write(m_ik_count);
  	m_root->serialize(stream);
  }
  
  bool Controller::deserialize(InputMemoryStream& stream) {
  	Header header;
  	stream.read(header);
  	stream.read(m_flags);
  	stream.read(m_root_motion_bone);
  	InputDecl::Type type;
  	stream.read(type);
  	while (type != InputDecl::EMPTY) {
  		const int idx = m_inputs.addInput();
  		m_inputs.inputs[idx].type = type;
  		stream.read(m_inputs.inputs[idx].name);
  		stream.read(type);
  	}
  	m_inputs.recalculateOffsets();
  	if (header.magic != Header::MAGIC) {
  		logError("Animation") << "Invalid animation controller file " << getPath();
  		return false;
  	}
  	if (header.version > Header::Version::LATEST) {
  		logError("Animation") << "Version of animation controller " << getPath() << " is not supported";
  		return false;
  	}
  	initEmpty();
  	const u32 slots_count = stream.read<u32>();
  	m_animation_slots.reserve(slots_count);
  	for (u32 i = 0; i < slots_count; ++i) {
  		String& slot = m_animation_slots.emplace(m_allocator);
  		const char* tmp = stream.readString();
  		slot = tmp;
  	}
  
  	const u32 entries_count = stream.read<u32>();
  	m_animation_entries.reserve(entries_count);
  	for (u32 i = 0; i < entries_count; ++i) {
  		AnimationEntry& entry = m_animation_entries.emplace();
  		stream.read(entry.slot);
  		stream.read(entry.set);
  		const char* path = stream.readString();
  		entry.animation = path[0] ? m_resource_manager.getOwner().load<Animation>(Path(path)) : nullptr;
  	}
  
  	stream.read(m_ik);
  	stream.read(m_ik_count);
  	m_root->deserialize(stream, *this);
  	return true;
  }
  
  
  } // ns Lumix::Anim