// SPDX-FileCopyrightText: 2023 Erin Catto
// SPDX-License-Identifier: MIT

#pragma once

#include "array.h"

#include "box2d/math_functions.h"
#include "box2d/types.h"

// Length of body debug name
#define B2_NAME_LENGTH 32

typedef struct b2World b2World;

enum b2BodyFlags
{
	// This body has fixed translation along the x-axis
	b2_lockLinearX = 0x00000001,

	// This body has fixed translation along the y-axis
	b2_lockLinearY = 0x00000002,

	// This body has fixed rotation
	b2_lockAngularZ = 0x00000004,

	// This flag is used for debug draw
	b2_isFast = 0x00000008,

	// This dynamic body does a final CCD pass against all body types, but not other bullets
	b2_isBullet = 0x00000010,

	// This body was speed capped in the current time step
	b2_isSpeedCapped = 0x00000020,
	
	// This body had a time of impact event in the current time step
	b2_hadTimeOfImpact = 0x00000040,

	// This body has no limit on angular velocity
	b2_allowFastRotation = 0x00000080,

	// This body need's to have its AABB increased
	b2_enlargeBounds = 0x00000100,

	// This body is dynamic so the solver should write to it.
	// This prevents writing to kinematic bodies that causes a multithreaded sharing
	// cache coherence problem even when the values are not changing.
	// Used for b2BodyState flags.
	b2_dynamicFlag = 0x00000200,

	// Flag to indicate the user has used the updateBodyMass option to defer mass
	// computation but b2Body_ApplyMassFromShapes was not called before the world step.
	b2_dirtyMass = 0x00000400,

	// All lock flags
	b2_allLocks = b2_lockAngularZ | b2_lockLinearX | b2_lockLinearY,
};

// Body organizational details that are not used in the solver.
typedef struct b2Body
{
	char name[B2_NAME_LENGTH];

	void* userData;

	// index of solver set stored in b2World
	// may be B2_NULL_INDEX
	int setIndex;

	// body sim and state index within set
	// may be B2_NULL_INDEX
	int localIndex;

	// [31 : contactId | 1 : edgeIndex]
	int headContactKey;
	int contactCount;

	// todo maybe move this to the body sim
	int headShapeId;
	int shapeCount;

	int headChainId;

	// [31 : jointId | 1 : edgeIndex]
	int headJointKey;
	int jointCount;

	// All enabled dynamic and kinematic bodies are in an island.
	int islandId;

	// doubly-linked island list
	int islandPrev;
	int islandNext;

	float mass;

	// Rotational inertia about the center of mass.
	float inertia;

	float sleepThreshold;
	float sleepTime;

	// this is used to adjust the fellAsleep flag in the body move array
	int bodyMoveIndex;

	int id;

	// b2BodyFlags
	uint32_t flags;

	b2BodyType type;

	// This is monotonically advanced when a body is allocated in this slot
	// Used to check for invalid b2BodyId
	uint16_t generation;

	// todo move into flags
	bool enableSleep;
} b2Body;

// Body State
// The body state is designed for fast conversion to and from SIMD via scatter-gather.
// Only awake dynamic and kinematic bodies have a body state.
// This is used in the performance critical constraint solver
//
// The solver operates on the body state. The body state array does not hold static bodies. Static bodies are shared
// across worker threads. It would be okay to read their states, but writing to them would cause cache thrashing across
// workers, even if the values don't change.
// This causes some trouble when computing anchors. I rotate joint anchors using the body rotation every sub-step. For static
// bodies the anchor doesn't rotate. Body A or B could be static and this can lead to lots of branching. This branching
// should be minimized.
//
// Solution 1:
// Use delta rotations. This means anchors need to be prepared in world space. The delta rotation for static bodies will be
// identity using a dummy state. Base separation and angles need to be computed. Manifolds will be behind a frame, but that
// is probably best if bodies move fast.
//
// Solution 2:
// Use full rotation. The anchors for static bodies will be in world space while the anchors for dynamic bodies will be in local
// space. Potentially confusing and bug prone.
//
// Note:
// I rotate joint anchors each sub-step but not contact anchors. Joint stability improves a lot by rotating joint anchors
// according to substep progress. Contacts have reduced stability when anchors are rotated during substeps, especially for
// round shapes.

// 32 bytes
typedef struct b2BodyState
{
	b2Vec2 linearVelocity; // 8
	float angularVelocity; // 4

	// b2BodyFlags
	// Important flags: locking, dynamic
	uint32_t flags; // 4

	// Using delta position reduces round-off error far from the origin
	b2Vec2 deltaPosition; // 8

	// Using delta rotation because I cannot access the full rotation on static bodies in
	// the solver and must use zero delta rotation for static bodies (c,s) = (1,0)
	b2Rot deltaRotation; // 8
} b2BodyState;

// Identity body state, notice the deltaRotation is {1, 0}
static const b2BodyState b2_identityBodyState = { { 0.0f, 0.0f }, 0.0f, 0, { 0.0f, 0.0f }, { 1.0f, 0.0f } };

// Body simulation data used for integration of position and velocity
// Transform data used for collision and solver preparation.
typedef struct b2BodySim
{
	// transform for body origin
	b2Transform transform;

	// center of mass position in world space
	b2Vec2 center;

	// previous rotation and COM for TOI
	b2Rot rotation0;
	b2Vec2 center0;

	// location of center of mass relative to the body origin
	b2Vec2 localCenter;

	b2Vec2 force;
	float torque;

	// inverse inertia
	float invMass;
	float invInertia;

	float minExtent;
	float maxExtent;
	float linearDamping;
	float angularDamping;
	float gravityScale;

	// Index of b2Body
	int bodyId;

	// b2BodyFlags
	uint32_t flags;
} b2BodySim;

// Get a validated body from a world using an id.
b2Body* b2GetBodyFullId( b2World* world, b2BodyId bodyId );

b2Transform b2GetBodyTransformQuick( b2World* world, b2Body* body );
b2Transform b2GetBodyTransform( b2World* world, int bodyId );

// Create a b2BodyId from a raw id.
b2BodyId b2MakeBodyId( b2World* world, int bodyId );

bool b2ShouldBodiesCollide( b2World* world, b2Body* bodyA, b2Body* bodyB );

b2BodySim* b2GetBodySim( b2World* world, b2Body* body );
b2BodyState* b2GetBodyState( b2World* world, b2Body* body );

// careful calling this because it can invalidate body, state, joint, and contact pointers
bool b2WakeBody( b2World* world, b2Body* body );

void b2UpdateBodyMassData( b2World* world, b2Body* body );

static inline b2Sweep b2MakeSweep( const b2BodySim* bodySim )
{
	b2Sweep s;
	s.c1 = bodySim->center0;
	s.c2 = bodySim->center;
	s.q1 = bodySim->rotation0;
	s.q2 = bodySim->transform.q;
	s.localCenter = bodySim->localCenter;
	return s;
}

// Define inline functions for arrays
B2_ARRAY_INLINE( b2Body, b2Body )
B2_ARRAY_INLINE( b2BodySim, b2BodySim )
B2_ARRAY_INLINE( b2BodyState, b2BodyState )
