#include "chipmunk/chipmunk.h"
#include "chipmunk/chipmunk_unsafe.h"
#include "ChipmunkDemo.h"

#define ENABLE_HASTY 0
#if ENABLE_HASTY
#    include "chipmunk/cpHastySpace.h"

static cpSpace* MakeHastySpace()
{
    cpSpace* space = cpHastySpaceNew();
    cpHastySpaceSetThreads(space, 0);
    return space;
}

#    define BENCH_SPACE_NEW MakeHastySpace
#    define BENCH_SPACE_FREE cpHastySpaceFree
#    define BENCH_SPACE_STEP cpHastySpaceStep
#else
#    define BENCH_SPACE_NEW cpSpaceNew
#    define BENCH_SPACE_FREE cpSpaceFree
#    define BENCH_SPACE_STEP cpSpaceStep
#endif

const cpFloat bevel = 1.0;

static cpVect simple_terrain_verts[] = {
    {350.00, 425.07}, {336.00, 436.55}, {272.00, 435.39}, {258.00, 427.63}, {225.28, 420.00}, {202.82, 396.00},
    {191.81, 388.00}, {189.00, 381.89}, {173.00, 380.39}, {162.59, 368.00}, {150.47, 319.00}, {128.00, 311.55},
    {119.14, 286.00}, {126.84, 263.00}, {120.56, 227.00}, {141.14, 178.00}, {137.52, 162.00}, {146.51, 142.00},
    {156.23, 136.00}, {158.00, 118.27}, {170.00, 100.77}, {208.43, 84.00},  {224.00, 69.65},  {249.30, 68.00},
    {257.00, 54.77},  {363.00, 45.94},  {374.15, 54.00},  {386.00, 69.60},  {413.00, 70.73},  {456.00, 84.89},
    {468.09, 99.00},  {467.09, 123.00}, {464.92, 135.00}, {469.00, 141.03}, {497.00, 148.67}, {513.85, 180.00},
    {509.56, 223.00}, {523.51, 247.00}, {523.00, 277.00}, {497.79, 311.00}, {478.67, 348.00}, {467.90, 360.00},
    {456.76, 382.00}, {432.95, 389.00}, {417.00, 411.32}, {373.00, 433.19}, {361.00, 430.02}, {350.00, 425.07},
};
static int simple_terrain_count = sizeof(simple_terrain_verts) / sizeof(cpVect);

// cpBody bodies[1000] = {};
// cpCircleShape circles[1000] = {};

static void add_circle(cpSpace* space, int index, cpFloat radius)
{
    cpFloat mass = radius * radius / 25.0f;
    cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
    //	cpBody *body = cpSpaceAddBody(space, cpBodyInit(&bodies[i], mass, cpMomentForCircle(mass, 0.0f, radius,
    // cpvzero)));
    cpBodySetPosition(body, cpvmult(frand_unit_circle(), 180.0f));

    cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero));
    //	cpShape *shape = cpSpaceAddShape(space, cpCircleShapeInit(&circles[i], body, radius, cpvzero));
    cpShapeSetElasticity(shape, 0.0);
    cpShapeSetFriction(shape, 0.9);
}

static void add_box(cpSpace* space, int index, cpFloat size)
{
    cpFloat mass = size * size / 100.0f;
    cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, size, size)));
    //	cpBody *body = cpSpaceAddBody(space, cpBodyInit(&bodies[i], mass, cpMomentForBox(mass, size, size)));
    cpBodySetPosition(body, cpvmult(frand_unit_circle(), 180.0f));

    cpShape* shape = cpSpaceAddShape(space, cpBoxShapeNew(body, size - bevel * 2, size - bevel * 2, 0.0));
    cpPolyShapeSetRadius(shape, bevel);
    cpShapeSetElasticity(shape, 0.0);
    cpShapeSetFriction(shape, 0.9);
}

static void add_hexagon(cpSpace* space, int index, cpFloat radius)
{
    cpVect hexagon[6];
    for (int i = 0; i < 6; i++)
    {
        cpFloat angle = -CP_PI * 2.0f * i / 6.0f;
        hexagon[i]    = cpvmult(cpv(cos(angle), sin(angle)), radius - bevel);
    }

    cpFloat mass = radius * radius;
    cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 6, hexagon, cpvzero, 0.0f)));
    cpBodySetPosition(body, cpvmult(frand_unit_circle(), 180.0f));

    cpShape* shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 6, hexagon, cpTransformIdentity, bevel));
    cpShapeSetElasticity(shape, 0.0);
    cpShapeSetFriction(shape, 0.9);
}

