#pragma once


class AudioFile;
class ShaderProgram;
class ModelNode;
class WEntity;



struct PhysicsCreationInfo {
	bool physics_enabled;
	JPH::Shape* collision_shape = nullptr;

	JPH::EMotionType motion_type;
	JPH::ObjectLayer object_layer;

	JPH::EAllowedDOFs allowed_dofs;


	glm::quat rotation = glm::quat(1, 0, 0, 0);
	glm::bvec3 locked_rot = glm::bvec3(true, true, true);
	glm::bvec3 locked_pos = glm::bvec3(false, false, false);

	float mass = 0.5;
	float gravity_factor = 1.0;


	JPH::RVec3 position;
	// Layers::MOVING;


	PhysicsCreationInfo();

	PhysicsCreationInfo set_2d_dof();

	PhysicsCreationInfo set_physics_enabled(bool _physics_enabled);

	PhysicsCreationInfo set_static();

	PhysicsCreationInfo set_position(JPH::RVec3 pos);

	PhysicsCreationInfo set_mass(float mass);

	PhysicsCreationInfo set_gravity_factor(float gravity_factor);

	PhysicsCreationInfo remove_col_shape();

	PhysicsCreationInfo set_col_shape(JPH::Shape* col_shape);
};

struct WCollision {
	WEntity* me = nullptr;
	WEntity* other = nullptr;
	JPH::CollisionEstimationResult collision_res;
	glm::vec3 pos;
	WCollision();
	WCollision(WEntity* me, WEntity* other, JPH::CollisionEstimationResult collision_res, glm::vec3 pos);
	// JPH::ContactManifold contact_manifold;
};




enum class ObjType {
	Player,
	EnemyBlock,
	Wall,
	Enemy,
};
enum class EnemyState {
	Patrolling,
	Alerted,
	BackToPatrol
};


// } else if (c == 'B' || c == 'P' || c == 'H' || c == 'V') {
						
enum class EnemyType: char {
	Bababooey = 'B',
	Pipefalling = 'P',
	Vineboom = 'V',
	Huhcat = 'H',
};

struct PlayerProps {
	glm::ivec2 pos;
	glm::ivec2 attention_tar;
	glm::ivec2 patrol_dir;
	glm::ivec2 spawn_pos;
	EnemyState enemy_state;
	EnemyType enemy_type;
};
struct EnemyBlockProps {
	glm::vec2 pos;
};
union ObjProps {
	PlayerProps ppprops;
	EnemyBlockProps enemy_block_props;
};

//
// enum class ObjType {
// 	Player,
// 	EnemyBlock,
// 	IcosaTest
// };
// struct PlayerProps {
// 	glm::vec2 pos;
// 	glm::vec4 amog;
// 	std::array<float,10> amogus;
// };
// struct EnemyBlockProps {
// 	glm::vec2 pos;
// };
// union ObjProps {
// 	PlayerProps player_props;
// 	EnemyBlockProps enemy_block_props;
// };
//
class WEntity {
public:
	ObjType obj_type;
	ObjProps props;

	int generation = 0;

	glm::vec3 position = glm::vec3(0,0,0);
	glm::quat rotation = glm::quat(1,0,0,0);
	// glm::quat rotation;
	glm::vec3 scale = glm::vec3(1,1,1);

	glm::mat4 matrix = glm::mat4(1);
	glm::mat4 prev_matrix = glm::mat4(1);
	glm::mat4 rot_matrix = glm::mat4(1);

	// WArray<WCollision*> temp_collisions = WArray<WCollision*>({}, 10);

	bool physics_active = false;

	JPH::BodyID jph_body_id;
	JPH::Body* jph_body = nullptr;

	std::function<void(WCollision)> on_collision = [](WCollision col){};
	std::function<void(WEntity*, float, float, float, float, float, float)> render_function;
	std::function<void()> tick_function = [](){};

	float col_force;
	std::optional<AudioFile*> col_sound = std::nullopt;

	std::optional<JPH::BodyCreationSettings> bcs;

	int alloc_slot = 0;
	long game_obj_id = 0;
	// std::shared_ptr<JPH::Shape> collision_shape;
	JPH::Shape* collision_shape = nullptr;
	std::optional<JPH::Shape*> creation_collision_shape = nullptr;

	operator WObj<WEntity>() {
		return WObj(this);
	}

	glm::vec3 get_pos();
	glm::quat get_rot();
	glm::vec3 get_vel();
	void set_scale(glm::vec3 scale);
	void set_rot(glm::quat rot);
	void set_vel(glm::vec3 vel);
	void set_pos(glm::vec3 pos);


	private:
		void init_game_object( std::optional<PhysicsCreationInfo> physics_creation_info );

public:
	WEntity() = default;
	WEntity(
		ModelNode* model_node,
		ShaderProgram* shader_prog,
        std::optional<PhysicsCreationInfo> physics_creation_info = std::optional<PhysicsCreationInfo>()
	);

	ModelNode* model_node = nullptr;
	ShaderProgram* shader_prog = nullptr;

	void draw(float arg_a = 0, float arg_b = 0, float arg_c = 0, float arg_d = 0, float arg_e = 0, float arg_f = 0);
	void recalc_matrices();

	void destroy();
	WEntity(
		ShaderProgram* shader_prog,
        std::optional<PhysicsCreationInfo> physics_creation_info = std::optional<PhysicsCreationInfo>()
	);
	WEntity(std::optional<PhysicsCreationInfo> physics_creation_info);


};
