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

#include "draw.h"
#include "sample.h"
#include "settings.h"

#include "box2d/box2d.h"

#include <imgui.h>

class BodyType : public Sample
{
public:
	explicit BodyType( Settings& settings )
		: Sample( settings )
	{
		if ( settings.restart == false )
		{
			g_camera.m_center = { 0.8f, 6.4f };
			g_camera.m_zoom = 25.0f * 0.4f;
		}

		m_type = b2_dynamicBody;
		m_isEnabled = true;

		b2BodyId groundId = b2_nullBodyId;
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			groundId = b2CreateBody( m_worldId, &bodyDef );

			b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			b2CreateSegmentShape( groundId, &shapeDef, &segment );
		}

		// Define attachment
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = b2_dynamicBody;
			bodyDef.position = { -2.0f, 3.0f };
			m_attachmentId = b2CreateBody( m_worldId, &bodyDef );

			b2Polygon box = b2MakeBox( 0.5f, 2.0f );
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			shapeDef.density = 1.0f;
			b2CreatePolygonShape( m_attachmentId, &shapeDef, &box );
		}

		// Define second attachment
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = m_type;
			bodyDef.isEnabled = m_isEnabled;
			bodyDef.position = { 3.0f, 3.0f };
			m_secondAttachmentId = b2CreateBody( m_worldId, &bodyDef );

			b2Polygon box = b2MakeBox( 0.5f, 2.0f );
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			shapeDef.density = 1.0f;
			b2CreatePolygonShape( m_secondAttachmentId, &shapeDef, &box );
		}

		// Define platform
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = m_type;
			bodyDef.isEnabled = m_isEnabled;
			bodyDef.position = { -4.0f, 5.0f };
			m_platformId = b2CreateBody( m_worldId, &bodyDef );

			b2Polygon box = b2MakeOffsetBox( 0.5f, 4.0f, { 4.0f, 0.0f }, 0.5f * b2_pi );

			b2ShapeDef shapeDef = b2DefaultShapeDef();
			shapeDef.friction = 0.6f;
			shapeDef.density = 2.0f;
			b2CreatePolygonShape( m_platformId, &shapeDef, &box );

			b2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef();
			b2Vec2 pivot = { -2.0f, 5.0f };
			revoluteDef.bodyIdA = m_attachmentId;
			revoluteDef.bodyIdB = m_platformId;
			revoluteDef.localAnchorA = b2Body_GetLocalPoint( m_attachmentId, pivot );
			revoluteDef.localAnchorB = b2Body_GetLocalPoint( m_platformId, pivot );
			revoluteDef.maxMotorTorque = 50.0f;
			revoluteDef.enableMotor = true;
			b2CreateRevoluteJoint( m_worldId, &revoluteDef );

			pivot = { 3.0f, 5.0f };
			revoluteDef.bodyIdA = m_secondAttachmentId;
			revoluteDef.bodyIdB = m_platformId;
			revoluteDef.localAnchorA = b2Body_GetLocalPoint( m_secondAttachmentId, pivot );
			revoluteDef.localAnchorB = b2Body_GetLocalPoint( m_platformId, pivot );
			revoluteDef.maxMotorTorque = 50.0f;
			revoluteDef.enableMotor = true;
			b2CreateRevoluteJoint( m_worldId, &revoluteDef );

			b2PrismaticJointDef prismaticDef = b2DefaultPrismaticJointDef();
			b2Vec2 anchor = { 0.0f, 5.0f };
			prismaticDef.bodyIdA = groundId;
			prismaticDef.bodyIdB = m_platformId;
			prismaticDef.localAnchorA = b2Body_GetLocalPoint( groundId, anchor );
			prismaticDef.localAnchorB = b2Body_GetLocalPoint( m_platformId, anchor );
			prismaticDef.localAxisA = { 1.0f, 0.0f };
			prismaticDef.maxMotorForce = 1000.0f;
			prismaticDef.motorSpeed = 0.0f;
			prismaticDef.enableMotor = true;
			prismaticDef.lowerTranslation = -10.0f;
			prismaticDef.upperTranslation = 10.0f;
			prismaticDef.enableLimit = true;

			b2CreatePrismaticJoint( m_worldId, &prismaticDef );

			m_speed = 3.0f;
		}

		// Create a payload
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = b2_dynamicBody;
			bodyDef.position = { -3.0f, 8.0f };
			b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );

			b2Polygon box = b2MakeBox( 0.75f, 0.75f );

			b2ShapeDef shapeDef = b2DefaultShapeDef();
			shapeDef.friction = 0.6f;
			shapeDef.density = 2.0f;

			b2CreatePolygonShape( bodyId, &shapeDef, &box );
		}

		// Create a second payload
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = m_type;
			bodyDef.isEnabled = m_isEnabled;
			bodyDef.position = { 2.0f, 8.0f };
			m_secondPayloadId = b2CreateBody( m_worldId, &bodyDef );

			b2Polygon box = b2MakeBox( 0.75f, 0.75f );

			b2ShapeDef shapeDef = b2DefaultShapeDef();
			shapeDef.friction = 0.6f;
			shapeDef.density = 2.0f;

			b2CreatePolygonShape( m_secondPayloadId, &shapeDef, &box );
		}

		// Create a separate body on the ground
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = m_type;
			bodyDef.isEnabled = m_isEnabled;
			bodyDef.position = { 8.0f, 0.2f };
			m_touchingBodyId = b2CreateBody( m_worldId, &bodyDef );

			b2Capsule capsule = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, 0.25f };

			b2ShapeDef shapeDef = b2DefaultShapeDef();
			shapeDef.friction = 0.6f;
			shapeDef.density = 2.0f;

			b2CreateCapsuleShape( m_touchingBodyId, &shapeDef, &capsule );
		}

		// Create a separate floating body
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = m_type;
			bodyDef.isEnabled = m_isEnabled;
			bodyDef.position = { -8.0f, 12.0f };
			bodyDef.gravityScale = 0.0f;
			m_floatingBodyId = b2CreateBody( m_worldId, &bodyDef );

			b2Circle circle = { { 0.0f, 0.5f }, 0.25f };

			b2ShapeDef shapeDef = b2DefaultShapeDef();
			shapeDef.friction = 0.6f;
			shapeDef.density = 2.0f;

			b2CreateCircleShape( m_floatingBodyId, &shapeDef, &circle );
		}
	}

	void UpdateUI() override
	{
		float height = 140.0f;
		ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
		ImGui::SetNextWindowSize( ImVec2( 180.0f, height ) );
		ImGui::Begin( "Body Type", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );

		if ( ImGui::RadioButton( "Static", m_type == b2_staticBody ) )
		{
			m_type = b2_staticBody;
			b2Body_SetType( m_platformId, b2_staticBody );
			b2Body_SetType( m_secondAttachmentId, b2_staticBody );
			b2Body_SetType( m_secondPayloadId, b2_staticBody );
			b2Body_SetType( m_touchingBodyId, b2_staticBody );
			b2Body_SetType( m_floatingBodyId, b2_staticBody );
		}

		if ( ImGui::RadioButton( "Kinematic", m_type == b2_kinematicBody ) )
		{
			m_type = b2_kinematicBody;
			b2Body_SetType( m_platformId, b2_kinematicBody );
			b2Body_SetLinearVelocity( m_platformId, { -m_speed, 0.0f } );
			b2Body_SetAngularVelocity( m_platformId, 0.0f );
			b2Body_SetType( m_secondAttachmentId, b2_kinematicBody );
			b2Body_SetType( m_secondPayloadId, b2_kinematicBody );
			b2Body_SetType( m_touchingBodyId, b2_kinematicBody );
			b2Body_SetType( m_floatingBodyId, b2_kinematicBody );
		}

		if ( ImGui::RadioButton( "Dynamic", m_type == b2_dynamicBody ) )
		{
			m_type = b2_dynamicBody;
			b2Body_SetType( m_platformId, b2_dynamicBody );
			b2Body_SetType( m_secondAttachmentId, b2_dynamicBody );
			b2Body_SetType( m_secondPayloadId, b2_dynamicBody );
			b2Body_SetType( m_touchingBodyId, b2_dynamicBody );
			b2Body_SetType( m_floatingBodyId, b2_dynamicBody );
		}

		if ( ImGui::Checkbox( "Enable", &m_isEnabled ) )
		{
			if ( m_isEnabled )
			{
				b2Body_Enable( m_platformId );
				b2Body_Enable( m_secondAttachmentId );
				b2Body_Enable( m_secondPayloadId );
				b2Body_Enable( m_touchingBodyId );
				b2Body_Enable( m_floatingBodyId );

				if ( m_type == b2_kinematicBody )
				{
					b2Body_SetLinearVelocity( m_platformId, { -m_speed, 0.0f } );
					b2Body_SetAngularVelocity( m_platformId, 0.0f );
				}
			}
			else
			{
				b2Body_Disable( m_platformId );
				b2Body_Disable( m_secondAttachmentId );
				b2Body_Disable( m_secondPayloadId );
				b2Body_Disable( m_touchingBodyId );
				b2Body_Disable( m_floatingBodyId );
			}
		}

		ImGui::End();
	}

	void Step( Settings& settings ) override
	{
		// Drive the kinematic body.
		if ( m_type == b2_kinematicBody )
		{
			b2Vec2 p = b2Body_GetPosition( m_platformId );
			b2Vec2 v = b2Body_GetLinearVelocity( m_platformId );

			if ( ( p.x < -14.0f && v.x < 0.0f ) || ( p.x > 6.0f && v.x > 0.0f ) )
			{
				v.x = -v.x;
				b2Body_SetLinearVelocity( m_platformId, v );
			}
		}

		Sample::Step( settings );
	}

	static Sample* Create( Settings& settings )
	{
		return new BodyType( settings );
	}

	b2BodyId m_attachmentId;
	b2BodyId m_secondAttachmentId;
	b2BodyId m_platformId;
	b2BodyId m_secondPayloadId;
	b2BodyId m_touchingBodyId;
	b2BodyId m_floatingBodyId;
	b2BodyType m_type;
	float m_speed;
	bool m_isEnabled;
};

