window.EditorTables = (function () {

    function repeat(ch, n) {
        var s = '';
        for (var i = 0; i < n; i++) s += ch;
        return s;
    }

    function padLeft(s, w) {
        while (s.length < w) s = ' ' + s;
        return s;
    }

    function padRight(s, w) {
        while (s.length < w) s += ' ';
        return s;
    }

    function padCenter(s, w) {
        while (s.length < w) {
            s = s + ' ';
            if (s.length < w) s = ' ' + s;
        }
        return s;
    }

    function parseTableRow(line) {
        var trimmed = line.trim();
        if (trimmed.charAt(0) === '|') trimmed = trimmed.slice(1);
        if (trimmed.charAt(trimmed.length - 1) === '|') trimmed = trimmed.slice(0, -1);
        return trimmed.split('|').map(function (c) { return c.trim(); });
    }

    function isSeparatorRow(cells) {
        return cells.every(function (c) { return /^:?-+:?$/.test(c); });
    }

    function parseAlignment(cell) {
        var left = cell.charAt(0) === ':';
        var right = cell.charAt(cell.length - 1) === ':';
        if (left && right) return 'center';
        if (left) return 'left';
        if (right) return 'right';
        return null;
    }

    function makeSepCell(width, align) {
        if (align === 'left') return ':' + repeat('-', width - 1);
        if (align === 'center') return ':' + repeat('-', width - 2) + ':';
        if (align === 'right') return repeat('-', width - 1) + ':';
        return repeat('-', width);
    }

    function findTableRange(text, cursorPos) {
        var lines = text.split('\n');
        var charCount = 0;
        var cursorLine = 0;
        for (var i = 0; i < lines.length; i++) {
            if (charCount + lines[i].length >= cursorPos && charCount <= cursorPos) {
                cursorLine = i;
            }
            charCount += lines[i].length + 1;
        }
        if (!/^\|.*\|$/.test(lines[cursorLine].trim())) return null;
        var start = cursorLine;
        while (start > 0 && /^\|.*\|$/.test(lines[start - 1].trim())) start--;
        var end = cursorLine;
        while (end < lines.length - 1 && /^\|.*\|$/.test(lines[end + 1].trim())) end++;
        if (end - start < 1) return null;
        return { start: start, end: end, lines: lines, cursorLine: cursorLine };
    }

    function formatTableText(text, cursorPos) {
        var range = findTableRange(text, cursorPos);
        if (!range) return null;

        var rows = [];
        var sepIndex = -1;
        var alignments = [];
        for (var i = range.start; i <= range.end; i++) {
            var cells = parseTableRow(range.lines[i]);
            if (sepIndex === -1 && isSeparatorRow(cells)) {
                sepIndex = rows.length;
                alignments = cells.map(function (c) { return parseAlignment(c); });
            }
            rows.push(cells);
        }
        if (sepIndex === -1) return null;

        var colCount = 0;
        rows.forEach(function (r) { if (r.length > colCount) colCount = r.length; });
        while (alignments.length < colCount) alignments.push(null);

        rows = rows.map(function (r) {
            while (r.length < colCount) r.push('');
            return r;
        });

        var widths = [];
        for (var c = 0; c < colCount; c++) {
            var max = 3;
            for (var r = 0; r < rows.length; r++) {
                if (r === sepIndex) continue;
                if (rows[r][c].length > max) max = rows[r][c].length;
            }
            widths.push(max);
        }

        var formatted = rows.map(function (row, ri) {
            var cells = row.map(function (cell, ci) {
                var w = widths[ci];
                if (ri === sepIndex) return makeSepCell(w, alignments[ci]);
                var align = alignments[ci];
                if (align === 'right') return padLeft(cell, w);
                if (align === 'center') return padCenter(cell, w);
                return padRight(cell, w);
            });
            return '| ' + cells.join(' | ') + ' |';
        });

        var beforeTable = range.lines.slice(0, range.start).join('\n');
        var afterTable = range.lines.slice(range.end + 1).join('\n');
        var parts = [];
        if (beforeTable) parts.push(beforeTable);
        parts.push(formatted.join('\n'));
        if (afterTable) parts.push(afterTable);
        var newText = parts.join('\n');

        var oldBeforeLen = 0;
        for (var i = 0; i < range.start; i++) oldBeforeLen += range.lines[i].length + 1;
        var cursorInTable = cursorPos - oldBeforeLen;
        var newTableText = formatted.join('\n');
        var newCursor = (beforeTable ? beforeTable.length + 1 : 0) + Math.min(cursorInTable, newTableText.length);

        return { text: newText, cursor: newCursor };
    }

    function getCursorColumn(text, cursorPos) {
        var range = findTableRange(text, cursorPos);
        if (!range) return null;
        var lines = text.split('\n');
        var charCount = 0;
        for (var i = 0; i < range.cursorLine; i++) charCount += lines[i].length + 1;
        var lineOffset = cursorPos - charCount;
        var line = lines[range.cursorLine];
        var col = -1;
        for (var c = 0; c < line.length; c++) {
            if (line.charAt(c) === '|') {
                col++;
                if (c >= lineOffset) return Math.max(col - 1, 0);
            }
        }
        return Math.max(col, 0);
    }

    function setColumnAlignment(text, cursorPos, align) {
        var range = findTableRange(text, cursorPos);
        if (!range) return null;
        var colIdx = getCursorColumn(text, cursorPos);
        if (colIdx === null) return null;

        var rows = [];
        var sepIndex = -1;
        for (var i = range.start; i <= range.end; i++) {
            var cells = parseTableRow(range.lines[i]);
            if (sepIndex === -1 && isSeparatorRow(cells)) sepIndex = rows.length;
            rows.push(cells);
        }
        if (sepIndex === -1 || colIdx >= rows[sepIndex].length) return null;

        var cell = rows[sepIndex][colIdx].replace(/:/g, '-');
        if (align === 'left') cell = ':' + cell.slice(1);
        else if (align === 'center') cell = ':' + cell.slice(1, -1) + ':';
        else if (align === 'right') cell = cell.slice(0, -1) + ':';
        rows[sepIndex][colIdx] = cell;

        var newLines = range.lines.slice();
        for (var i = 0; i < rows.length; i++) {
            newLines[range.start + i] = '| ' + rows[i].join(' | ') + ' |';
        }
        return formatTableText(newLines.join('\n'), cursorPos);
    }

    function insertColumn(text, cursorPos) {
        var range = findTableRange(text, cursorPos);
        if (!range) return null;
        var colIdx = getCursorColumn(text, cursorPos);
        if (colIdx === null) return null;

        var newLines = range.lines.slice();
        var sepIndex = -1;
        for (var i = range.start; i <= range.end; i++) {
            var cells = parseTableRow(range.lines[i]);
            var isSep = sepIndex === -1 && isSeparatorRow(cells);
            if (isSep) sepIndex = i;
            cells.splice(colIdx, 0, isSep ? '---' : '');
            newLines[i] = '| ' + cells.join(' | ') + ' |';
        }
        return formatTableText(newLines.join('\n'), cursorPos);
    }

    function deleteColumn(text, cursorPos) {
        var range = findTableRange(text, cursorPos);
        if (!range) return null;
        var colIdx = getCursorColumn(text, cursorPos);
        if (colIdx === null) return null;

        var newLines = range.lines.slice();
        for (var i = range.start; i <= range.end; i++) {
            var cells = parseTableRow(range.lines[i]);
            if (cells.length <= 1) return null;
            cells.splice(colIdx, 1);
            newLines[i] = '| ' + cells.join(' | ') + ' |';
        }
        return formatTableText(newLines.join('\n'), cursorPos);
    }

    function insertRow(text, cursorPos) {
        var range = findTableRange(text, cursorPos);
        if (!range) return null;
        var colCount = 0;
        for (var i = range.start; i <= range.end; i++) {
            var cells = parseTableRow(range.lines[i]);
            if (cells.length > colCount) colCount = cells.length;
        }
        var emptyCells = [];
        for (var c = 0; c < colCount; c++) emptyCells.push('');
        var newLines = range.lines.slice();
        newLines.splice(range.cursorLine + 1, 0, '| ' + emptyCells.join(' | ') + ' |');
        return formatTableText(newLines.join('\n'), cursorPos);
    }

    function insertRowBelow(text, cursorPos) {
        var range = findTableRange(text, cursorPos);
        if (!range) return null;
        var result = insertRow(text, cursorPos);
        if (!result) return null;
        var lines = result.text.split('\n');
        var cursor = 0;
        for (var i = 0; i <= range.cursorLine; i++) cursor += lines[i].length + 1;
        cursor += 2; // skip leading '| '
        return { text: result.text, cursor: cursor };
    }

    function deleteRow(text, cursorPos) {
        var range = findTableRange(text, cursorPos);
        if (!range) return null;
        var sepIndex = -1;
        for (var i = range.start; i <= range.end; i++) {
            if (isSeparatorRow(parseTableRow(range.lines[i]))) { sepIndex = i; break; }
        }
        if (range.cursorLine === range.start || range.cursorLine === sepIndex) return null;

        var newLines = range.lines.slice();
        newLines.splice(range.cursorLine, 1);
        var newCursor = cursorPos;
        if (range.cursorLine < newLines.length) {
            var charCount = 0;
            for (var i = 0; i < range.cursorLine; i++) charCount += newLines[i].length + 1;
            newCursor = charCount;
        }
        return formatTableText(newLines.join('\n'), Math.min(newCursor, newLines.join('\n').length));
    }

    return {
        formatTableText:    formatTableText,
        setColumnAlignment: setColumnAlignment,
        insertColumn:       insertColumn,
        deleteColumn:       deleteColumn,
        insertRow:          insertRow,
        insertRowBelow:     insertRowBelow,
        deleteRow:          deleteRow,
    };
})();
