/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
catcrafts.net

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
*/
module;

export module Crafter.Graphics:InputField;
import std;
import :Types;
import :Keys;
import :Font;
import :UI;
import :UIComponents;

// Tier 3: single-line text input. Same contract as the other UI components —
// state is a user-owned POD, the draw function is stateless presentation, and
// input feeding is done via free functions called from the host's own event
// listeners. Validation rules cover the common numeric cases (UInt/Int/Float)
// and accept "in-progress" intermediate states (lone '-', '.', "1e") so the
// user can type naturally; final-form parsing is on the caller via TryParse.

export namespace Crafter {
    enum class InputFieldType : std::uint8_t {
        Text,    // any UTF-8
        UInt,    // digits only
        Int,     // optional leading '-', then digits
        Float,   // signed float with optional exponent
    };

    struct InputFieldColors {
        std::array<float, 4> bg;
        std::array<float, 4> bgFocused;
        std::array<float, 4> border;
        std::array<float, 4> borderFocused;
        std::array<float, 4> text;
        std::array<float, 4> caret;
        float cornerRadius    = 6.0f;
        float borderThickness = 2.0f;
        float paddingX        = 8.0f;     // text inset from the rect's left edge
    };

    struct InputField {
        std::string    value;
        InputFieldType type      = InputFieldType::Text;
        bool           focused   = false;
        std::size_t    cursorPos = 0;     // byte offset into `value`
    };

    // Returns true if `s` is a valid candidate for the given type, including
    // "in-progress" states (lone '-', empty, "1e", "1.").
    bool InputField_IsValidCandidate(InputFieldType, std::string_view s);

    // Try to parse the field's current value into T. Returns nullopt for empty
    // or in-progress strings. Defined for arithmetic types via std::from_chars.
    template <class T>
    std::optional<T> InputField_TryParse(const InputField& f) {
        if (f.value.empty()) return std::nullopt;
        T out{};
        const char* begin = f.value.data();
        const char* end   = begin + f.value.size();
        auto [ptr, ec] = std::from_chars(begin, end, out);
        if (ec != std::errc{} || ptr != end) return std::nullopt;
        return out;
    }

    // Insert UTF-8 text at the cursor. The full would-be result is validated
    // against the field's type before committing — invalid candidates are
    // dropped silently. Cursor advances by the inserted byte count.
    void InputField_OnText(InputField&, std::string_view utf8);

    // Edit-control keys: Backspace, Delete, Left, Right, Home, End. Anything
    // else is ignored. Safe to feed every key the host receives. The KeyCode
    // is the raw platform key (Win32 PS/2 scancode + extended bit; Wayland
    // kernel keycode) — typically the value delivered by
    // `Window::onRawKeyDown`. Internal comparisons use `Key(CrafterKeys::X)`
    // from :Keys so the code stays cross-platform.
    void InputField_OnKey(InputField&, KeyCode);

    // Map a click x-coord (in window pixels) to a cursor position. `rect` is
    // the field's current draw rect; `colors.paddingX` is consulted for the
    // text-start offset.
    std::size_t InputField_HitTestCursor(const InputField&,
                                         Rect rect,
                                         float clickX,
                                         Font&, float fontSize,
                                         const InputFieldColors&);

    // Draw the field. `caretVisible` is provided by the caller so that blink
    // policy stays in user code (typical: `(steady_clock::now() / 500ms) & 1`).
    // The caret is only drawn if the field is focused AND caretVisible is true.
    void DrawInputField(UIBuffer& buf, const InputField& f, Rect rect,
                        Font& font, float fontSize,
                        const InputFieldColors& colors,
                        bool caretVisible);
}