static cpSpace* SetupSpace_simpleTerrain()
{
    cpSpace* space = BENCH_SPACE_NEW();
    cpSpaceSetIterations(space, 10);
    cpSpaceSetGravity(space, cpv(0, -100));
    cpSpaceSetCollisionSlop(space, 0.5f);

    cpVect offset = cpv(-320, -240);
    for (int i = 0; i < (simple_terrain_count - 1); i++)
    {
        cpVect a = simple_terrain_verts[i], b = simple_terrain_verts[i + 1];
        cpSpaceAddShape(space,
                        cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f));
    }

    return space;
}

// SimpleTerrain constant sized objects
static cpSpace* init_SimpleTerrainCircles_1000(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 1000; i++)
        add_circle(space, i, 5.0f);

    return space;
}

static cpSpace* init_SimpleTerrainCircles_500(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 500; i++)
        add_circle(space, i, 5.0f);

    return space;
}

static cpSpace* init_SimpleTerrainCircles_100(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 100; i++)
        add_circle(space, i, 5.0f);

    return space;
}

static cpSpace* init_SimpleTerrainBoxes_1000(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 1000; i++)
        add_box(space, i, 10.0f);

    return space;
}

static cpSpace* init_SimpleTerrainBoxes_500(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 500; i++)
        add_box(space, i, 10.0f);

    return space;
}

static cpSpace* init_SimpleTerrainBoxes_100(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 100; i++)
        add_box(space, i, 10.0f);

    return space;
}

static cpSpace* init_SimpleTerrainHexagons_1000(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 1000; i++)
        add_hexagon(space, i, 5.0f);

    return space;
}

static cpSpace* init_SimpleTerrainHexagons_500(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 500; i++)
        add_hexagon(space, i, 5.0f);

    return space;
}

static cpSpace* init_SimpleTerrainHexagons_100(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 100; i++)
        add_hexagon(space, i, 5.0f);

    return space;
}

// SimpleTerrain variable sized objects
static cpFloat rand_size()
{
    return cpfpow(1.5, cpflerp(-1.5, 3.5, frand()));
}

static cpSpace* init_SimpleTerrainVCircles_200(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 200; i++)
        add_circle(space, i, 5.0f * rand_size());

    return space;
}

static cpSpace* init_SimpleTerrainVBoxes_200(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 200; i++)
        add_box(space, i, 8.0f * rand_size());

    return space;
}

static cpSpace* init_SimpleTerrainVHexagons_200(void)
{
    cpSpace* space = SetupSpace_simpleTerrain();
    for (int i = 0; i < 200; i++)
        add_hexagon(space, i, 5.0f * rand_size());

    return space;
}

