/*
  MenuItem.h - MenuItem declaration and definition

  MIT License

  Copyright (c) 2020-2024 Forntoh Thomas

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

#ifndef MenuItem_H
#define MenuItem_H

#define ITEM_DRAW_BUFFER_SIZE 64

#include "renderer/GraphicalMenuItem.h"
#include "renderer/MenuRenderer.h"
#include "utils/lcd_menu_constants.h"
#include <utils/lcd_menu_utils.h>

class LcdMenu;
class MenuScreen;

/**
 * @class MenuItem
 * @brief Represent single item in menu.
 *
 * ```
 * ┌────────────────────────────┐
 * │ > T E X T                  │
 * └────────────────────────────┘
 * ```
 */
class MenuItem {
    friend MenuScreen;

  protected:
    const char* text = NULL;
    bool polling = false;

    static bool _isEditing;

  public:
    static bool isEditing();
    static void beginEdit();
    static void endEdit();
    MenuItem(const char* text) : text(text) {}
    /**
     * @brief Get the text of the item
     * @return `String` - Item's text
     */
    const char* getText() {
        return text;
    }
    /**
     * @brief Set the text of the item
     * @note You need to call `LcdMenu::refresh` after this method to see the changes.
     * @param text text to display for the item
     */
    void setText(const char* text) {
        this->text = text;
    };

    /**
     * @brief Check if the item can be selected by the cursor.
     */
    virtual bool isSelectable() const { return true; }

    // Destructor
    virtual ~MenuItem() noexcept = default;

    /**
     * @brief Optional capability lookup for item-specific extensions.
     *
     * Returns NULL when capability is not supported.
     */
    virtual const void* queryCapability(uint8_t capabilityId) const {
        (void)capabilityId;
        return NULL;
    }

  protected:
    /**
     * @brief The number of available columns for the potential value of the item.
     *
     * ```
     *          visible area
     *        ┌───────────────┐
     * X X X X│X X X X █ X X X│X X
     *        ├───────────────┤
     *        │<── viewSize ─>│
     * ```
     *
     * Effectively const, but initialized lately when renderer is injected.
     */
    inline uint8_t getViewSize(MenuRenderer* renderer) const {
        const char* label = text == NULL ? "" : text;
        int16_t viewSize = static_cast<int16_t>(renderer->getEffectiveCols()) - static_cast<int16_t>(strlen(label)) - 1 + renderer->viewShift;
        return viewSize > 0 ? static_cast<uint8_t>(viewSize) : 0;
    };
    /**
     * @brief Process a command decoded in 1 byte.
     * It can be a printable character or a control command like `ENTER` or `LEFT`.
     * Return value is used to determine operation was successful or ignored.
     * If the parent of item received that handle was ignored it can execute it's own action on this command.
     * Thus, the item always has priority in processing; if it is ignored, it is delegated to the parent element.
     * Behaviour is very similar to Even Bubbling in JavaScript.
     * @param menu the owner menu of the item, can be used to retrieve required object, such as `DisplayInterface` or `MenuScreen`
     * @param command the character command, can be a printable character or a control command
     * @return true if command was successfully handled by item.
     */
    virtual bool process(LcdMenu* menu, const unsigned char command) {
        return false;
    };
    /**
     * @brief Draw this menu item on specified display on current row.
     * @param renderer The renderer to use for drawing.
     */
    virtual void draw(MenuRenderer* renderer) {
        renderer->drawItem(text, NULL);
    };
};

class BasicItem : public MenuItem, public GraphicalMenuItem {
  public:
    explicit BasicItem(const char* text) : MenuItem(text) {}

    const void* queryCapability(uint8_t capabilityId) const override {
        if (capabilityId == GraphicalMenuItem::capabilityId()) {
            return static_cast<const GraphicalMenuItem*>(this);
        }
        return MenuItem::queryCapability(capabilityId);
    }
};

#define ITEM_BASIC(...) (new BasicItem(__VA_ARGS__))

#endif
