
#ifndef mosaic_phys_h
#define mosaic_phys_h

#include <vector>

#include "itty/box.hh"
#include "itty/grid.hh"
#include "itty/vec.hh"

struct Body {
        // Indices of bits in the flags fields: the HIT_FOO flags determine if
        // the body hit the scenery on that side last tick; the SOLID flag
        // determines if the body can hit the scenery at all.
        static constexpr int HIT_LF     = 0x01;
        static constexpr int HIT_RT     = 0x02;
        static constexpr int HIT_UP     = 0x04;
        static constexpr int HIT_DN     = 0x08;
        static constexpr int SOLID      = 0x10;

        // A body holds: position, velocity, half-size of the box, the actual
        // box from p-r to p+r, and individual bits that can be handled with
        // the is(), set() and unset() functions.
        itty::Vec p;
        itty::Vec v;
        itty::Vec r;
        itty::Box box;
        int flags{0};

        Body(const itty::Box& box, int flags)
                : p{box.ct()}, r{box.size() * 0.5f}, box{box}, flags{flags} {}

        bool is(int f) { return flags & f; }
        bool set(int f) { return flags |= f; }
        bool unset(int f) { return flags &= ~f; }
};

struct Engine {
        // An engine holds: the body being moved, the direction of movement,
        // the target speed in that direction and the acceleration to be
        // applied to reach that speed.
        Body *b;
        itty::Vec n;
        float s;
        float a;

        void apply(float t);
};

struct Phys : public itty::Grid<bool> {
 private:
        std::vector<Body *> _bodies;
        std::vector<Engine> _engines;

        void test_lf(Body &b, float t) const;
        void test_rt(Body &b, float t) const;
        void test_up(Body &b, float t) const;
        void test_dn(Body &b, float t) const;

        void kiss_lf(Body &b, float t) const;
        void kiss_rt(Body &b, float t) const;
        void kiss_up(Body &b, float t) const;
        void kiss_dn(Body &b, float t) const;

        void solid_body(Body& b, float t) const;
        void ghost_body(Body& b, float t) const;
        void move_body(Body& b, float t) const;

 public:
        // The underlying grid can be accessed with the at() function from
        // Grid<bool>, giving the cell at the x,y coordinates, or with the
        // specialized at() function that gives the cell corresponding to the
        // position p, taking into account the size of the cells
        using itty::Grid<bool>::at;
        int at(const itty::Vec& p);

        // The physics step requires every body and engine that should be
        // simulated to be added every frame, and then the step() function to
        // be called. After the simulation step has happened, the body and
        // engine lists will be cleared.
        void add_body(Body *b);
        void add_engine(Body *b, itty::Vec n, float s, float a);
        void step(float t);

};

#endif // mosaic_phys_h