// ComplexTerrain
static cpVect complex_terrain_verts[] = {
    {46.78, 479.00},  {35.00, 475.63},  {27.52, 469.00},  {23.52, 455.00},  {23.78, 441.00},  {28.41, 428.00},
    {49.61, 394.00},  {59.00, 381.56},  {80.00, 366.03},  {81.46, 358.00},  {86.31, 350.00},  {77.74, 320.00},
    {70.26, 278.00},  {67.51, 270.00},  {58.86, 260.00},  {57.19, 247.00},  {38.00, 235.60},  {25.76, 221.00},
    {24.58, 209.00},  {27.63, 202.00},  {31.28, 198.00},  {40.00, 193.72},  {48.00, 193.73},  {55.00, 196.70},
    {62.10, 204.00},  {71.00, 209.04},  {79.00, 206.55},  {88.00, 206.81},  {95.88, 211.00},  {103.00, 220.49},
    {131.00, 220.51}, {137.00, 222.66}, {143.08, 228.00}, {146.22, 234.00}, {147.08, 241.00}, {145.45, 248.00},
    {142.31, 253.00}, {132.00, 259.30}, {115.00, 259.70}, {109.28, 270.00}, {112.91, 296.00}, {119.69, 324.00},
    {129.00, 336.26}, {141.00, 337.59}, {153.00, 331.57}, {175.00, 325.74}, {188.00, 325.19}, {235.00, 317.46},
    {250.00, 317.19}, {255.00, 309.12}, {262.62, 302.00}, {262.21, 295.00}, {248.00, 273.59}, {229.00, 257.93},
    {221.00, 255.48}, {215.00, 251.59}, {210.79, 246.00}, {207.47, 234.00}, {203.25, 227.00}, {179.00, 205.90},
    {148.00, 189.54}, {136.00, 181.45}, {120.00, 180.31}, {110.00, 181.65}, {95.00, 179.31},  {63.00, 166.96},
    {50.00, 164.23},  {31.00, 154.49},  {19.76, 145.00},  {15.96, 136.00},  {16.65, 127.00},  {20.57, 120.00},
    {28.00, 114.63},  {40.00, 113.67},  {65.00, 127.22},  {73.00, 128.69},  {81.96, 120.00},  {77.58, 103.00},
    {78.18, 92.00},   {59.11, 77.00},   {52.00, 67.29},   {31.29, 55.00},   {25.67, 47.00},   {24.65, 37.00},
    {27.82, 29.00},   {35.00, 22.55},   {44.00, 20.35},   {49.00, 20.81},   {61.00, 25.69},   {79.00, 37.81},
    {88.00, 49.64},   {97.00, 56.65},   {109.00, 49.61},  {143.00, 38.96},  {197.00, 37.27},  {215.00, 35.30},
    {222.00, 36.65},  {228.42, 41.00},  {233.30, 49.00},  {234.14, 57.00},  {231.00, 65.80},  {224.00, 72.38},
    {218.00, 74.50},  {197.00, 76.62},  {145.00, 78.81},  {123.00, 87.41},  {117.59, 98.00},  {117.79, 104.00},
    {119.00, 106.23}, {138.73, 120.00}, {148.00, 129.50}, {158.50, 149.00}, {203.93, 175.00}, {229.00, 196.60},
    {238.16, 208.00}, {245.20, 221.00}, {275.45, 245.00}, {289.00, 263.24}, {303.60, 287.00}, {312.00, 291.57},
    {339.25, 266.00}, {366.33, 226.00}, {363.43, 216.00}, {364.13, 206.00}, {353.00, 196.72}, {324.00, 181.05},
    {307.00, 169.63}, {274.93, 156.00}, {256.00, 152.48}, {228.00, 145.13}, {221.09, 142.00}, {214.87, 135.00},
    {212.67, 127.00}, {213.81, 119.00}, {219.32, 111.00}, {228.00, 106.52}, {236.00, 106.39}, {290.00, 119.40},
    {299.33, 114.00}, {300.52, 109.00}, {300.30, 53.00},  {301.46, 47.00},  {305.00, 41.12},  {311.00, 36.37},
    {317.00, 34.43},  {325.00, 34.81},  {334.90, 41.00},  {339.45, 50.00},  {339.82, 132.00}, {346.09, 139.00},
    {350.00, 150.26}, {380.00, 167.38}, {393.00, 166.48}, {407.00, 155.54}, {430.00, 147.30}, {437.78, 135.00},
    {433.13, 122.00}, {410.23, 78.00},  {401.59, 69.00},  {393.48, 56.00},  {392.80, 44.00},  {395.50, 38.00},
    {401.00, 32.49},  {409.00, 29.41},  {420.00, 30.84},  {426.92, 36.00},  {432.32, 44.00},  {439.49, 51.00},
    {470.13, 108.00}, {475.71, 124.00}, {483.00, 130.11}, {488.00, 139.43}, {529.00, 139.40}, {536.00, 132.52},
    {543.73, 129.00}, {540.47, 115.00}, {541.11, 100.00}, {552.18, 68.00},  {553.78, 47.00},  {559.00, 39.76},
    {567.00, 35.52},  {577.00, 35.45},  {585.00, 39.58},  {591.38, 50.00},  {591.67, 66.00},  {590.31, 79.00},
    {579.76, 109.00}, {582.25, 119.00}, {583.66, 136.00}, {586.45, 143.00}, {586.44, 151.00}, {580.42, 168.00},
    {577.15, 173.00}, {572.00, 177.13}, {564.00, 179.49}, {478.00, 178.81}, {443.00, 184.76}, {427.10, 190.00},
    {424.00, 192.11}, {415.94, 209.00}, {408.82, 228.00}, {405.82, 241.00}, {411.00, 250.82}, {415.00, 251.50},
    {428.00, 248.89}, {469.00, 246.29}, {505.00, 246.49}, {533.00, 243.60}, {541.87, 248.00}, {547.55, 256.00},
    {548.48, 267.00}, {544.00, 276.00}, {534.00, 282.24}, {513.00, 285.46}, {468.00, 285.76}, {402.00, 291.70},
    {392.00, 290.29}, {377.00, 294.46}, {367.00, 294.43}, {356.44, 304.00}, {354.22, 311.00}, {362.00, 321.36},
    {390.00, 322.44}, {433.00, 330.16}, {467.00, 332.76}, {508.00, 347.64}, {522.00, 357.67}, {528.00, 354.46},
    {536.00, 352.96}, {546.06, 336.00}, {553.47, 306.00}, {564.19, 282.00}, {567.84, 268.00}, {578.72, 246.00},
    {585.00, 240.97}, {592.00, 238.91}, {600.00, 239.72}, {606.00, 242.82}, {612.36, 251.00}, {613.35, 263.00},
    {588.75, 324.00}, {583.25, 350.00}, {572.12, 370.00}, {575.45, 378.00}, {575.20, 388.00}, {589.00, 393.81},
    {599.20, 404.00}, {607.14, 416.00}, {609.96, 430.00}, {615.45, 441.00}, {613.44, 462.00}, {610.48, 469.00},
    {603.00, 475.63}, {590.96, 479.00},
};
static int complex_terrain_count = sizeof(complex_terrain_verts) / sizeof(cpVect);