static int sampleBodyType = RegisterSample( "Bodies", "Body Type", BodyType::Create );

/// This is a test of typical character collision scenarios. This does not
/// show how you should implement a character in your application.
/// Instead this is used to test smooth collision on edge chains.
class Character : public Sample
{
public:
	explicit Character( Settings& settings )
		: Sample( settings )
	{
		if ( settings.restart == false )
		{
			g_camera.m_center = { -2.0f, 7.0f };
			g_camera.m_zoom = 25.0f * 0.4f;
		}

		// Ground body
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );

			b2ShapeDef shapeDef = b2DefaultShapeDef();
			b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };
			b2CreateSegmentShape( groundId, &shapeDef, &segment );
		}

		// Collinear edges with no adjacency information.
		// This shows the problematic case where a box shape can hit
		// an internal vertex.
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );

			b2ShapeDef shapeDef = b2DefaultShapeDef();
			b2Segment segment1 = { { -8.0f, 1.0f }, { -6.0f, 1.0f } };
			b2CreateSegmentShape( groundId, &shapeDef, &segment1 );

			b2Segment segment2 = { { -6.0f, 1.0f }, { -4.0f, 1.0f } };
			b2CreateSegmentShape( groundId, &shapeDef, &segment2 );

			b2Segment segment3 = { { -4.0f, 1.0f }, { -2.0f, 1.0f } };
			b2CreateSegmentShape( groundId, &shapeDef, &segment3 );
		}

		// Chain shape
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.rotation = b2MakeRot( 0.25f * b2_pi );
			b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );

			b2Vec2 points[4] = { { 8.0f, 7.0f }, { 7.0f, 8.0f }, { 6.0f, 8.0f }, { 5.0f, 7.0f } };
			b2ChainDef chainDef = b2DefaultChainDef();
			chainDef.points = points;
			chainDef.count = 4;
			chainDef.isLoop = true;

			b2CreateChain( groundId, &chainDef );
		}

		// Square tiles. This shows that adjacency shapes may have non-smooth collision. Box2D has no solution
		// to this problem.
		// TODO_ERIN try this: https://briansemrau.github.io/dealing-with-ghost-collisions/
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );

			b2ShapeDef shapeDef = b2DefaultShapeDef();
			b2Polygon box = b2MakeOffsetBox( 1.0f, 1.0f, { 4.0f, 3.0f }, 0.0f );
			b2CreatePolygonShape( groundId, &shapeDef, &box );

			box = b2MakeOffsetBox( 1.0f, 1.0f, { 6.0f, 3.0f }, 0.0f );
			b2CreatePolygonShape( groundId, &shapeDef, &box );

			box = b2MakeOffsetBox( 1.0f, 1.0f, { 8.0f, 3.0f }, 0.0f );
			b2CreatePolygonShape( groundId, &shapeDef, &box );
		}

		// Square made from a chain loop. Collision should be smooth.
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );

			b2Vec2 points[4] = { { -1.0f, 3.0 }, { 1.0f, 3.0f }, { 1.0f, 5.0f }, { -1.0f, 5.0 } };
			b2ChainDef chainDef = b2DefaultChainDef();
			chainDef.points = points;
			chainDef.count = 4;
			chainDef.isLoop = true;
			b2CreateChain( groundId, &chainDef );
		}

		// Chain loop. Collision should be smooth.
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.position = { -10.0f, 4.0f };
			b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef );

			b2Vec2 points[10] = { { 0.0f, 0.0f }, { 6.0f, 0.0f },  { 6.0f, 2.0f },	{ 4.0f, 1.0f },	 { 2.0f, 2.0f },
								  { 0.0f, 2.0f }, { -2.0f, 2.0f }, { -4.0f, 3.0f }, { -6.0f, 2.0f }, { -6.0f, 0.0f } };
			b2ChainDef chainDef = b2DefaultChainDef();
			chainDef.points = points;
			chainDef.count = 10;
			chainDef.isLoop = true;
			b2CreateChain( groundId, &chainDef );
		}

		// Circle character
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.position = { -7.0f, 6.0f };
			bodyDef.type = b2_dynamicBody;
			bodyDef.fixedRotation = true;
			bodyDef.enableSleep = false;

			m_circleCharacterId = b2CreateBody( m_worldId, &bodyDef );

			b2Circle circle = { { 0.0f, 0.0f }, 0.25f };

			b2ShapeDef shapeDef = b2DefaultShapeDef();
			shapeDef.density = 20.0f;
			shapeDef.friction = 0.2f;
			b2CreateCircleShape( m_circleCharacterId, &shapeDef, &circle );
		}

		// Capsule character
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.position = { 3.0f, 5.0f };
			bodyDef.type = b2_dynamicBody;
			bodyDef.fixedRotation = true;
			bodyDef.enableSleep = false;

			m_capsuleCharacterId = b2CreateBody( m_worldId, &bodyDef );

			b2Capsule capsule = { { 0.0f, 0.25f }, { 0.0f, 0.75f }, 0.25f };

			b2ShapeDef shapeDef = b2DefaultShapeDef();
			shapeDef.density = 20.0f;
			shapeDef.friction = 0.2f;
			b2CreateCapsuleShape( m_capsuleCharacterId, &shapeDef, &capsule );
		}

		// Square character
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.position = { -3.0f, 8.0f };
			bodyDef.type = b2_dynamicBody;
			bodyDef.fixedRotation = true;
			bodyDef.enableSleep = false;

			m_boxCharacterId = b2CreateBody( m_worldId, &bodyDef );

			b2Polygon box = b2MakeBox( 0.4f, 0.4f );

			b2ShapeDef shapeDef = b2DefaultShapeDef();
			shapeDef.density = 20.0f;
			shapeDef.friction = 0.2f;
			b2CreatePolygonShape( m_boxCharacterId, &shapeDef, &box );
		}
	}

	void Step( Settings& settings ) override
	{
		Sample::Step( settings );

		g_draw.DrawString( 5, m_textLine, "This tests various character collision shapes." );
		m_textLine += m_textIncrement;
		g_draw.DrawString( 5, m_textLine, "Limitation: square and hexagon can snag on aligned boxes." );
		m_textLine += m_textIncrement;
		g_draw.DrawString( 5, m_textLine, "Feature: edge chains have smooth collision inside and out." );
		m_textLine += m_textIncrement;
	}

	static Sample* Create( Settings& settings )
	{
		return new Character( settings );
	}

	b2BodyId m_circleCharacterId;
	b2BodyId m_capsuleCharacterId;
	b2BodyId m_boxCharacterId;
};

