#ifndef GAME_OBJECT_H
#define GAME_OBJECT_H

#include <string>

#include "engine/ientity.h"
#include "engine/camera.h"
#include "engine/physics/physicsworld.h"

#include "render/model.h"
#include "render/modelsystem.h"

#include "game_lua_help.h"

enum EMovmentDir
{
	EMovementDir_None = 1 << 0,
	EMovementDir_Forward = 1 << 1,
	EMovementDir_Backward = 1 << 2,
	EMovementDir_Left = 1 << 3,
	EMovementDir_Right = 1 << 4,
	EMovementDir_Jump = 1 << 5,
};

struct ph_motion_state : public btMotionState {
	btRigidBody* m_body;
	btTransform  m_transform;

	ph_motion_state() : m_body(nullptr) {}
	ph_motion_state(btRigidBody* body) : m_body(body) {}

	void setBody(btRigidBody* body) { m_body = body; }

	// getWorldTransform() relays transform from external logic to Bullet.
	// It is called by Bullet in two places:
	// (1) Once when the Body is created and added to the world.
	// (2) Once per substep iff the Body is Kinematic and active.
	//
	void getWorldTransform(btTransform& worldTrans) const {
		// Note: worldTrans is passed by reference
		// It is our duty here to copy into it the correct transform as known
		// to external logic and Bullet will harvest·
		// ·
		// m_gameObject->getWorldTransform( worldTrans );
	}

	// setWorldTransform() relays Body transform from the Bullet simulation to the GameObject
	// and is called once per substep iff the Body is Dynamic and active.
	//
	void setWorldTransform(const btTransform& worldTrans) {
		// It is our duty here to apply worldTrans to GameObject.
		// Note: worldTrans is NOT necessarily the Body's transform as known to Bullet.
		// It is an extrapolated transform based on the Body's state at the
		// end of the last substep... extrapolated forward into the future (e.g. the remainder
		// not yet stepped) according the current Body velocities.
		//
		m_transform = worldTrans;
	}
};


class Entity : public IEntityBase
{
public:
	Entity();
	virtual ~Entity();

	virtual void Update(float dt);
	virtual void Render();
	virtual void OnCollide(IEntityBase* other);

	// Game entity extensions
	const std::string& GetName();
	void SetName(const std::string& name);

	virtual void LoadModel(const char* filename);
	void SetVisible(bool visible);
	bool GetVisible();

	SkeletonInstance* GetSkeletonInstance();
	void UpdateSkeleton(float dt);

	void Init();

	// Game entity physics
	void CreateTestBody();

	void CreateBoxBody(const glm::vec3& size, float mass, float friction, float damping, bool is_trigger, bool use_parameters);
	void CreateSphereBody(float radius, float mass, float friction, float damping, bool is_trigger, bool use_parameters);
	void CreateBodyPrivate(float mass, float friction, float damping, bool is_trigger, bool use_parameters);

	void UpdateBody();
	void UpdateBodyDirty();
	void ForceUpdateBodyTranslation();

	void SetBodyFilterGroup(PhysicsFilter group);
	void SetBodyFilterMask(int mask);

	// Game entity lua bindings

	void InitFromTable(LuaPlus::LuaObject& _object);
	void InitLuaCallbacks();
	void RegisterBaseFunctions();
	virtual void RegisterFunctions();

	// Game entity lua wrappers
	void Help_Translate(float x, float y, float z);
	void Help_SetPosition(float x, float y, float z);
	void Help_SetRotation(float x, float y, float z);
	void Help_SetRotationFromVectors(const glm::vec3& front, const glm::vec3& right, const glm::vec3& up);
	void Help_SetVelocity(float x, float y, float z);

	int Lua_LoadModel(LuaPlus::LuaState* state);
	int Lua_Translate(LuaPlus::LuaState* state);
	int Lua_SetPosition(LuaPlus::LuaState* state);
	int Lua_GetPosition(LuaPlus::LuaState* state);
	int Lua_GetRotation(LuaPlus::LuaState* state);
	int Lua_SetRotation(LuaPlus::LuaState* state);
	int Lua_SetRotationFromVectors(LuaPlus::LuaState* state);
	int Lua_GetClassname(LuaPlus::LuaState* state);
	int Lua_GetID(LuaPlus::LuaState* state);
	int Lua_UpdateTransform(LuaPlus::LuaState* state);
	int Lua_MarkForDelete(LuaPlus::LuaState* state);
	int Lua_SetName(LuaPlus::LuaState* state);
	int Lua_GetName(LuaPlus::LuaState* state);

	int Lua_SetVelocity(LuaPlus::LuaState* state);
	int Lua_GetVelocity(LuaPlus::LuaState* state);
	int Lua_HasRigidBody(LuaPlus::LuaState* state);
	int Lua_CreateBoxBody(LuaPlus::LuaState* state);
	int Lua_CreateSphereBody(LuaPlus::LuaState* state);
	int Lua_ForceBodyTransformUpdate(LuaPlus::LuaState* state);
	int Lua_SetBodyFilterGroup(LuaPlus::LuaState* state);
	int Lua_SetBodyFilterMask(LuaPlus::LuaState* state);
	int Lua_GetBodyFilterGroup(LuaPlus::LuaState* state);
	int Lua_GetBodyFilterMask(LuaPlus::LuaState* state);

	int Lua_FindAnimation(LuaPlus::LuaState* state);
	int Lua_PlayAnimation(LuaPlus::LuaState* state);
	int Lua_StopAnimation(LuaPlus::LuaState* state);
	int Lua_GetCurrentAnimation(LuaPlus::LuaState* state);
	int Lua_GetCurrentAnimationTime(LuaPlus::LuaState* state);
	int Lua_GetAnimationTime(LuaPlus::LuaState* state);

	const LuaPlus::LuaObject& GetLuaObject();

protected:
	std::string m_name;

	Model* m_model;
	SkeletonInstance* m_skeleton;

	LuaPlus::LuaObject m_luaObject;
	LuaPlus::LuaObject m_onInitFunction;
	LuaPlus::LuaObject m_onShutdownFunction;
	LuaPlus::LuaObject m_onUpdateFunction;
	LuaPlus::LuaObject m_onCollideFunction;

protected:
	btCollisionShape* m_shape;
	btRigidBody* m_rigidBody;
	ph_motion_state m_ph_motion_state;
	float m_mass;
	bool m_bodyDirty;
};

class WeaponBase : public Entity
{
public:
	WeaponBase();
	~WeaponBase();

	virtual void Update(float dt);

	void Fire(const glm::vec3& direction, float damage);
};

class TriggerBase : public Entity
{
public:
	TriggerBase();
	~TriggerBase();

	virtual void Render();
};

#endif // !GAME_OBJECT_H