static cpSpace* init_ComplexTerrainCircles_1000(void)
{
    cpSpace* space = BENCH_SPACE_NEW();
    cpSpaceSetIterations(space, 10);
    cpSpaceSetGravity(space, cpv(0, -100));
    cpSpaceSetCollisionSlop(space, 0.5f);

    cpVect offset = cpv(-320, -240);
    for (int i = 0; i < (complex_terrain_count - 1); i++)
    {
        cpVect a = complex_terrain_verts[i], b = complex_terrain_verts[i + 1];
        cpSpaceAddShape(space,
                        cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f));
    }

    for (int i = 0; i < 1000; i++)
    {
        cpFloat radius = 5.0f;
        cpFloat mass   = radius * radius;
        cpBody* body   = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
        cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 180.0f), cpv(0.0f, 300.0f)));

        cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero));
        cpShapeSetElasticity(shape, 0.0);
        cpShapeSetFriction(shape, 0.0);
    }

    return space;
}

static cpSpace* init_ComplexTerrainHexagons_1000(void)
{
    cpSpace* space = BENCH_SPACE_NEW();
    cpSpaceSetIterations(space, 10);
    cpSpaceSetGravity(space, cpv(0, -100));
    cpSpaceSetCollisionSlop(space, 0.5f);

    cpVect offset = cpv(-320, -240);
    for (int i = 0; i < (complex_terrain_count - 1); i++)
    {
        cpVect a = complex_terrain_verts[i], b = complex_terrain_verts[i + 1];
        cpSpaceAddShape(space,
                        cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f));
    }

    cpFloat radius = 5.0f;
    cpVect hexagon[6];
    for (int i = 0; i < 6; i++)
    {
        cpFloat angle = -CP_PI * 2.0f * i / 6.0f;
        hexagon[i]    = cpvmult(cpv(cos(angle), sin(angle)), radius - bevel);
    }

    for (int i = 0; i < 1000; i++)
    {
        cpFloat mass = radius * radius;
        cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 6, hexagon, cpvzero, 0.0f)));
        cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 180.0f), cpv(0.0f, 300.0f)));

        cpShape* shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 6, hexagon, cpTransformIdentity, bevel));
        cpShapeSetElasticity(shape, 0.0);
        cpShapeSetFriction(shape, 0.0);
    }

    return space;
}