static int sampleCharacter = RegisterSample( "Bodies", "Character", Character::Create );

class Weeble : public Sample
{
public:
	explicit Weeble( Settings& settings )
		: Sample( settings )
	{
		if ( settings.restart == false )
		{
			g_camera.m_center = { 2.3f, 10.0f };
			g_camera.m_zoom = 25.0f * 0.5f;
		}

		b2BodyId groundId = b2_nullBodyId;
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			groundId = b2CreateBody( m_worldId, &bodyDef );

			b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			b2CreateSegmentShape( groundId, &shapeDef, &segment );
		}

		// Build weeble
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = b2_dynamicBody;
			bodyDef.position = { 0.0f, 3.0f };
			bodyDef.rotation = b2MakeRot( 0.25f * b2_pi );
			m_weebleId = b2CreateBody( m_worldId, &bodyDef );

			b2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 1.0f };
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			shapeDef.density = 1.0f;
			b2CreateCapsuleShape( m_weebleId, &shapeDef, &capsule );

			float mass = b2Body_GetMass( m_weebleId );
			float inertiaTensor = b2Body_GetInertiaTensor( m_weebleId );

			float offset = 1.5f;

			// See: https://en.wikipedia.org/wiki/Parallel_axis_theorem
			inertiaTensor += mass * offset * offset;

			b2MassData massData = { mass, { 0.0f, -offset }, inertiaTensor };
			b2Body_SetMassData( m_weebleId, massData );
		}

		m_explosionPosition = { 0.0f, 0.0f };
		m_explosionRadius = 2.0f;
		m_explosionMagnitude = 8.0f;
	}

	void UpdateUI() override
	{
		float height = 120.0f;
		ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
		ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );
		ImGui::Begin( "Weeble", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );
		if ( ImGui::Button( "Teleport" ) )
		{
			b2Body_SetTransform( m_weebleId, { 0.0f, 5.0f }, b2MakeRot( 0.95 * b2_pi ) );
		}

		if ( ImGui::Button( "Explode" ) )
		{
			b2World_Explode( m_worldId, m_explosionPosition, m_explosionRadius, m_explosionMagnitude );
		}
		ImGui::PushItemWidth( 100.0f );

		ImGui::SliderFloat( "Magnitude", &m_explosionMagnitude, -100.0f, 100.0f, "%.1f" );

		ImGui::PopItemWidth();
		ImGui::End();
	}

	void Step( Settings& settings ) override
	{
		Sample::Step( settings );

		g_draw.DrawCircle( m_explosionPosition, m_explosionRadius, b2_colorAzure );
	}

	static Sample* Create( Settings& settings )
	{
		return new Weeble( settings );
	}

	b2BodyId m_weebleId;
	b2Vec2 m_explosionPosition;
	float m_explosionRadius;
	float m_explosionMagnitude;
};

