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

#pragma once

#include "core.h"

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

typedef struct b2Shape b2Shape;
typedef struct b2World b2World;

enum b2ContactFlags
{
	// Set when the solid shapes are touching.
	b2_contactTouchingFlag = 0x00000001,

	// Contact has a hit event
	b2_contactHitEventFlag = 0x00000002,

	// One of the shapes is a sensor
	b2_contactSensorFlag = 0x0000004,

	// Set when a sensor is touching
	// todo this is not used, perhaps have b2Body_GetSensorContactData()
	b2_contactSensorTouchingFlag = 0x00000008,

	// This contact wants sensor events
	b2_contactEnableSensorEvents = 0x00000010,

	// This contact wants contact events
	b2_contactEnableContactEvents = 0x00000020,
};

// A contact edge is used to connect bodies and contacts together
// in a contact graph where each body is a node and each contact
// is an edge. A contact edge belongs to a doubly linked list
// maintained in each attached body. Each contact has two contact
// edges, one for each attached body.
typedef struct b2ContactEdge
{
	int bodyId;
	int prevKey;
	int nextKey;
} b2ContactEdge;

// Cold contact data. Used as a persistent handle and for persistent island
// connectivity.
typedef struct b2Contact
{
	// index of simulation set stored in b2World
	// B2_NULL_INDEX when slot is free
	int setIndex;

	// index into the constraint graph color array
	// B2_NULL_INDEX for non-touching or sleeping contacts
	// B2_NULL_INDEX when slot is free
	int colorIndex;

	// contact index within set or graph color
	// B2_NULL_INDEX when slot is free
	int localIndex;

	b2ContactEdge edges[2];
	int shapeIdA;
	int shapeIdB;

	// A contact only belongs to an island if touching, otherwise B2_NULL_INDEX.
	int islandPrev;
	int islandNext;
	int islandId;

	int contactId;

	// b2ContactFlags
	uint32_t flags;

	bool isMarked;
} b2Contact;

// Shifted to be distinct from b2ContactFlags
enum b2ContactSimFlags
{
	// Set when the shapes are touching, including sensors
	b2_simTouchingFlag = 0x00010000,

	// This contact no longer has overlapping AABBs
	b2_simDisjoint = 0x00020000,

	// This contact started touching
	b2_simStartedTouching = 0x00040000,

	// This contact stopped touching
	b2_simStoppedTouching = 0x00080000,

	// This contact has a hit event
	b2_simEnableHitEvent = 0x00100000,

	// This contact wants pre-solve events
	b2_simEnablePreSolveEvents = 0x00200000,
};

/// The class manages contact between two shapes. A contact exists for each overlapping
/// AABB in the broad-phase (except if filtered). Therefore a contact object may exist
/// that has no contact points.
typedef struct b2ContactSim
{
	int contactId;

#if B2_VALIDATE
	int bodyIdA;
	int bodyIdB;
#endif

	int bodySimIndexA;
	int bodySimIndexB;

	int shapeIdA;
	int shapeIdB;

	float invMassA;
	float invIA;

	float invMassB;
	float invIB;

	b2Manifold manifold;

	// Mixed friction and restitution
	float friction;
	float restitution;

	// todo for conveyor belts
	float tangentSpeed;

	// b2ContactSimFlags
	uint32_t simFlags;

	b2DistanceCache cache;
} b2ContactSim;

void b2InitializeContactRegisters( void );

void b2CreateContact( b2World* world, b2Shape* shapeA, b2Shape* shapeB );
void b2DestroyContact( b2World* world, b2Contact* contact, bool wakeBodies );

b2ContactSim* b2GetContactSim( b2World* world, b2Contact* contact );

bool b2ShouldShapesCollide( b2Filter filterA, b2Filter filterB );

bool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, b2Transform transformA, b2Vec2 centerOffsetA,
					  b2Shape* shapeB, b2Transform transformB, b2Vec2 centerOffsetB );