// BouncyTerrain
static cpVect bouncy_terrain_verts[] = {
    {537.18, 23.00},  {520.50, 36.00},  {501.53, 63.00},  {496.14, 76.00},  {498.86, 86.00},  {504.00, 90.51},
    {508.00, 91.36},  {508.77, 84.00},  {513.00, 77.73},  {519.00, 74.48},  {530.00, 74.67},  {545.00, 54.65},
    {554.00, 48.77},  {562.00, 46.39},  {568.00, 45.94},  {568.61, 47.00},  {567.94, 55.00},  {571.27, 64.00},
    {572.92, 80.00},  {572.00, 81.39},  {563.00, 79.93},  {556.00, 82.69},  {551.49, 88.00},  {549.00, 95.76},
    {538.00, 93.40},  {530.00, 102.38}, {523.00, 104.00}, {517.00, 103.02}, {516.22, 109.00}, {518.96, 116.00},
    {526.00, 121.15}, {534.00, 116.48}, {543.00, 116.77}, {549.28, 121.00}, {554.00, 130.17}, {564.00, 125.67},
    {575.60, 129.00}, {573.31, 121.00}, {567.77, 111.00}, {575.00, 106.47}, {578.51, 102.00}, {580.25, 95.00},
    {577.98, 87.00},  {582.00, 85.71},  {597.00, 89.46},  {604.80, 95.00},  {609.28, 104.00}, {610.55, 116.00},
    {609.30, 125.00}, {600.80, 142.00}, {597.31, 155.00}, {584.00, 167.23}, {577.86, 175.00}, {583.52, 184.00},
    {582.64, 195.00}, {591.00, 196.56}, {597.81, 201.00}, {607.45, 219.00}, {607.51, 246.00}, {600.00, 275.46},
    {588.00, 267.81}, {579.00, 264.91}, {557.00, 264.41}, {552.98, 259.00}, {548.00, 246.18}, {558.00, 247.12},
    {565.98, 244.00}, {571.10, 237.00}, {571.61, 229.00}, {568.25, 222.00}, {562.00, 217.67}, {544.00, 213.93},
    {536.73, 214.00}, {535.60, 204.00}, {539.69, 181.00}, {542.84, 171.00}, {550.43, 161.00}, {540.00, 156.27},
    {536.62, 152.00}, {534.70, 146.00}, {527.00, 141.88}, {518.59, 152.00}, {514.51, 160.00}, {510.33, 175.00},
    {519.38, 183.00}, {520.52, 194.00}, {516.00, 201.27}, {505.25, 206.00}, {507.57, 223.00}, {519.90, 260.00},
    {529.00, 260.48}, {534.00, 262.94}, {538.38, 268.00}, {540.00, 275.00}, {537.06, 284.00}, {530.00, 289.23},
    {520.00, 289.23}, {513.00, 284.18}, {509.71, 286.00}, {501.69, 298.00}, {501.56, 305.00}, {504.30, 311.00},
    {512.00, 316.43}, {521.00, 316.42}, {525.67, 314.00}, {535.00, 304.98}, {562.00, 294.80}, {573.00, 294.81},
    {587.52, 304.00}, {600.89, 310.00}, {596.96, 322.00}, {603.28, 327.00}, {606.52, 333.00}, {605.38, 344.00},
    {597.65, 352.00}, {606.36, 375.00}, {607.16, 384.00}, {603.40, 393.00}, {597.00, 398.14}, {577.00, 386.15},
    {564.35, 373.00}, {565.21, 364.00}, {562.81, 350.00}, {553.00, 346.06}, {547.48, 338.00}, {547.48, 330.00},
    {550.00, 323.30}, {544.00, 321.53}, {537.00, 322.70}, {532.00, 326.23}, {528.89, 331.00}, {527.83, 338.00},
    {533.02, 356.00}, {542.00, 360.73}, {546.68, 369.00}, {545.38, 379.00}, {537.58, 386.00}, {537.63, 388.00},
    {555.00, 407.47}, {563.00, 413.52}, {572.57, 418.00}, {582.72, 426.00}, {578.00, 431.12}, {563.21, 440.00},
    {558.00, 449.27}, {549.00, 452.94}, {541.00, 451.38}, {536.73, 448.00}, {533.00, 441.87}, {520.00, 437.96},
    {514.00, 429.69}, {490.00, 415.15}, {472.89, 399.00}, {472.03, 398.00}, {474.00, 396.71}, {486.00, 393.61},
    {492.00, 385.85}, {492.00, 376.15}, {489.04, 371.00}, {485.00, 368.11}, {480.00, 376.27}, {472.00, 379.82},
    {463.00, 378.38}, {455.08, 372.00}, {446.00, 377.69}, {439.00, 385.24}, {436.61, 391.00}, {437.52, 404.00},
    {440.00, 409.53}, {463.53, 433.00}, {473.80, 441.00}, {455.00, 440.30}, {443.00, 436.18}, {436.00, 431.98},
    {412.00, 440.92}, {397.00, 442.46}, {393.59, 431.00}, {393.71, 412.00}, {400.00, 395.10}, {407.32, 387.00},
    {408.54, 380.00}, {407.42, 375.00}, {403.97, 370.00}, {399.00, 366.74}, {393.00, 365.68}, {391.23, 374.00},
    {387.00, 380.27}, {381.00, 383.52}, {371.56, 384.00}, {364.98, 401.00}, {362.96, 412.00}, {363.63, 435.00},
    {345.00, 433.55}, {344.52, 442.00}, {342.06, 447.00}, {337.00, 451.38}, {330.00, 453.00}, {325.00, 452.23},
    {318.00, 448.17}, {298.00, 453.70}, {284.00, 451.49}, {278.62, 449.00}, {291.47, 408.00}, {291.77, 398.00},
    {301.00, 393.83}, {305.00, 393.84}, {305.60, 403.00}, {310.00, 409.47}, {318.00, 413.07}, {325.00, 412.40},
    {332.31, 407.00}, {335.07, 400.00}, {334.40, 393.00}, {329.00, 385.69}, {319.00, 382.79}, {301.00, 389.23},
    {289.00, 389.97}, {265.00, 389.82}, {251.00, 385.85}, {245.00, 389.23}, {239.00, 389.94}, {233.00, 388.38},
    {226.00, 382.04}, {206.00, 374.75}, {206.00, 394.00}, {204.27, 402.00}, {197.00, 401.79}, {191.00, 403.49},
    {186.53, 407.00}, {183.60, 412.00}, {183.60, 422.00}, {189.00, 429.31}, {196.00, 432.07}, {203.00, 431.40},
    {209.47, 427.00}, {213.00, 419.72}, {220.00, 420.21}, {227.00, 418.32}, {242.00, 408.41}, {258.98, 409.00},
    {250.00, 435.43}, {239.00, 438.78}, {223.00, 448.19}, {209.00, 449.70}, {205.28, 456.00}, {199.00, 460.23},
    {190.00, 460.52}, {182.73, 456.00}, {178.00, 446.27}, {160.00, 441.42}, {148.35, 435.00}, {149.79, 418.00},
    {157.72, 401.00}, {161.00, 396.53}, {177.00, 385.00}, {180.14, 380.00}, {181.11, 374.00}, {180.00, 370.52},
    {170.00, 371.68}, {162.72, 368.00}, {158.48, 361.00}, {159.56, 349.00}, {154.00, 342.53}, {146.00, 339.85},
    {136.09, 343.00}, {130.64, 351.00}, {131.74, 362.00}, {140.61, 374.00}, {130.68, 387.00}, {120.75, 409.00},
    {118.09, 421.00}, {117.92, 434.00}, {100.00, 432.40}, {87.00, 427.48},  {81.59, 423.00},  {73.64, 409.00},
    {72.57, 398.00},  {74.62, 386.00},  {78.80, 378.00},  {88.00, 373.43},  {92.49, 367.00},  {93.32, 360.00},
    {91.30, 353.00},  {103.00, 342.67}, {109.00, 343.10}, {116.00, 340.44}, {127.33, 330.00}, {143.00, 327.24},
    {154.30, 322.00}, {145.00, 318.06}, {139.77, 311.00}, {139.48, 302.00}, {144.95, 293.00}, {143.00, 291.56},
    {134.00, 298.21}, {118.00, 300.75}, {109.40, 305.00}, {94.67, 319.00},  {88.00, 318.93},  {81.00, 321.69},
    {67.24, 333.00},  {56.68, 345.00},  {53.00, 351.40},  {47.34, 333.00},  {50.71, 314.00},  {56.57, 302.00},
    {68.00, 287.96},  {91.00, 287.24},  {110.00, 282.36}, {133.80, 271.00}, {147.34, 256.00}, {156.47, 251.00},
    {157.26, 250.00}, {154.18, 242.00}, {154.48, 236.00}, {158.72, 229.00}, {166.71, 224.00}, {170.15, 206.00},
    {170.19, 196.00}, {167.24, 188.00}, {160.00, 182.67}, {150.00, 182.66}, {143.60, 187.00}, {139.96, 195.00},
    {139.50, 207.00}, {136.45, 221.00}, {136.52, 232.00}, {133.28, 238.00}, {129.00, 241.38}, {119.00, 243.07},
    {115.00, 246.55}, {101.00, 253.16}, {86.00, 257.32},  {63.00, 259.24},  {57.00, 257.31},  {50.54, 252.00},
    {47.59, 247.00},  {46.30, 240.00},  {47.58, 226.00},  {50.00, 220.57},  {58.00, 226.41},  {69.00, 229.17},
    {79.00, 229.08},  {94.50, 225.00},  {100.21, 231.00}, {107.00, 233.47}, {107.48, 224.00}, {109.94, 219.00},
    {115.00, 214.62}, {122.57, 212.00}, {116.00, 201.49}, {104.00, 194.57}, {90.00, 194.04},  {79.00, 198.21},
    {73.00, 198.87},  {62.68, 191.00},  {62.58, 184.00},  {64.42, 179.00},  {75.00, 167.70},  {80.39, 157.00},
    {68.79, 140.00},  {61.67, 126.00},  {61.47, 117.00},  {64.43, 109.00},  {63.10, 96.00},   {56.48, 82.00},
    {48.00, 73.88},   {43.81, 66.00},   {43.81, 56.00},   {50.11, 46.00},   {59.00, 41.55},   {71.00, 42.64},
    {78.00, 36.77},   {83.00, 34.75},   {99.00, 34.32},   {117.00, 38.92},  {133.00, 55.15},  {142.00, 50.70},
    {149.74, 51.00},  {143.55, 68.00},  {153.28, 74.00},  {156.23, 79.00},  {157.00, 84.00},  {156.23, 89.00},
    {153.28, 94.00},  {144.58, 99.00},  {151.52, 112.00}, {151.51, 124.00}, {150.00, 126.36}, {133.00, 130.25},
    {126.71, 125.00}, {122.00, 117.25}, {114.00, 116.23}, {107.73, 112.00}, {104.48, 106.00}, {104.32, 99.00},
    {106.94, 93.00},  {111.24, 89.00},  {111.60, 85.00},  {107.24, 73.00},  {102.00, 67.57},  {99.79, 67.00},
    {99.23, 76.00},   {95.00, 82.27},   {89.00, 85.52},   {79.84, 86.00},   {86.73, 114.00},  {98.00, 136.73},
    {99.00, 137.61},  {109.00, 135.06}, {117.00, 137.94}, {122.52, 146.00}, {122.94, 151.00}, {121.00, 158.58},
    {134.00, 160.97}, {153.00, 157.45}, {171.30, 150.00}, {169.06, 142.00}, {169.77, 136.00}, {174.00, 129.73},
    {181.46, 126.00}, {182.22, 120.00}, {182.20, 111.00}, {180.06, 101.00}, {171.28, 85.00},  {171.75, 80.00},
    {182.30, 53.00},  {189.47, 50.00},  {190.62, 38.00},  {194.00, 33.73},  {199.00, 30.77},  {208.00, 30.48},
    {216.00, 34.94},  {224.00, 31.47},  {240.00, 30.37},  {247.00, 32.51},  {249.77, 35.00},  {234.75, 53.00},
    {213.81, 93.00},  {212.08, 99.00},  {213.00, 101.77}, {220.00, 96.77},  {229.00, 96.48},  {236.28, 101.00},
    {240.00, 107.96}, {245.08, 101.00}, {263.00, 65.32},  {277.47, 48.00},  {284.00, 47.03},  {286.94, 41.00},
    {292.00, 36.62},  {298.00, 35.06},  {304.00, 35.77},  {314.00, 43.81},  {342.00, 32.56},  {359.00, 31.32},
    {365.00, 32.57},  {371.00, 36.38},  {379.53, 48.00},  {379.70, 51.00},  {356.00, 52.19},  {347.00, 54.74},
    {344.38, 66.00},  {341.00, 70.27},  {335.00, 73.52},  {324.00, 72.38},  {317.00, 65.75},  {313.00, 67.79},
    {307.57, 76.00},  {315.00, 78.62},  {319.28, 82.00},  {322.23, 87.00},  {323.00, 94.41},  {334.00, 92.49},
    {347.00, 87.47},  {349.62, 80.00},  {353.00, 75.73},  {359.00, 72.48},  {366.00, 72.32},  {372.00, 74.94},
    {377.00, 81.34},  {382.00, 83.41},  {392.00, 83.40},  {399.00, 79.15},  {404.00, 85.74},  {411.00, 85.06},
    {417.00, 86.62},  {423.38, 93.00},  {425.05, 104.00}, {438.00, 110.35}, {450.00, 112.17}, {452.62, 103.00},
    {456.00, 98.73},  {462.00, 95.48},  {472.00, 95.79},  {471.28, 92.00},  {464.00, 84.62},  {445.00, 80.39},
    {436.00, 75.33},  {428.00, 68.46},  {419.00, 68.52},  {413.00, 65.27},  {408.48, 58.00},  {409.87, 46.00},
    {404.42, 39.00},  {408.00, 33.88},  {415.00, 29.31},  {429.00, 26.45},  {455.00, 28.77},  {470.00, 33.81},
    {482.00, 42.16},  {494.00, 46.85},  {499.65, 36.00},  {513.00, 25.95},  {529.00, 22.42},  {537.18, 23.00},
};
static int bouncy_terrain_count = sizeof(bouncy_terrain_verts) / sizeof(cpVect);