static int sampleWeeble = RegisterSample( "Bodies", "Weeble", Weeble::Create );

class Sleep : public Sample
{
public:
	explicit Sleep( Settings& settings )
		: Sample( settings )
	{
		if ( settings.restart == false )
		{
			g_camera.m_center = { 3.0f, 50.0f };
			g_camera.m_zoom = 25.0f * 2.2f;
		}

		b2BodyId groundId = b2_nullBodyId;
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			groundId = b2CreateBody( m_worldId, &bodyDef );

			b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			m_groundShapeId = b2CreateSegmentShape( groundId, &shapeDef, &segment );
		}

		// Sleeping body with sensors
		for ( int i = 0; i < 2; ++i )
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = b2_dynamicBody;
			bodyDef.position = { -4.0f, 3.0f + 2.0f * i };
			bodyDef.isAwake = false;
			bodyDef.enableSleep = true;
			b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );

			b2Capsule capsule = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, 0.75f };
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			b2CreateCapsuleShape( bodyId, &shapeDef, &capsule );

			shapeDef.isSensor = true;
			capsule.radius = 1.0f;
			m_sensorIds[i] = b2CreateCapsuleShape( bodyId, &shapeDef, &capsule );
			m_sensorTouching[i] = false;
		}

		// Sleeping body but sleep is disabled
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = b2_dynamicBody;
			bodyDef.position = { 0.0f, 3.0f };
			bodyDef.isAwake = false;
			bodyDef.enableSleep = false;
			b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );

			b2Circle circle = { { 1.0f, 1.0f }, 1.0f };
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			b2CreateCircleShape( bodyId, &shapeDef, &circle );
		}

		// Awake body and sleep is disabled
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = b2_dynamicBody;
			bodyDef.position = { 5.0f, 3.0f };
			bodyDef.isAwake = true;
			bodyDef.enableSleep = false;
			b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );

			b2Polygon box = b2MakeOffsetBox( 1.0f, 1.0f, { 0.0f, 1.0f }, 0.25f * b2_pi );
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			b2CreatePolygonShape( bodyId, &shapeDef, &box );
		}

		// A sleeping body to test waking on collision
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = b2_dynamicBody;
			bodyDef.position = { 5.0f, 1.0f };
			bodyDef.isAwake = false;
			bodyDef.enableSleep = true;
			b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );

			b2Polygon box = b2MakeSquare( 1.0f );
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			b2CreatePolygonShape( bodyId, &shapeDef, &box );
		}

		// A long pendulum
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = b2_dynamicBody;
			bodyDef.position = { 0.0f, 100.0f };
			bodyDef.angularDamping = 0.5f;
			bodyDef.sleepThreshold = 0.05f;
			m_pendulumId = b2CreateBody( m_worldId, &bodyDef );

			b2Capsule capsule = { { 0.0f, 0.0f }, { 90.0f, 0.0f }, 0.25f };
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			b2CreateCapsuleShape( m_pendulumId, &shapeDef, &capsule );

			b2Vec2 pivot = bodyDef.position;
			b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef();
			jointDef.bodyIdA = groundId;
			jointDef.bodyIdB = m_pendulumId;
			jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot );
			jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot );
			b2CreateRevoluteJoint( m_worldId, &jointDef );
		}
	}

	void UpdateUI() override
	{
		float height = 100.0f;
		ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
		ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) );
		ImGui::Begin( "Sleep", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );

		ImGui::PushItemWidth( 120.0f );

		ImGui::Text( "Pendulum Tuning" );

		float sleepVelocity = b2Body_GetSleepThreshold( m_pendulumId );
		if ( ImGui::SliderFloat( "sleep velocity", &sleepVelocity, 0.0f, 1.0f, "%.2f" ) )
		{
			b2Body_SetSleepThreshold( m_pendulumId, sleepVelocity );
			b2Body_SetAwake( m_pendulumId, true );
		}

		float angularDamping = b2Body_GetAngularDamping( m_pendulumId );
		if ( ImGui::SliderFloat( "angular damping", &angularDamping, 0.0f, 2.0f, "%.2f" ) )
		{
			b2Body_SetAngularDamping( m_pendulumId, angularDamping );
		}

		ImGui::PopItemWidth();

		ImGui::End();
	}

	void Step( Settings& settings ) override
	{
		Sample::Step( settings );

		// Detect sensors touching the ground
		b2SensorEvents sensorEvents = b2World_GetSensorEvents( m_worldId );

		for ( int i = 0; i < sensorEvents.beginCount; ++i )
		{
			b2SensorBeginTouchEvent* event = sensorEvents.beginEvents + i;
			if ( B2_ID_EQUALS( event->visitorShapeId, m_groundShapeId ) )
			{
				if ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[0] ) )
				{
					m_sensorTouching[0] = true;
				}
				else if ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[1] ) )
				{
					m_sensorTouching[1] = true;
				}
			}
		}

		for ( int i = 0; i < sensorEvents.endCount; ++i )
		{
			b2SensorEndTouchEvent* event = sensorEvents.endEvents + i;
			if ( B2_ID_EQUALS( event->visitorShapeId, m_groundShapeId ) )
			{
				if ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[0] ) )
				{
					m_sensorTouching[0] = false;
				}
				else if ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[1] ) )
				{
					m_sensorTouching[1] = false;
				}
			}
		}

		for ( int i = 0; i < 2; ++i )
		{
			g_draw.DrawString( 5, m_textLine, "sensor touch %d = %s", i, m_sensorTouching[i] ? "true" : "false" );
			m_textLine += m_textIncrement;
		}
	}

	static Sample* Create( Settings& settings )
	{
		return new Sleep( settings );
	}

	b2BodyId m_pendulumId;
	b2ShapeId m_groundShapeId;
	b2ShapeId m_sensorIds[2];
	bool m_sensorTouching[2];
};

