// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
//
// ConsoleTextLayout — pure (ImGui-free) word-wrap layout + hit-testing + selection
// extraction for the console output. Text measurement is injected via ConsoleTextMeasure
// so the production path uses ImFont exactly as before while tests drive it with a
// fixed-width stub. Extracted from ConsoleTab::renderOutput / screenToTextPos /
// getSelectedText so the trickiest console logic (byte-offset selection across wrapped
// rows) becomes unit-testable.

#pragma once

#include <functional>
#include <string>
#include <vector>

namespace dragonx {
namespace ui {

// One visual sub-row of a (possibly wrapped) logical line.
struct ConsoleWrapSegment {
    int   byteStart = 0;  // byte offset into the line's text
    int   byteEnd   = 0;  // byte offset past the last char on this sub-row
    float yOffset   = 0;  // Y offset of this sub-row relative to the line's top
    float height    = 0;  // visual height of this sub-row
};

// The wrap layout for a set of visible lines.
struct ConsoleLayout {
    std::vector<std::vector<ConsoleWrapSegment>> segments;  // per visible line
    std::vector<float> heights;       // per visible line (includes the inter-line gap)
    std::vector<float> cumulativeY;   // cumulative Y offset of each visible line
    float totalHeight = 0.0f;
};

struct ConsoleTextPos { int line = -1; int col = 0; };  // line = raw index; col = byte offset
struct ConsoleHit     { int visibleRow = 0; int col = 0; };  // visibleRow = index into the visible set

// Text measurement backend. Production wraps ImFont; tests inject a fixed-width stub.
struct ConsoleTextMeasure {
    virtual ~ConsoleTextMeasure() = default;
    // Pixel width of the byte range [begin, end).
    virtual float width(const char* begin, const char* end) const = 0;
    // The byte position at which [begin, end) should wrap for the given width — mirrors
    // ImFont::CalcWordWrapPositionA. Returns `end` when the whole range fits.
    virtual const char* wrapPosition(const char* begin, const char* end, float wrapWidth) const = 0;
};

// Accessor returning a visible/raw line's text by index (avoids copying the buffer).
using ConsoleLineAccessor = std::function<const std::string&(int)>;

// Build the wrap layout for `visibleCount` lines (text via getVisibleText(0..N-1)).
ConsoleLayout BuildConsoleLayout(int visibleCount, const ConsoleLineAccessor& getVisibleText,
                                 float wrapWidth, float lineHeight, float interLineGap,
                                 const ConsoleTextMeasure& measure);

// Map a point relative to the layout origin to a (visibleRow, byte col). The caller maps
// visibleRow -> a raw line index. Returns {0,0} when there are no visible lines.
ConsoleHit HitTestConsoleLayout(const ConsoleLayout& layout, int visibleCount,
                                const ConsoleLineAccessor& getVisibleText,
                                float relX, float relY,
                                const ConsoleTextMeasure& measure);

// Extract the selected text between raw positions [start, end], restricted to visible
// lines when a filter is active, joining lines with '\n'. Pure.
std::string ExtractConsoleSelection(int lineCount, const ConsoleLineAccessor& getLine,
                                    const std::vector<int>& visibleIndices,
                                    ConsoleTextPos start, ConsoleTextPos end);

} // namespace ui
} // namespace dragonx