static cpSpace* init_BouncyTerrainCircles_500(void)
{
    cpSpace* space = BENCH_SPACE_NEW();
    cpSpaceSetIterations(space, 10);

    cpVect offset = cpv(-320, -240);
    for (int i = 0; i < (bouncy_terrain_count - 1); i++)
    {
        cpVect a = bouncy_terrain_verts[i], b = bouncy_terrain_verts[i + 1];
        cpShape* shape = cpSpaceAddShape(
            space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f));
        cpShapeSetElasticity(shape, 1.0);
    }

    for (int i = 0; i < 500; i++)
    {
        cpFloat radius = 5.0f;
        cpFloat mass   = radius * radius;
        cpBody* body   = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
        cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 130.0f), cpvzero));
        cpBodySetVelocity(body, cpvmult(frand_unit_circle(), 50.0f));

        cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero));
        cpShapeSetElasticity(shape, 1.0);
    }

    return space;
}

static cpSpace* init_BouncyTerrainHexagons_500(void)
{
    cpSpace* space = BENCH_SPACE_NEW();
    cpSpaceSetIterations(space, 10);

    cpVect offset = cpv(-320, -240);
    for (int i = 0; i < (bouncy_terrain_count - 1); i++)
    {
        cpVect a = bouncy_terrain_verts[i], b = bouncy_terrain_verts[i + 1];
        cpShape* shape = cpSpaceAddShape(
            space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f));
        cpShapeSetElasticity(shape, 1.0);
    }

    cpFloat radius = 5.0f;
    cpVect hexagon[6];
    for (int i = 0; i < 6; i++)
    {
        cpFloat angle = -CP_PI * 2.0f * i / 6.0f;
        hexagon[i]    = cpvmult(cpv(cos(angle), sin(angle)), radius - bevel);
    }

    for (int i = 0; i < 500; i++)
    {
        cpFloat mass = radius * radius;
        cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 6, hexagon, cpvzero, 0.0f)));
        cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 130.0f), cpvzero));
        cpBodySetVelocity(body, cpvmult(frand_unit_circle(), 50.0f));

        cpShape* shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 6, hexagon, cpTransformIdentity, bevel));
        cpShapeSetElasticity(shape, 1.0);
    }

    return space;
}

