
#pragma once

#include "BinaryReader.h"

// All of this is temporary.

class Vector3
{
public:
	float x;
	float y;
	float z;

	static Vector3 read(BinaryReader &binaryReader) {
		Vector3 newObj;
		newObj.x = binaryReader.ReadSingle();
		newObj.y = binaryReader.ReadSingle();
		newObj.z = binaryReader.ReadSingle();
		return newObj;
	}
};

class Frame
{
public:
	Vector3 m_fOrigin;
	float qw;
	float qx;
	float qy;
	float qz;

	static Frame read(BinaryReader &binaryReader) {
		Frame newObj;
		newObj.m_fOrigin = Vector3::read(binaryReader);
		newObj.qw = binaryReader.ReadSingle();
		newObj.qx = binaryReader.ReadSingle();
		newObj.qy = binaryReader.ReadSingle();
		newObj.qz = binaryReader.ReadSingle();
		return newObj;
	}
};

class Position
{
public:
	uint32_t objcell_id;
	Frame frame;

	static Position read(BinaryReader &binaryReader) {
		Position newObj;
		newObj.objcell_id = binaryReader.ReadUInt32();
		newObj.frame = Frame::read(binaryReader);
		return newObj;
	}

	static Position readOrigin(BinaryReader &binaryReader) {
		Position newObj;
		newObj.objcell_id = binaryReader.ReadUInt32();
		newObj.frame.m_fOrigin = Vector3::read(binaryReader);
		return newObj;
	}
};

class ChildInfo
{
public:
	uint32_t id;
	uint32_t location_id;

	static ChildInfo read(BinaryReader &binaryReader) {
		ChildInfo newObj;
		newObj.id = binaryReader.ReadUInt32();
		newObj.location_id = binaryReader.ReadUInt32();
		return newObj;
	}
};

class PhysicsDesc
{
public:
	enum PhysicsDescInfo {
		CSetup = (1 << 0), // 0x1
		MTABLE = (1 << 1), // 0x2
		VELOCITY = (1 << 2), // 0x4
		ACCELERATION = (1 << 3), // 0x8
		OMEGA = (1 << 4), // 0x10
		PARENT = (1 << 5), // 0x20
		CHILDREN = (1 << 6), // 0x40
		OBJSCALE = (1 << 7), // 0x80
		FRICTION = (1 << 8), // 0x100
		ELASTICITY = (1 << 9), // 0x200
		TIMESTAMPS = (1 << 10), // 0x400
		STABLE = (1 << 11), // 0x800
		PETABLE = (1 << 12), // 0x1000
		DEFAULT_SCRIPT = (1 << 13), // 0x2000
		DEFAULT_SCRIPT_INTENSITY = (1 << 14), // 0x4000
		POSITION = (1 << 15), // 0x8000
		MOVEMENT = (1 << 16), // 0x10000
		ANIMFRAME_ID = (1 << 17), // 0x20000
		TRANSLUCENCY = (1 << 18) // 0x40000
	};

	DWORD bitfield;
	PhysicsState state;
	BYTE *movement_buffer;
	DWORD movement_buffer_length;
	DWORD autonomous_movement;
	DWORD animframe_id;
	Position pos;
	DWORD mtable_id; // These are tag ids like animpartchange
	DWORD stable_id;
	DWORD phstable_id;
	DWORD setup_id;
	DWORD parent_id;
	DWORD location_id;
	std::list<ChildInfo> children;
	float object_scale;
	float friction;
	float elasticity;
	float translucency;
	Vector3 velocity;
	Vector3 acceleration;
	Vector3 omega;
	PScriptType default_script;
	float default_script_intensity;
	uint16_t timestamps[9];

