/*
 * Custom Terminal Emulator for Sai
 * 
 * Provides an abstract interface for passing buffers of data in and out,
 * marked with stdxxx.
 */

class SaiTerminal {
    constructor(container, options = {}) {
        this.container = container;
        this.options = Object.assign({
            onData: (data) => {},
            onClose: () => {}
        }, options);

        this.el = document.createElement('div');
        this.el.className = 'sai-terminal';
        
        // Window furniture
        this.titleBar = document.createElement('div');
        this.titleBar.className = 'sai-terminal-titlebar';
        
        this.titleText = document.createElement('span');
        this.titleText.className = 'sai-terminal-title';
        this.titleText.textContent = this.options.title || "Terminal";
        this.titleBar.appendChild(this.titleText);

        this.closeBtn = document.createElement('button');
        this.closeBtn.className = 'sai-terminal-close';
        this.closeBtn.textContent = 'X';
        this.closeBtn.onclick = () => {
            this.options.onClose();
            this.destroy();
        };
        this.titleBar.appendChild(this.closeBtn);
        this.el.appendChild(this.titleBar);

        this.contentEl = document.createElement('div');
        this.contentEl.className = 'sai-terminal-content';
        
        this.outputEl = document.createElement('div');
        this.outputEl.className = 'sai-terminal-output';
        this.contentEl.appendChild(this.outputEl);
        
        this.inputEl = document.createElement('input');
        this.inputEl.className = 'sai-terminal-input';
        this.inputEl.type = 'text';
        // Hide the input, we use it just for focus/typing
        this.inputEl.style.opacity = '0';
        this.inputEl.style.position = 'absolute';
        this.inputEl.style.left = '-9999px';
        this.contentEl.appendChild(this.inputEl);

        this.el.appendChild(this.contentEl);

        this.container.appendChild(this.el);

        this.core = new TerminalCore(80, 24);
        
        // Setup ResizeObserver to dynamically resize
        this.resizeObserver = new ResizeObserver(entries => {
            for (let entry of entries) {
                // Approximate character size (e.g., 8x16)
                // This would be better if dynamically measured
                const cols = Math.max(10, Math.floor(entry.contentRect.width / 8.5));
                const rows = Math.max(5, Math.floor(entry.contentRect.height / 16));
                if (cols !== this.core.cols || rows !== this.core.rows) {
                    this.core.resize(cols, rows);
                    this.render();
                }
            }
        });
        this.resizeObserver.observe(this.contentEl);

        // Dragging logic
        let isDragging = false;
        let dragStartX, dragStartY;
        let startLeft, startTop;

        this.titleBar.addEventListener('mousedown', (e) => {
            if (e.target === this.closeBtn) return;
            isDragging = true;
            dragStartX = e.clientX;
            dragStartY = e.clientY;
            
            const rect = this.el.getBoundingClientRect();
            startLeft = rect.left;
            startTop = rect.top;
            
            this.el.style.left = startLeft + 'px';
            this.el.style.top = startTop + 'px';
            this.el.style.transform = 'none';
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            const dx = e.clientX - dragStartX;
            const dy = e.clientY - dragStartY;
            this.el.style.left = (startLeft + dx) + 'px';
            this.el.style.top = (startTop + dy) + 'px';
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
        });

        this.inputEl.addEventListener('keydown', (e) => {
            if (e.ctrlKey) {
                if (e.key === 'c') {
                    this.options.onData('\x03');
                    e.preventDefault();
                } else if (e.key === 'd') {
                    this.options.onData('\x04');
                    e.preventDefault();
                } else if (e.key === 'z') {
                    this.options.onData('\x1a');
                    e.preventDefault();
                } else if (e.key === 'l') {
                    this.options.onData('\x0c');
                    e.preventDefault();
                }
            } else if (e.key === 'Enter') {
                this.options.onData('\r');
                e.preventDefault();
            } else if (e.key === 'Backspace') {
                this.options.onData('\x7f');
                e.preventDefault();
            } else if (e.key === 'Tab') {
                this.options.onData('\t');
                e.preventDefault();
            } else if (e.key === 'Escape') {
                this.options.onData('\x1b');
                e.preventDefault();
            } else if (e.key.startsWith('Arrow')) {
                if (e.key === 'ArrowUp') this.options.onData('\x1b[A');
                else if (e.key === 'ArrowDown') this.options.onData('\x1b[B');
                else if (e.key === 'ArrowRight') this.options.onData('\x1b[C');
                else if (e.key === 'ArrowLeft') this.options.onData('\x1b[D');
                e.preventDefault();
            } else if (e.key.length === 1 && !e.metaKey && !e.altKey) {
                // Send regular characters immediately
                this.options.onData(e.key);
                e.preventDefault();
            }
        });
        
        // click to focus input
        this.el.addEventListener('click', () => {
            const selection = window.getSelection();
            if (!selection.toString()) {
                this.inputEl.focus();
            }
        });
    }

    write(channel, data) {
        if (channel === 1 || channel === 2 || channel === 0) {
            this.core.write(data);
            this.render();
        }
    }

    render() {
        this.outputEl.innerHTML = this.core.renderHtml();
    }

    focus() {
        this.inputEl.focus();
    }
    
    destroy() {
        this.resizeObserver.disconnect();
        if (this.el.parentNode) {
            this.el.parentNode.removeChild(this.el);
        }
    }
}