static int sampleSleep = RegisterSample( "Bodies", "Sleep", Sleep::Create );

class BadBody : public Sample
{
public:
	explicit BadBody( Settings& settings )
		: Sample( settings )
	{
		if ( settings.restart == false )
		{
			g_camera.m_center = { 2.3f, 10.0f };
			g_camera.m_zoom = 25.0f * 0.5f;
		}

		b2BodyId groundId = b2_nullBodyId;
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			groundId = b2CreateBody( m_worldId, &bodyDef );

			b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } };
			b2ShapeDef shapeDef = b2DefaultShapeDef();
			b2CreateSegmentShape( groundId, &shapeDef, &segment );
		}

		// Build a bad body
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = b2_dynamicBody;
			bodyDef.position = { 0.0f, 3.0f };
			bodyDef.angularVelocity = 0.2f;
			bodyDef.rotation = b2MakeRot( 0.25f * b2_pi );

			m_badBodyId = b2CreateBody( m_worldId, &bodyDef );

			b2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 1.0f };
			b2ShapeDef shapeDef = b2DefaultShapeDef();

			// density set to zero intentionally to create a bad body
			shapeDef.density = 0.0f;
			b2CreateCapsuleShape( m_badBodyId, &shapeDef, &capsule );
		}

		// Build a normal body
		{
			b2BodyDef bodyDef = b2DefaultBodyDef();
			bodyDef.type = b2_dynamicBody;
			bodyDef.position = { 2.0f, 3.0f };
			bodyDef.rotation = b2MakeRot( 0.25f * b2_pi );

			b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );

			b2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 1.0f };
			b2ShapeDef shapeDef = b2DefaultShapeDef();

			b2CreateCapsuleShape( bodyId, &shapeDef, &capsule );
		}
	}

	void Step( Settings& settings ) override
	{
		Sample::Step( settings );

		g_draw.DrawString( 5, m_textLine, "A bad body is a dynamic body with no mass and behaves like a kinematic body." );
		m_textLine += m_textIncrement;

		g_draw.DrawString( 5, m_textLine, "Bad bodies are considered invalid and a user bug. Behavior is not guaranteed." );
		m_textLine += m_textIncrement;

		// For science
		b2Body_ApplyForceToCenter( m_badBodyId, { 0.0f, 10.0f }, true );
	}

	static Sample* Create( Settings& settings )
	{
		return new BadBody( settings );
	}

	b2BodyId m_badBodyId;
};

static int sampleBadBody = RegisterSample( "Bodies", "Bad", BadBody::Create );
