#pragma once

#include "../Identifiers.h"
#include "../world/Location.hpp"

namespace OpenRCT2
{
    class DataSerialiser;

    enum class EntityType : uint8_t
    {
        vehicle,
        guest,
        staff,
        litter,
        steamParticle,
        moneyEffect,
        crashedVehicleParticle,
        explosionCloud,
        crashSplash,
        explosionFlare,
        jumpingFountain,
        balloon,
        duck,
        count,
        null = 255
    };

    struct EntitySpriteData
    {
        // Width from centre of sprite to edge
        uint8_t Width;
        // Height from centre of sprite to bottom
        uint8_t HeightMin;
        // Height from centre of sprite to top
        uint8_t HeightMax;
        // Screen Coordinates of sprite
        ScreenRect SpriteRect;
    };

    struct EntityBase
    {
        EntityType Type;
        EntityId Id;
        int32_t x;
        int32_t y;
        int32_t z;
        EntitySpriteData SpriteData;
        // Used as direction or rotation depending on the entity.
        uint8_t Orientation;
        uint32_t SpatialIndex;

        /**
         * Moves a sprite to a new location, invalidates the current position if valid
         * and also the new position.
         *
         *  rct2: 0x0069E9D3
         */
        void MoveTo(const CoordsXYZ& newLocation);

        void MoveToAndUpdateSpatialIndex(const CoordsXYZ& newLocation);

        /**
         * Sets the entity location without screen invalidation.
         */
        void SetLocation(const CoordsXYZ& newLocation);

        /**
         * Gets the entity current location.
         */
        CoordsXYZ GetLocation() const;

        void Invalidate();
        template<typename T>
        bool Is() const;
        template<typename T>
        T* As()
        {
            return Is<T>() ? reinterpret_cast<T*>(this) : nullptr;
        }
        template<typename T>
        const T* As() const
        {
            return Is<T>() ? reinterpret_cast<const T*>(this) : nullptr;
        }

        template<typename T>
        T* cast()
        {
            return reinterpret_cast<T*>(this);
        }

        template<typename T>
        const T* cast() const
        {
            return reinterpret_cast<const T*>(this);
        }

        void Serialise(class DataSerialiser& stream);

        void Paint() const;
    };
} // namespace OpenRCT2
