#ifndef DISPLAY_HPP_
#define DISPLAY_HPP_

#include <memory>
#include <stack>
#include <GraphicsContext/GraphicsContext.hpp>

/**
 * @brief Abstract parent class for all displays.
 */
class Display
{
private:
    static std::stack<std::shared_ptr<Display>> navStack;

public:
    virtual ~Display() = default;

    /**
     * @brief Draw the display.
     * 
     * @param context The graphics context.
     */
    virtual void draw(GraphicsContext &context) = 0;
    
    /**
     * @brief Update the display. This is called at a fixed rate.
     */
    virtual void update() = 0;

    /**
     * @brief Update the display. This is called at an unlimited rate.
     */
    virtual void fastUpdate();

    /**
     * @brief Handle an event.
     * 
     * If overriding this, it is recommended to call the supermethod for
     * unhandled events.
     * 
     * @param ev The event.
     */
    virtual void onEvent(SDL_Event &event);

    /**
     * @brief Called from the default handler when the user attempts to quit.
     * 
     * By defualt actually quits.
     */
    virtual void onQuit();

    /**
     * @brief Called after a `navPop` when this display becomes the top of the stack.
     * 
     * By default does nothing.
     */
    virtual void onExposed();

    /**
     * @brief Check whether a key is pressed.
     * 
     * @param keyCode The key code.
     * @return true If the key is currently down.
     * @return false If the key is currently up.
     */
    bool isKeyDown(SDL_Scancode keyCode);

    /**
     * @brief Set the current display to a new display.
     * 
     * This deletes the current display object, and constructs a new one of the
     * specified type, and makes it the current display.
     * 
     * @tparam TDisplay The display type (must inherit from `Display`).
     * @tparam ConstructorArgs Constructor argument types.
     * @param args The args constructor.
     */
    template<typename TDisplay, typename... TConstructorArgs>
    static void setCurrent(TConstructorArgs&&... args)
    {
        navStack.top() = std::make_shared<TDisplay>(args...);
    }

    /**
     * @brief Push a new display to the top of the nav stack.
     * 
     * @tparam TDisplay The display type (must inherit from `Display`).
     * @tparam ConstructorArgs Constructor argument types.
     * @param args The args constructor.
     */
    template<typename TDisplay, typename... TConstructorArgs>
    static void navPush(TConstructorArgs&&... args)
    {
        navStack.push(std::make_shared<TDisplay>(args...));
    }

    /**
     * @brief Get the shared pointer to the current display.
     */
    static std::shared_ptr<Display> getCurrent();

    /**
     * @brief Return to the previous display.
     */
    static void navPop();
};

#endif