/**
 * Visual Workspace
 *
 * Extended canvas system for creating rich visual workspaces with
 * elements like diagrams, charts, code blocks, and images.
 */
import { EventEmitter } from 'events';
import * as crypto from 'crypto';
/**
 * Default workspace configuration
 */
export const DEFAULT_WORKSPACE_CONFIG = {
    name: 'Untitled Workspace',
    width: 1920,
    height: 1080,
    backgroundColor: '#ffffff',
    gridSize: 20,
    showGrid: true,
    snapToGrid: true,
    zoom: 1,
    panOffset: { x: 0, y: 0 },
    maxHistory: 50,
};
// ============================================================================
// Visual Workspace Manager
// ============================================================================
/**
 * Manages visual workspaces
 */
export class VisualWorkspaceManager extends EventEmitter {
    workspaces = new Map();
    history = new Map();
    historyIndex = new Map();
    /**
     * Create a new workspace
     */
    createWorkspace(config = {}) {
        const workspace = {
            id: crypto.randomUUID(),
            config: { ...DEFAULT_WORKSPACE_CONFIG, ...config },
            elements: [],
            selectedIds: [],
            createdAt: new Date(),
            updatedAt: new Date(),
            version: 1,
        };
        this.workspaces.set(workspace.id, workspace);
        this.history.set(workspace.id, []);
        this.historyIndex.set(workspace.id, -1);
        this.emit('workspace-created', workspace);
        return workspace;
    }
    /**
     * Get a workspace by ID
     */
    getWorkspace(workspaceId) {
        return this.workspaces.get(workspaceId);
    }
    /**
     * Get all workspaces
     */
    getAllWorkspaces() {
        return Array.from(this.workspaces.values());
    }
    /**
     * Delete a workspace
     */
    deleteWorkspace(workspaceId) {
        const deleted = this.workspaces.delete(workspaceId);
        if (deleted) {
            this.history.delete(workspaceId);
            this.historyIndex.delete(workspaceId);
            this.emit('workspace-deleted', workspaceId);
        }
        return deleted;
    }
    /**
     * Add an element to workspace
     */
    addElement(workspaceId, type, content, position, size, style) {
        const workspace = this.workspaces.get(workspaceId);
        if (!workspace)
            throw new Error(`Workspace ${workspaceId} not found`);
        const element = {
            id: crypto.randomUUID(),
            type,
            content,
            position: this.snapToGrid(workspace, position),
            size: this.snapSizeToGrid(workspace, size),
            zIndex: workspace.elements.length,
            locked: false,
            visible: true,
            style,
            createdAt: new Date(),
            updatedAt: new Date(),
        };
        workspace.elements.push(element);
        workspace.updatedAt = new Date();
        workspace.version++;
        this.addHistoryEntry(workspaceId, {
            action: 'add',
            elementIds: [element.id],
            previousState: [],
            newState: [element],
            timestamp: new Date(),
        });
        this.emit('element-added', element);
        return element;
    }
    /**
     * Update an element
     */
    updateElement(workspaceId, elementId, updates) {
        const workspace = this.workspaces.get(workspaceId);
        if (!workspace)
            return null;
        const index = workspace.elements.findIndex((e) => e.id === elementId);
        if (index === -1)
            return null;
        const previousState = { ...workspace.elements[index] };
        const updatedElement = {
            ...workspace.elements[index],
            ...updates,
            id: elementId,
            updatedAt: new Date(),
        };
        workspace.elements[index] = updatedElement;
        workspace.updatedAt = new Date();
        workspace.version++;
        this.addHistoryEntry(workspaceId, {
            action: 'update',
            elementIds: [elementId],
            previousState: [previousState],
            newState: [updatedElement],
            timestamp: new Date(),
        });
        this.emit('element-updated', updatedElement);
        return updatedElement;
    }
    /**
     * Delete an element
     */
    deleteElement(workspaceId, elementId) {
        const workspace = this.workspaces.get(workspaceId);
        if (!workspace)
            return false;
        const index = workspace.elements.findIndex((e) => e.id === elementId);
        if (index === -1)
            return false;
        const deletedElement = workspace.elements[index];
        workspace.elements.splice(index, 1);
        workspace.selectedIds = workspace.selectedIds.filter((id) => id !== elementId);
        workspace.updatedAt = new Date();
        workspace.version++;
        this.addHistoryEntry(workspaceId, {
            action: 'delete',
            elementIds: [elementId],
            previousState: [deletedElement],
            newState: [],
            timestamp: new Date(),
        });
        this.emit('element-deleted', elementId);
        return true;
    }
    /**
     * Move an element
     */
    moveElement(workspaceId, elementId, position) {
        const workspace = this.workspaces.get(workspaceId);
        if (!workspace)
            return null;
        const element = workspace.elements.find((e) => e.id === elementId);
        if (!element || element.locked)
            return null;
        return this.updateElement(workspaceId, elementId, {
            position: this.snapToGrid(workspace, position),
        });
    }
    /**
     * Resize an element
     */
    resizeElement(workspaceId, elementId, size) {
        const workspace = this.workspaces.get(workspaceId);
        if (!workspace)
            return null;
        const element = workspace.elements.find((e) => e.id === elementId);
        if (!element || element.locked)
            return null;
        return this.updateElement(workspaceId, elementId, {
            size: this.snapSizeToGrid(workspace, size),
        });
    }
    /**
     * Snap position to grid
     */
    snapToGrid(workspace, position) {
        if (!workspace.config.snapToGrid || workspace.config.gridSize === 0) {
            return position;
        }
        const gridSize = workspace.config.gridSize;
        return {
            x: Math.round(position.x / gridSize) * gridSize,
            y: Math.round(position.y / gridSize) * gridSize,
        };
    }
    /**
     * Snap size to grid
     */
    snapSizeToGrid(workspace, size) {
        if (!workspace.config.snapToGrid || workspace.config.gridSize === 0) {
            return size;
        }
        const gridSize = workspace.config.gridSize;
        return {
            width: Math.max(gridSize, Math.round(size.width / gridSize) * gridSize),
            height: Math.max(gridSize, Math.round(size.height / gridSize) * gridSize),
        };
    }
    /**
     * Add history entry
     */
    addHistoryEntry(workspaceId, entry) {
        const workspace = this.workspaces.get(workspaceId);
        const hist = this.history.get(workspaceId);
        const idx = this.historyIndex.get(workspaceId);
        if (!workspace || !hist || idx === undefined)
            return;
        hist.splice(idx + 1);
        hist.push(entry);
        while (hist.length > workspace.config.maxHistory) {
            hist.shift();
        }
        this.historyIndex.set(workspaceId, hist.length - 1);
    }
    /**
     * Undo last action
     */
    undo(workspaceId) {
        const workspace = this.workspaces.get(workspaceId);
        const hist = this.history.get(workspaceId);
        const idx = this.historyIndex.get(workspaceId);
        if (!workspace || !hist || idx === undefined || idx < 0)
            return false;
        const entry = hist[idx];
        this.historyIndex.set(workspaceId, idx - 1);
        for (let i = 0; i < entry.elementIds.length; i++) {
            const elementId = entry.elementIds[i];
            if (entry.action === 'add') {
                workspace.elements = workspace.elements.filter((e) => e.id !== elementId);
            }
            else if (entry.action === 'delete') {
                workspace.elements.push(entry.previousState[i]);
            }
            else {
                const idx = workspace.elements.findIndex((e) => e.id === elementId);
                if (idx >= 0) {
                    workspace.elements[idx] = entry.previousState[i];
                }
            }
        }
        workspace.updatedAt = new Date();
        workspace.version++;
        this.emit('undo', entry);
        return true;
    }
    /**
     * Redo last undone action
     */
    redo(workspaceId) {
        const hist = this.history.get(workspaceId);
        const idx = this.historyIndex.get(workspaceId);
        if (!hist || idx === undefined || idx >= hist.length - 1)
            return false;
        const workspace = this.workspaces.get(workspaceId);
        if (!workspace)
            return false;
        this.historyIndex.set(workspaceId, idx + 1);
        const entry = hist[idx + 1];
        for (let i = 0; i < entry.elementIds.length; i++) {
            const elementId = entry.elementIds[i];
            if (entry.action === 'delete') {
                workspace.elements = workspace.elements.filter((e) => e.id !== elementId);
            }
            else if (entry.action === 'add') {
                workspace.elements.push(entry.newState[i]);
            }
            else {
                const idx = workspace.elements.findIndex((e) => e.id === elementId);
                if (idx >= 0) {
                    workspace.elements[idx] = entry.newState[i];
                }
            }
        }
        workspace.updatedAt = new Date();
        workspace.version++;
        this.emit('redo', entry);
        return true;
    }
    /**
     * Render workspace to terminal
     */
    renderToTerminal(workspaceId, width = 80, height = 24) {
        const workspace = this.workspaces.get(workspaceId);
        if (!workspace)
            return '';
        const buffer = Array(height)
            .fill(null)
            .map(() => Array(width).fill(' '));
        const scaleX = width / workspace.config.width;
        const scaleY = height / workspace.config.height;
        // Draw grid
        if (workspace.config.showGrid) {
            const gridX = Math.max(1, Math.floor(workspace.config.gridSize * scaleX));
            const gridY = Math.max(1, Math.floor(workspace.config.gridSize * scaleY));
            for (let y = 0; y < height; y += gridY) {
                for (let x = 0; x < width; x++) {
                    if (buffer[y])
                        buffer[y][x] = '·';
                }
            }
        }
        // Draw elements
        const sortedElements = [...workspace.elements]
            .sort((a, b) => a.zIndex - b.zIndex)
            .filter((e) => e.visible);
        for (const element of sortedElements) {
            const x1 = Math.floor(element.position.x * scaleX);
            const y1 = Math.floor(element.position.y * scaleY);
            const x2 = Math.min(width - 1, Math.floor((element.position.x + element.size.width) * scaleX));
            const y2 = Math.min(height - 1, Math.floor((element.position.y + element.size.height) * scaleY));
            // Draw border
            for (let x = x1; x <= x2; x++) {
                if (buffer[y1])
                    buffer[y1][x] = '─';
                if (buffer[y2])
                    buffer[y2][x] = '─';
            }
            for (let y = y1; y <= y2; y++) {
                if (buffer[y]) {
                    buffer[y][x1] = '│';
                    buffer[y][x2] = '│';
                }
            }
            // Corners
            if (buffer[y1]) {
                buffer[y1][x1] = '┌';
                buffer[y1][x2] = '┐';
            }
            if (buffer[y2]) {
                buffer[y2][x1] = '└';
                buffer[y2][x2] = '┘';
            }
            // Label
            let label = element.label || `[${element.type}]`;
            const maxLen = x2 - x1 - 2;
            if (label.length > maxLen) {
                label = label.substring(0, maxLen - 1) + '…';
            }
            const labelY = Math.floor((y1 + y2) / 2);
            const labelX = x1 + 1 + Math.floor((maxLen - label.length) / 2);
            if (buffer[labelY]) {
                for (let i = 0; i < label.length && labelX + i < x2; i++) {
                    buffer[labelY][labelX + i] = label[i];
                }
            }
        }
        return buffer.map((row) => row.join('')).join('\n');
    }
    /**
     * Export workspace to JSON
     */
    exportToJSON(workspaceId) {
        const workspace = this.workspaces.get(workspaceId);
        if (!workspace)
            throw new Error(`Workspace ${workspaceId} not found`);
        return JSON.stringify(workspace, null, 2);
    }
    /**
     * Import workspace from JSON
     */
    importFromJSON(json) {
        const data = JSON.parse(json);
        const newId = crypto.randomUUID();
        const workspace = {
            ...data,
            id: newId,
            createdAt: new Date(),
            updatedAt: new Date(),
            elements: data.elements.map((e) => ({
                ...e,
                id: crypto.randomUUID(),
                createdAt: new Date(),
                updatedAt: new Date(),
            })),
        };
        this.workspaces.set(workspace.id, workspace);
        this.history.set(workspace.id, []);
        this.historyIndex.set(workspace.id, -1);
        this.emit('workspace-created', workspace);
        return workspace;
    }
    /**
     * Shutdown
     */
    shutdown() {
        this.workspaces.clear();
        this.history.clear();
        this.historyIndex.clear();
        this.removeAllListeners();
    }
}
// Singleton
let visualWorkspaceManagerInstance = null;
export function getVisualWorkspaceManager() {
    if (!visualWorkspaceManagerInstance) {
        visualWorkspaceManagerInstance = new VisualWorkspaceManager();
    }
    return visualWorkspaceManagerInstance;
}
export function resetVisualWorkspaceManager() {
    if (visualWorkspaceManagerInstance) {
        visualWorkspaceManagerInstance.shutdown();
        visualWorkspaceManagerInstance = null;
    }
}
export default VisualWorkspaceManager;
//# sourceMappingURL=visual-workspace.js.map