"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ViewManager = void 0;
const FloatingWindowController_1 = require("./FloatingWindowController");
const DevToolsManager_1 = require("./DevToolsManager");
const TilingManager_1 = require("./TilingManager");
const ViewCreator_1 = require("./ViewCreator");
const ViewLifecycle_1 = require("./ViewLifecycle");
const tsyringe_1 = require("tsyringe");
const ipc_1 = require("../ipc");
const ViewHandler_1 = require("./ViewHandler");
/**
 * ViewManager
 *
 * Central orchestrator for view management in Eden.
 */
let ViewManager = class ViewManager extends ipc_1.EdenEmitter {
    views = new Map();
    mainWindow = null;
    // Specialized modules
    tilingManager;
    floatingWindows;
    viewCreator;
    devToolsManager;
    windowSize = { width: 800, height: 600 };
    viewHandler;
    constructor(commandRegistry, ipcBridge, config, distPath) {
        super(ipcBridge);
        // Initialize specialized modules
        const tilingConfig = config.tiling || { mode: "none", gap: 0, padding: 0 };
        this.tilingManager = new TilingManager_1.TilingManager(tilingConfig);
        this.devToolsManager = new DevToolsManager_1.DevToolsManager();
        this.floatingWindows = new FloatingWindowController_1.FloatingWindowController(() => this.tilingManager.getWorkspaceBounds(), () => this.views.values());
        // Use consumer's dist path for runtime assets
        this.viewCreator = new ViewCreator_1.ViewCreator(distPath, this.tilingManager, this.floatingWindows, this.devToolsManager);
        // Create and register handler
        this.viewHandler = new ViewHandler_1.ViewHandler(this, ipcBridge);
        commandRegistry.registerManager(this.viewHandler);
    }
    /**
     * Set the main window that will host the views
     */
    setMainWindow(window) {
        this.mainWindow = window;
    }
    /**
     * Set workspace bounds (the area where views can be placed)
     */
    setWorkspaceBounds(bounds) {
        this.tilingManager.setWorkspaceBounds(bounds);
        if (this.tilingManager.isEnabled()) {
            this.tilingManager.recalculateTiledViews(this.views);
        }
    }
    setWindowSize(windowSize) {
        this.windowSize = windowSize;
    }
    getWindowSize() {
        return this.windowSize;
    }
    /**
     * Create a view for an app or overlay
     */
    createView(appId, manifest, installPath, bounds, launchArgs) {
        if (!ViewLifecycle_1.ViewGuard.isWindowAlive(this.mainWindow)) {
            throw new Error("Main window not set. Call setMainWindow first.");
        }
        const viewInfo = this.viewCreator.createView(appId, manifest, installPath, bounds, this.views.values(), launchArgs);
        const viewId = viewInfo.id;
        // Store view info
        this.views.set(viewId, viewInfo);
        // Electron event listener for view load failure
        viewInfo.view.webContents.on("did-fail-load", (_event, errorCode, errorDescription) => {
            console.error(`[ViewManager] View load failed for ${appId}:`, errorDescription);
            this.notify("view-load-failed", {
                viewId: viewInfo.id,
                appId,
                errorCode,
                errorDescription,
            });
        });
        // Notify listeners that the view has been loaded
        this.notify("view-loaded", {
            viewId: viewInfo.id,
            appId,
            overlay: viewInfo.viewType === "overlay",
        });
        // Recalculate all tiles if using tiling and this is a tiled view
        if (viewInfo.mode === "tiled" && this.tilingManager.isEnabled()) {
            this.tilingManager.recalculateTiledViews(this.views);
        }
        // Add to main window's contentView and maintain proper layering
        this.reorderViewLayers();
        return viewId;
    }
    /**
     * Remove a view
     */
    removeView(viewId) {
        const viewInfo = this.views.get(viewId);
        if (!viewInfo) {
            throw new Error(`View ${viewId} not found`);
        }
        try {
            // Close DevTools if open
            this.devToolsManager.closeDevToolsForView(viewInfo.view);
            // Destroy the view safely
            ViewLifecycle_1.ViewLifecycle.destroyView(viewInfo, this.mainWindow);
            // Clean up all event subscriptions for this view
            this.ipcBridge.eventSubscribers.removeViewSubscriptions(viewId);
            // Remove from tracking map
            this.views.delete(viewId);
            // Recalculate tiles if using tiling and this was an app view
            if (viewInfo.viewType === "app" && this.tilingManager.isEnabled()) {
                this.tilingManager.recalculateTiledViews(this.views);
            }
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            console.error(`[ViewManager] Failed to remove view ${viewId}:`, errorMessage);
            // Still remove from tracking even if removal failed
            this.views.delete(viewId);
            throw new Error(`Failed to remove view: ${errorMessage}`);
        }
    }
    /**
     * Update view bounds
     */
    setViewBounds(viewId, bounds) {
        const viewInfo = ViewLifecycle_1.ViewGuard.requireView(viewId, this.views);
        // If this is an overlay view, set bounds directly without constraints
        if (viewInfo.viewType === "overlay") {
            try {
                viewInfo.view.setBounds(bounds);
                viewInfo.bounds = bounds;
                return;
            }
            catch (error) {
                const errorMessage = error instanceof Error ? error.message : String(error);
                throw new Error(`Failed to set overlay bounds: ${errorMessage}`);
            }
        }
        // If this is a tiled view, ignore individual bounds updates
        if (viewInfo.mode === "tiled") {
            console.log(`[ViewManager] Ignoring individual bounds update for tiled view ${viewId}`);
            return;
        }
        // For floating views, allow bounds updates
        if (!viewInfo.visible) {
            viewInfo.bounds = bounds;
            return;
        }
        try {
            const windowConfig = viewInfo.manifest.window;
            const finalBounds = this.floatingWindows.applyWindowConstraints(bounds, windowConfig);
            viewInfo.view.setBounds(finalBounds);
            viewInfo.bounds = finalBounds;
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            throw new Error(`Failed to set bounds: ${errorMessage}`);
        }
    }
    /**
     * Get view by ID
     */
    getView(viewId) {
        return this.views.get(viewId)?.view;
    }
    /**
     * Get view info
     */
    getViewInfo(viewId) {
        return this.views.get(viewId);
    }
    /**
     * Find views by app ID
     */
    getViewsByAppId(appId) {
        const viewIds = [];
        for (const [viewId, info] of this.views.entries()) {
            if (info.appId === appId) {
                viewIds.push(viewId);
            }
        }
        return viewIds;
    }
    /**
     * Get app ID from a webContents ID
     */
    getAppIdByWebContentsId(webContentsId) {
        for (const [, info] of this.views.entries()) {
            if (info.view.webContents.id === webContentsId) {
                return info.appId;
            }
        }
        return undefined;
    }
    /**
     * Get view ID from a webContents ID
     */
    getViewIdByWebContentsId(webContentsId) {
        for (const [viewId, info] of this.views.entries()) {
            if (info.view.webContents.id === webContentsId) {
                return viewId;
            }
        }
        return undefined;
    }
    /**
     * Get all views (as entries for iteration)
     */
    getAllViews() {
        return Array.from(this.views.entries());
    }
    /**
     * Show a view (bring to front)
     */
    showView(viewId) {
        const viewInfo = ViewLifecycle_1.ViewGuard.requireView(viewId, this.views);
        try {
            viewInfo.visible = true;
            if (viewInfo.mode === "tiled") {
                this.tilingManager.recalculateTiledViews(this.views);
                this.reorderViewLayers();
            }
            else {
                viewInfo.zIndex = this.floatingWindows.getNextZIndex();
                this.reorderViewLayers();
            }
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            throw new Error(`Failed to show view: ${errorMessage}`);
        }
    }
    /**
     * Hide a view by setting its bounds to zero
     */
    hideView(viewId) {
        const viewInfo = ViewLifecycle_1.ViewGuard.requireView(viewId, this.views);
        try {
            viewInfo.visible = false;
            viewInfo.view.setBounds({ x: 0, y: 0, width: 0, height: 0 });
            if (viewInfo.mode === "tiled") {
                this.tilingManager.recalculateTiledViews(this.views);
            }
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            throw new Error(`Failed to hide view: ${errorMessage}`);
        }
    }
    /**
     * Reorder all views to maintain proper layering
     */
    reorderViewLayers() {
        if (!ViewLifecycle_1.ViewGuard.isWindowAlive(this.mainWindow))
            return;
        // Clean up any destroyed views first
        ViewLifecycle_1.ViewLifecycle.cleanupDestroyedViews(this.views);
        try {
            const appViews = Array.from(this.views.values()).filter((v) => v.viewType === "app");
            const tiledViews = appViews
                .filter((v) => v.mode === "tiled" && v.visible)
                .sort((a, b) => (a.tileIndex || 0) - (b.tileIndex || 0));
            const floatingAppViews = this.floatingWindows
                .getOrderedFloatingViews()
                .filter((v) => v.visible);
            const overlayViews = Array.from(this.views.values())
                .filter((v) => v.viewType === "overlay" && v.visible)
                .sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0));
            // Remove all views
            for (const info of this.views.values()) {
                if (ViewLifecycle_1.ViewGuard.isAlive(info.view)) {
                    this.mainWindow.contentView.removeChildView(info.view);
                }
            }
            // Add back in order: tiled (bottom), floating apps (middle), overlays (top)
            for (const info of tiledViews) {
                if (ViewLifecycle_1.ViewGuard.isAlive(info.view)) {
                    this.mainWindow.contentView.addChildView(info.view);
                    info.view.setBounds(info.bounds);
                }
            }
            for (const info of floatingAppViews) {
                if (ViewLifecycle_1.ViewGuard.isAlive(info.view)) {
                    this.mainWindow.contentView.addChildView(info.view);
                    info.view.setBounds(info.bounds);
                }
            }
            for (const info of overlayViews) {
                if (ViewLifecycle_1.ViewGuard.isAlive(info.view)) {
                    this.mainWindow.contentView.addChildView(info.view);
                    info.view.setBounds(info.bounds);
                }
            }
        }
        catch (error) {
            console.error("[ViewManager] Failed to reorder view layers:", error);
        }
    }
    /**
     * Send message to view's webContents
     */
    sendToView(viewId, channel, ...args) {
        try {
            const viewInfo = ViewLifecycle_1.ViewGuard.requireView(viewId, this.views);
            viewInfo.view.webContents.send(channel, ...args);
            return true;
        }
        catch (error) {
            console.error(`[ViewManager] Failed to send message to view ${viewId}:`, error);
            return false;
        }
    }
    /**
     * Get all active view IDs
     */
    getActiveViews() {
        return Array.from(this.views.keys());
    }
    /**
     * Toggle or set view mode between tiled and floating
     */
    setViewMode(viewId, targetMode) {
        const viewInfo = ViewLifecycle_1.ViewGuard.requireView(viewId, this.views);
        const manifest = viewInfo.manifest;
        const windowConfig = manifest.window;
        // Determine target mode
        const newMode = targetMode || (viewInfo.mode === "floating" ? "tiled" : "floating");
        // Check if the app supports this mode
        if (windowConfig?.mode) {
            if (windowConfig.mode === "floating" && newMode === "tiled") {
                throw new Error(`App ${viewInfo.appId} only supports floating mode`);
            }
            if (windowConfig.mode === "tiled" && newMode === "floating") {
                throw new Error(`App ${viewInfo.appId} only supports tiled mode`);
            }
        }
        // If already in target mode, do nothing
        if (viewInfo.mode === newMode) {
            return;
        }
        console.log(`[ViewManager] Switching view ${viewId} from ${viewInfo.mode} to ${newMode} mode`);
        try {
            if (newMode === "floating") {
                viewInfo.tileIndex = undefined;
                const floatingBounds = this.floatingWindows.calculateInitialBounds(windowConfig);
                viewInfo.bounds = floatingBounds;
                viewInfo.zIndex = this.floatingWindows.getNextZIndex();
                viewInfo.mode = "floating";
                viewInfo.view.setBounds(floatingBounds);
                if (this.tilingManager.isEnabled()) {
                    this.tilingManager.recalculateTiledViews(this.views);
                }
                this.reorderViewLayers();
                this.notifySubscriber(viewId, "mode-changed", {
                    mode: "floating",
                    bounds: floatingBounds,
                });
            }
            else {
                viewInfo.zIndex = undefined;
                if (this.tilingManager.isEnabled()) {
                    viewInfo.tileIndex = this.tilingManager.getNextTileIndex(this.views.values());
                    const visibleCount = this.tilingManager.getVisibleTiledCount(this.views.values());
                    const tileBounds = this.tilingManager.calculateTileBounds(viewInfo.tileIndex, visibleCount);
                    viewInfo.bounds = tileBounds;
                    viewInfo.mode = "tiled";
                    viewInfo.view.setBounds(tileBounds);
                    this.tilingManager.recalculateTiledViews(this.views);
                    this.reorderViewLayers();
                    this.notifySubscriber(viewId, "mode-changed", {
                        mode: "tiled",
                        bounds: tileBounds,
                    });
                }
                else {
                    const bounds = { ...this.tilingManager.getWorkspaceBounds() };
                    viewInfo.bounds = bounds;
                    viewInfo.mode = "tiled";
                    viewInfo.view.setBounds(bounds);
                    this.reorderViewLayers();
                    this.notifySubscriber(viewId, "mode-changed", {
                        mode: "tiled",
                        bounds,
                    });
                }
            }
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            throw new Error(`Failed to switch mode: ${errorMessage}`);
        }
    }
};
exports.ViewManager = ViewManager;
exports.ViewManager = ViewManager = __decorate([
    (0, tsyringe_1.singleton)(),
    (0, tsyringe_1.injectable)(),
    (0, ipc_1.EdenNamespace)("view"),
    __param(0, (0, tsyringe_1.inject)(ipc_1.CommandRegistry)),
    __param(1, (0, tsyringe_1.inject)((0, tsyringe_1.delay)(() => ipc_1.IPCBridge))),
    __param(2, (0, tsyringe_1.inject)("EdenConfig")),
    __param(3, (0, tsyringe_1.inject)("distPath")),
    __metadata("design:paramtypes", [ipc_1.CommandRegistry,
        ipc_1.IPCBridge, Object, String])
], ViewManager);
//# sourceMappingURL=ViewManager.js.map