// No collisions

static cpFloat pentagon_mass   = 0.0f;
static cpFloat pentagon_moment = 0.0f;

static cpBool NoCollide_begin(cpArbiter* arb, cpSpace* space, void* data)
{

    // CCLOG("NoCollide_begin");

    return cpTrue;
}

static cpSpace* init_NoCollide(void)
{
    cpSpace* space = BENCH_SPACE_NEW();
    cpSpaceSetIterations(space, 10);

    cpCollisionHandler* handler = cpSpaceAddCollisionHandler(space, 2, 2);
    handler->beginFunc          = NoCollide_begin;

    float radius = 4.5f;

    cpShapeSetElasticity(
        cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(-330 - radius, -250 - radius),
                                                 cpv(330 + radius, -250 - radius), 0.0f)),
        1.0f);
    cpShapeSetElasticity(
        cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(330 + radius, 250 + radius),
                                                 cpv(330 + radius, -250 - radius), 0.0f)),
        1.0f);
    cpShapeSetElasticity(
        cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(330 + radius, 250 + radius),
                                                 cpv(-330 - radius, 250 + radius), 0.0f)),
        1.0f);
    cpShapeSetElasticity(
        cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(-330 - radius, -250 - radius),
                                                 cpv(-330 - radius, 250 + radius), 0.0f)),
        1.0f);

    for (int x = -320; x <= 320; x += 20)
    {
        for (int y = -240; y <= 240; y += 20)
        {
            cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(cpSpaceGetStaticBody(space), radius, cpv(x, y)));
            cpShapeSetElasticity(shape, 1.0);
            cpShapeSetCollisionType(shape, 2);
        }
    }

    for (int y = 10 - 240; y <= 240; y += 40)
    {
        cpFloat mass = 7.0f;
        cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
        cpBodySetPosition(body, cpv(-320.0f, y));
        cpBodySetVelocity(body, cpv(100.0f, 0.0f));

        cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero));
        cpShapeSetElasticity(shape, 1.0);
        cpShapeSetCollisionType(shape, 2);
    }

    for (int x = 30 - 320; x <= 320; x += 40)
    {
        cpFloat mass = 7.0f;
        cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
        cpBodySetPosition(body, cpv(x, -240.0f));
        cpBodySetVelocity(body, cpv(0.0f, 100.0f));

        cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero));
        cpShapeSetElasticity(shape, 1.0);
        cpShapeSetCollisionType(shape, 2);
    }

    return space;
}

