//
// Created by robtor on 26.01.23.
//

#ifndef FLOATPUMP_MENU_H
#define FLOATPUMP_MENU_H

#include <cstdint>
#include <string>
#include <vector>
#include <memory>

namespace floatpump {
    namespace menu {

        struct IMenuEntry {
            virtual ~IMenuEntry() = default;

            virtual auto u_press() -> void = 0;

            virtual auto u_increase(uint16_t steps = 1) -> void = 0;

            virtual auto u_decrease(uint16_t steps = 1) -> void = 0;

            virtual auto is_entered() -> bool = 0;

            virtual auto toString() -> std::string = 0;
        };

        template<typename T>
        class MenuEntry : public IMenuEntry {
        public:
            MenuEntry(T *linked_config, std::string name) : m_name(name) {
                m_storage = linked_config;
            };

            auto getValue() -> T & {
                return (*m_storage);
            }

            auto setValue(T &value) -> void {
                *m_storage = value;
            }

            auto relinkConfig(T *linked_config) -> void {
                m_storage = linked_config;
            }

        protected:
            T *m_storage;
            std::string m_name;
            bool m_entered = false;

            auto buildString(std::string appendix) -> std::string {
                int spaces = 19 - (m_name.length() + appendix.length());
                std::string spacer;
                if (spaces > 0)
                    spacer.append(spaces, ' ');
                else if (spaces < 0)
                    return "ERROR";

                return {m_name + spacer + appendix};
            }
        };



        class MenuEntryCheckable : public MenuEntry<bool> {
        public:
            MenuEntryCheckable(bool *linked_config, const std::string &name) : MenuEntry(linked_config, name) {}

            auto toString() -> std::string override;

            auto u_press() -> void override;

            auto u_increase(uint16_t steps) -> void override {}

            auto u_decrease(uint16_t steps) -> void override {}

            auto is_entered() -> bool override {
                return false;
            }
        };

        class MenuEntryExecute : public MenuEntry<bool> {
        public:
            MenuEntryExecute(bool *linked_config, const std::string &name) : MenuEntry(linked_config, name) {}

            auto u_press() -> void override;

            auto u_increase(uint16_t steps) -> void override {}

            auto u_decrease(uint16_t steps) -> void override {}

            auto is_entered() -> bool override {
                return false;
            }

            auto toString() -> std::string override;
        };

        class MenuEntryNumeric : public MenuEntry<uint16_t> {
        public:
            MenuEntryNumeric(uint16_t *linked_config, const std::string &name) : MenuEntry(linked_config, name) {}

            auto u_press() -> void override;

            auto u_increase(uint16_t steps) -> void override;

            auto u_decrease(uint16_t steps) -> void override;

            auto is_entered() -> bool override;

            auto toString() -> std::string override;
        };

        class MenuEntryPercent : public MenuEntry<uint8_t> {
        public:
            MenuEntryPercent(uint8_t *linked_config, const std::string &name) : MenuEntry(linked_config, name) {}

            auto u_press() -> void override;

            auto u_increase(uint16_t steps) -> void override;

            auto u_decrease(uint16_t steps) -> void override;

            auto is_entered() -> bool override;

            auto toString() -> std::string override;
        };

        class Menu {
        public:
            explicit Menu(const std::string &m_name) : m_name(m_name) {}

            enum class Error {
                invalid_index
            };

            template<typename T, typename... As>
            auto addEntry(As &&...args) -> void {
                m_entries.push_back(std::make_unique<T>(std::forward<As>(args)...));

            }

            auto addSubmenu(Menu &menu) -> void {
                m_submenus.emplace_back(std::make_unique<Menu>(std::move(menu)))->m_parent = this;
            }

            auto printLine() -> const std::string {
                return m_name;
            }

            auto getDisplayList() -> std::vector<std::string>;

            auto getAllEntriesNum() -> unsigned const int {
                return (m_entries.size() + m_submenus.size()) + ((m_parent != nullptr) ? 1 : 0);
            }

            auto getSubmenu(int index) -> Menu *;

            auto getEntry(int index) -> IMenuEntry* ;

            auto getParent() -> Menu * {
                return m_parent;
            }

        private:
            std::vector<std::unique_ptr<IMenuEntry>> m_entries;
            std::vector<std::unique_ptr<Menu>> m_submenus;
            Menu *m_parent = nullptr;
            std::string m_name;
        };


    }
}


#endif //FLOATPUMP_MENU_H
