import stripAnsi from 'strip-ansi';
import { truncateToWidth, visibleWidth } from '../../tui/index.js';
const SELECTION_BG = '\x1b[48;2;117;184;141m';
const SELECTION_FG = '\x1b[38;2;8;24;16m';
const COPY_HINT_BG = '\x1b[48;2;18;28;22m';
const COPY_HINT_FG = '\x1b[38;2;231;255;238m';
const COPY_HINT = ' Ctrl+C to copy ';
const RESET = '\x1b[0m';
function stripControlSequences(value) {
    return stripAnsi(value)
        .replace(/\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/g, '')
        .replace(/\x1b_[^\x07\x1b]*(?:\x07|\x1b\\)/g, '');
}
function renderSelectedLine(line, width, showCopyHint = false) {
    const plain = stripControlSequences(line);
    if (showCopyHint) {
        const hintWidth = visibleWidth(COPY_HINT);
        if (width > hintWidth) {
            const contentWidth = width - hintWidth;
            const selectedText = truncateToWidth(plain, contentWidth, '...', true);
            return `${SELECTION_BG}${SELECTION_FG}${selectedText}${COPY_HINT_BG}${COPY_HINT_FG}${COPY_HINT}${RESET}`;
        }
    }
    const padding = ' '.repeat(Math.max(0, width - visibleWidth(plain)));
    return `${SELECTION_BG}${SELECTION_FG}${plain}${padding}${RESET}`;
}
export class LineSelectionScrollContainer {
    source;
    viewportHeight;
    scrollTop = 0;
    sticky = true;
    contentHeight = 0;
    anchorContentRow;
    focusContentRow;
    selecting = false;
    selectionActive = false;
    lastPlainContentLines = [];
    lastVisibleStart = 0;
    lastVisibleRows = 0;
    lastRenderWidth = 0;
    constructor(source) {
        this.source = source;
    }
    setViewportHeight(height) {
        const nextHeight = Math.max(0, Math.floor(height));
        if (this.viewportHeight !== nextHeight) {
            this.viewportHeight = nextHeight;
            this.clamp();
        }
    }
    getViewportHeight() {
        return this.viewportHeight ?? this.contentHeight;
    }
    getScrollTop() {
        return this.scrollTop;
    }
    getContentHeight() {
        return this.contentHeight;
    }
    isAtBottom() {
        return this.scrollTop >= this.getMaxScrollTop();
    }
    scrollTo(scrollTop) {
        this.scrollTop = Math.max(0, Math.floor(scrollTop));
        this.sticky = false;
        this.clamp();
    }
    scrollBy(delta) {
        if (delta === 0)
            return;
        this.scrollTo(this.scrollTop + Math.floor(delta));
    }
    scrollPage(direction) {
        const page = Math.max(1, this.getViewportHeight() - 1);
        this.scrollBy(direction * page);
    }
    scrollToBottom() {
        this.sticky = true;
        this.scrollTop = this.getMaxScrollTop();
    }
    invalidate() {
        this.source.invalidate?.();
    }
    render(width) {
        const allLines = this.source.render(width);
        this.contentHeight = allLines.length;
        this.clamp();
        const height = this.getViewportHeight();
        const lines = height > 0 ? allLines.slice(this.scrollTop, this.scrollTop + height) : [];
        while (lines.length < height) {
            lines.push('');
        }
        this.lastVisibleStart = this.getScrollTop();
        this.lastVisibleRows = lines.length;
        this.lastRenderWidth = width;
        this.lastPlainContentLines = allLines.map((line) => stripControlSequences(line));
        if (!this.hasSelection()) {
            return lines;
        }
        const range = this.getSelectionRange();
        if (!range) {
            return lines;
        }
        const hintRow = Math.min(range.end, this.lastVisibleStart + lines.length - 1);
        return lines.map((line, index) => {
            const contentRow = this.lastVisibleStart + index;
            if (contentRow < range.start || contentRow > range.end) {
                return line;
            }
            return renderSelectedLine(line, width, contentRow === hintRow);
        });
    }
    startSelectionAtScreenRow(screenRow, screenCol) {
        const contentRow = this.screenRowToContentRow(screenRow, screenCol);
        if (contentRow === undefined)
            return false;
        this.anchorContentRow = contentRow;
        this.focusContentRow = contentRow;
        this.selecting = true;
        this.selectionActive = false;
        return true;
    }
    extendSelectionAtScreenRow(screenRow, screenCol) {
        if (!this.selecting)
            return false;
        const contentRow = this.screenRowToContentRowForDrag(screenRow, screenCol);
        if (contentRow === undefined)
            return false;
        this.focusContentRow = contentRow;
        this.selectionActive = true;
        return true;
    }
    finishSelection() {
        this.selecting = false;
    }
    clearSelection() {
        const hadSelection = this.hasSelection() || this.selecting;
        this.anchorContentRow = undefined;
        this.focusContentRow = undefined;
        this.selecting = false;
        this.selectionActive = false;
        return hadSelection;
    }
    hasSelection() {
        return (this.selectionActive &&
            this.anchorContentRow !== undefined &&
            this.focusContentRow !== undefined);
    }
    isSelecting() {
        return this.selecting;
    }
    getAutoScrollDeltaForScreenRow(screenRow) {
        if (!this.selecting || this.lastVisibleRows <= 0) {
            return 0;
        }
        if (screenRow <= 0) {
            return Math.max(-8, screenRow - 2);
        }
        if (screenRow === 1) {
            return -3;
        }
        if (screenRow === 2) {
            return -1;
        }
        if (screenRow >= this.lastVisibleRows + 1) {
            return Math.min(8, screenRow - this.lastVisibleRows + 2);
        }
        if (screenRow === this.lastVisibleRows) {
            return 3;
        }
        if (screenRow === this.lastVisibleRows - 1) {
            return 1;
        }
        return 0;
    }
    extendSelectionByAutoScroll(delta) {
        if (!this.selecting || delta === 0) {
            return false;
        }
        const before = this.getScrollTop();
        this.scrollBy(delta);
        if (this.getScrollTop() === before) {
            return false;
        }
        this.focusContentRow =
            delta < 0
                ? this.getScrollTop()
                : Math.min(this.contentHeight - 1, this.getScrollTop() + this.lastVisibleRows - 1);
        this.selectionActive = true;
        return true;
    }
    getSelectedText() {
        const range = this.getSelectionRange();
        if (!range)
            return '';
        const selected = [];
        for (let row = range.start; row <= range.end; row++) {
            if (row < 0 || row >= this.lastPlainContentLines.length) {
                continue;
            }
            selected.push(this.lastPlainContentLines[row].trimEnd());
        }
        return selected.join('\n');
    }
    screenRowToContentRow(screenRow, screenCol) {
        const visibleRow = screenRow - 1;
        if (visibleRow < 0 || visibleRow >= this.lastVisibleRows) {
            return undefined;
        }
        if (screenCol < 1 || screenCol > this.lastRenderWidth) {
            return undefined;
        }
        const contentRow = this.lastVisibleStart + visibleRow;
        return Math.max(0, contentRow);
    }
    screenRowToContentRowForDrag(screenRow, screenCol) {
        if (this.lastVisibleRows <= 0) {
            return undefined;
        }
        if (screenCol < 1 || screenCol > this.lastRenderWidth) {
            return undefined;
        }
        if (screenRow <= 1) {
            return this.getScrollTop();
        }
        if (screenRow >= this.lastVisibleRows) {
            return Math.max(0, this.getScrollTop() + this.lastVisibleRows - 1);
        }
        const contentRow = this.lastVisibleStart + screenRow - 1;
        return Math.max(0, contentRow);
    }
    getMaxScrollTop() {
        return Math.max(0, this.contentHeight - this.getViewportHeight());
    }
    clamp() {
        const maxScrollTop = this.getMaxScrollTop();
        if (this.sticky) {
            this.scrollTop = maxScrollTop;
        }
        else {
            this.scrollTop = Math.max(0, Math.min(this.scrollTop, maxScrollTop));
            if (this.scrollTop === maxScrollTop) {
                this.sticky = true;
            }
        }
    }
    getSelectionRange() {
        if (this.anchorContentRow === undefined || this.focusContentRow === undefined) {
            return undefined;
        }
        return {
            start: Math.min(this.anchorContentRow, this.focusContentRow),
            end: Math.max(this.anchorContentRow, this.focusContentRow)
        };
    }
}
//# sourceMappingURL=line-selection-scroll-container.js.map