// TODO ideas:
// addition/removal
// Memory usage? (too small to matter?)
// http://forums.tigsource.com/index.php?topic=18077.msg518578#msg518578

// Build benchmark list
static void update(cpSpace* space, double dt)
{
    BENCH_SPACE_STEP(space, dt);
}

static void destroy(cpSpace* space)
{
    ChipmunkDemoFreeSpaceChildren(space);
    BENCH_SPACE_FREE(space);
}

// Make a second demo declaration for this demo to use in the regular demo set.
ChipmunkDemo BouncyHexagons = {
    "Bouncy Hexagons", 1.0 / 60.0, init_BouncyTerrainHexagons_500, update, ChipmunkDemoDefaultDrawImpl, destroy,
};

#define BENCH(n)                                                               \
    {                                                                          \
#        n, 1.0 / 60.0, init_##n, update, ChipmunkDemoDefaultDrawImpl, destroy \
    }
ChipmunkDemo bench_list[] = {
    BENCH(SimpleTerrainCircles_1000),
    BENCH(SimpleTerrainCircles_500),
    BENCH(SimpleTerrainCircles_100),
    BENCH(SimpleTerrainBoxes_1000),
    BENCH(SimpleTerrainBoxes_500),
    BENCH(SimpleTerrainBoxes_100),
    BENCH(SimpleTerrainHexagons_1000),
    BENCH(SimpleTerrainHexagons_500),
    BENCH(SimpleTerrainHexagons_100),
    BENCH(SimpleTerrainVCircles_200),
    BENCH(SimpleTerrainVBoxes_200),
    BENCH(SimpleTerrainVHexagons_200),
    BENCH(ComplexTerrainCircles_1000),
    BENCH(ComplexTerrainHexagons_1000),
    BENCH(BouncyTerrainCircles_500),
    BENCH(BouncyTerrainHexagons_500),
    BENCH(NoCollide),
};

int bench_count = sizeof(bench_list) / sizeof(ChipmunkDemo);