	PhysicsDesc()
	{
		state = (PhysicsState)0;
		movement_buffer = NULL;
		movement_buffer_length = 0;
		autonomous_movement = 0;
		animframe_id = 0;
		
		mtable_id = 0;
		stable_id = 0;
		phstable_id = 0;
		setup_id = 0;
		parent_id = 0;
		location_id = 0;
		object_scale = 1.0f;
		friction = 0.0f;
		elasticity = 0.0f;
		translucency = 0.0f;
		default_script = (PScriptType)0;
		default_script_intensity = 0.0f;
	}

	~PhysicsDesc()
	{
		freeData();
	}

	void freeData()
	{
		if (movement_buffer)
		{
			delete[] movement_buffer;
			movement_buffer = NULL;
		}
	}

	void Unpack(BinaryReader &binaryReader) {
		PhysicsDesc &newObj = *this;
		newObj.bitfield = binaryReader.ReadUInt32();
		newObj.state = (PhysicsState)binaryReader.ReadUInt32();

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::MOVEMENT) != 0) {
			DWORD buff_length = binaryReader.ReadUInt32();
			if (buff_length > 0)
			{
				if (newObj.movement_buffer)
					delete[] newObj.movement_buffer;
				newObj.movement_buffer = new BYTE[buff_length];
				memcpy(newObj.movement_buffer, binaryReader.ReadArray((int)buff_length), buff_length);
				newObj.movement_buffer_length = buff_length;
			}

			newObj.autonomous_movement = binaryReader.ReadUInt32();
		}
		else if ((newObj.bitfield & (DWORD)PhysicsDescInfo::ANIMFRAME_ID) != 0) {
			newObj.animframe_id = binaryReader.ReadUInt32();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::POSITION) != 0) {
			newObj.pos = Position::read(binaryReader);
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::MTABLE) != 0) {
			newObj.mtable_id = binaryReader.ReadUInt32();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::STABLE) != 0) {
			newObj.stable_id = binaryReader.ReadUInt32();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::PETABLE) != 0) {
			newObj.phstable_id = binaryReader.ReadUInt32();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::CSetup) != 0) {
			newObj.setup_id = binaryReader.ReadUInt32();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::PARENT) != 0) {
			newObj.parent_id = binaryReader.ReadUInt32();
			newObj.location_id = binaryReader.ReadUInt32();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::CHILDREN) != 0) {
			DWORD num_children = binaryReader.ReadUInt32();
			for (DWORD i = 0; i < num_children; ++i) {
				newObj.children.push_back(ChildInfo::read(binaryReader));
			}
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::OBJSCALE) != 0) {
			newObj.object_scale = binaryReader.ReadSingle();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::FRICTION) != 0) {
			newObj.friction = binaryReader.ReadSingle();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::ELASTICITY) != 0) {
			newObj.elasticity = binaryReader.ReadSingle();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::TRANSLUCENCY) != 0) {
			newObj.translucency = binaryReader.ReadSingle();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::VELOCITY) != 0) {
			newObj.velocity.x = binaryReader.ReadSingle();
			newObj.velocity.y = binaryReader.ReadSingle();
			newObj.velocity.z = binaryReader.ReadSingle();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::ACCELERATION) != 0) {
			newObj.acceleration.x = binaryReader.ReadSingle();
			newObj.acceleration.y = binaryReader.ReadSingle();
			newObj.acceleration.z = binaryReader.ReadSingle();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::OMEGA) != 0) {
			newObj.omega.x = binaryReader.ReadSingle();
			newObj.omega.y = binaryReader.ReadSingle();
			newObj.omega.z = binaryReader.ReadSingle();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::DEFAULT_SCRIPT) != 0) {
			newObj.default_script = (PScriptType)binaryReader.ReadUInt32();
		}

		if ((newObj.bitfield & (DWORD)PhysicsDescInfo::DEFAULT_SCRIPT_INTENSITY) != 0) {
			newObj.default_script_intensity = binaryReader.ReadSingle();
		}

		for (int i = 0; i < 9; ++i) {
			newObj.timestamps[i] = binaryReader.ReadUInt16();
		}

		binaryReader.ReadAlign();
	}
};

