import { Application, Sprite, Texture, Container, Graphics, RenderTexture } from 'pixi.js';
import { IClip } from './clips/iclip';
import { PixiSpriteRenderer } from './sprite/pixi-sprite-renderer';
import { ProjectJSON } from './json-serialization';
import { Transformer } from './transfomer/transformer';
import { EffectKey } from './effect/glsl/gl-effect';
import { default as EventEmitter } from './event-emitter';
import { Core } from '@openvideo/core';
import { SelectionManager } from './studio/selection-manager';
import { Transport } from './studio/transport';
import { TimelineModel } from './studio/timeline-model';
import { HistoryManager } from './studio/history-manager';
import { ResourceManager } from './studio/resource-manager';
export interface IStudioOpts {
    width: number;
    height: number;
    fps?: number;
    backgroundColor?: string;
    canvas?: HTMLCanvasElement;
    interactivity?: boolean;
    spacing?: number;
    core?: Core;
    allowZoom?: boolean;
    allowPan?: boolean;
    /**
     * Internal rendering resolution multiplier for preview (0-1).
     * e.g., 0.5 renders at half-resolution, improving playback performance.
     * Default is 1.
     */
    previewScale?: number;
}
interface ActiveGlobalEffect {
    id: string;
    key: EffectKey;
    startTime: number;
    duration: number;
    trackIndex?: number;
    values?: Record<string, any>;
}
interface GlobalEffectInfo {
    id: string;
    key: EffectKey;
    startTime: number;
    duration: number;
}
export interface StudioEvents {
    "selection:created": {
        selected: IClip[];
    };
    "selection:updated": {
        selected: IClip[];
    };
    "selection:cleared": {
        deselected: IClip[];
    };
    "track:added": {
        track: StudioTrack;
        index?: number;
    };
    "track:order-changed": {
        tracks: StudioTrack[];
    };
    "track:removed": {
        trackId: string;
    };
    "clip:added": {
        clip: IClip;
        trackId: string;
    };
    "clips:added": {
        clips: IClip[];
        trackId?: string;
    };
    "clip:removed": {
        clipId: string;
    };
    "clip:updated": {
        clip: IClip;
    };
    "clip:lock-changed": {
        clip: IClip;
        locked: boolean;
    };
    "clip:replaced": {
        oldClip: IClip;
        newClip: IClip;
        trackId: string;
    };
    "studio:restored": {
        clips: IClip[];
        tracks: StudioTrack[];
        settings: IStudioOpts;
    };
    currentTime: {
        currentTime: number;
    };
    play: {
        isPlaying: boolean;
    };
    pause: {
        isPlaying: boolean;
    };
    "history:changed": {
        canUndo: boolean;
        canRedo: boolean;
    };
    "transform:start": {
        transformer: Transformer;
    };
    "transform:end": {
        transformer: Transformer;
    };
    "clip:dblclick": {
        clip: IClip;
    };
    "viewport:changed": {
        zoomLevel: number;
        panOffset: {
            x: number;
            y: number;
        };
    };
    [key: string]: any;
    [key: symbol]: any;
}
export interface StudioTrack {
    id: string;
    name: string;
    type: string;
    clipIds: string[];
}
export declare class Studio extends EventEmitter<StudioEvents> {
    selection: SelectionManager;
    transport: Transport;
    timeline: TimelineModel;
    history: HistoryManager;
    resourceManager: ResourceManager;
    pixiApp: Application | null;
    get tracks(): StudioTrack[];
    get clips(): IClip<import('./sprite/base-sprite').BaseSpriteEvents>[];
    spriteRenderers: Map<IClip<import('./sprite/base-sprite').BaseSpriteEvents>, PixiSpriteRenderer>;
    artboard: Container | null;
    clipContainer: Container | null;
    artboardMask: Graphics | null;
    artboardBg: Graphics | null;
    get activeTransformer(): Transformer | null;
    set activeTransformer(val: Transformer | null);
    get selectedClips(): Set<IClip>;
    set selectedClips(val: Set<IClip>);
    get interactiveClips(): Set<IClip>;
    set interactiveClips(val: Set<IClip>);
    get playbackElements(): Map<IClip<import('./sprite/base-sprite').BaseSpriteEvents>, import('./studio/transport').PlaybackElementInfo>;
    videoSprites: Map<IClip<import('./sprite/base-sprite').BaseSpriteEvents>, Sprite>;
    clipListeners: Map<IClip<import('./sprite/base-sprite').BaseSpriteEvents>, () => void>;
    get isPlaying(): boolean;
    set isPlaying(val: boolean);
    get currentTime(): number;
    set currentTime(val: number);
    get maxDuration(): number;
    set maxDuration(val: number);
    opts: Required<Omit<IStudioOpts, "canvas" | "core">> & {
        canvas?: HTMLCanvasElement;
        core?: Core;
    };
    destroyed: boolean;
    private renderingSuspended;
    private historyPaused;
    private processingHistory;
    /**
     * Indicates if the studio is currently restoring state from history (undo/redo)
     */
    isRestoring: boolean;
    private historyGroupDepth;
    private clipCache;
    private _isUpdatingLayout;
    private bridge;
    /** User-controlled zoom multiplier on top of the fit-to-screen base scale.
     *  1.0 = 100 % (no extra zoom beyond fit). */
    private _zoomLevel;
    /** Pan offset in canvas/screen pixels, applied after base centering. */
    private _panOffset;
    /** The base fit-to-screen scale last computed by updateArtboardLayout. */
    private _baseScale;
    private _allowZoom;
    private _allowPan;
    private _isPanning;
    private _isSpaceDown;
    private _lastPointerPos;
    private _boundOnWheel;
    private _boundOnPointerDown;
    private _boundOnPointerMove;
    private _boundOnPointerUp;
    private _boundOnKeyDown;
    private _boundOnKeyUp;
    globalEffects: Map<string, GlobalEffectInfo>;
    activeGlobalEffects: ActiveGlobalEffect[];
    currentGlobalEffectSprite: Sprite | null;
    effectFilters: Map<string, {
        filter: null;
        render({ canvasTexture }: import('./effect/types').EffectRendererOptions): Texture<import('pixi.js').TextureSource<any>>;
    } | {
        filter: any;
        render({ width, height, canvasTexture, progress, values: runtimeValues, }: import('./effect/types').EffectRendererOptions): RenderTexture;
    }>;
    transitionRenderers: Map<string, {
        render({ width, height, from, to, progress }: import('./transition/types').TransitionRendererOptions): RenderTexture;
        destroy(): void;
    }>;
    transitionSprites: Map<string, Sprite>;
    transFromTexture: RenderTexture | null;
    transToTexture: RenderTexture | null;
    transBgGraphics: Graphics | null;
    clipsNormalContainer: Container | null;
    clipsEffectContainer: Container | null;
    videoTextureCache: WeakMap<HTMLVideoElement, Texture<import('pixi.js').TextureSource<any>>>;
    lastFromFrame: Texture | ImageBitmap | VideoFrame | null;
    lastToFrame: Texture | ImageBitmap | VideoFrame | null;
    /**
     * Convert hex color string to number
     */
    private hexToNumber;
    ready: Promise<void>;
    /**
     * Create a new Studio instance
     */
    constructor(opts: IStudioOpts);
    private attachClipEvents;
    private handleTimelineChange;
    private saveHistory;
    beginHistoryGroup(): void;
    endHistoryGroup(): void;
    private setPath;
    private applyHistoryPatches;
    undo(): Promise<void>;
    redo(): Promise<void>;
    private cleanupClipVisuals;
    private handleClipRemoved;
    private handleClipsRemoved;
    private initPixiApp;
    /**
     * Get studio options
     */
    getOptions(): IStudioOpts;
    /**
     * Update studio dimensions
     */
    setSize(width: number, height: number): void;
    /**
     * Set the preview resolution scale dynamically
     * @param scale A multiplier between 0.1 and 1
     */
    setPreviewScale(scale: number): void;
    private recreateTransitionTextures;
    /**
     * Update the background color of the studio
     */
    setBackgroundColor(color: string): void;
    updateDimensions(width: number, height: number): void;
    private handleResize;
    updateArtboardLayout(options?: {
        skipResize?: boolean;
    }): void;
    /** Current user zoom level (1 = fit-to-screen, 2 = 200 %, etc.). */
    get zoomLevel(): number;
    /** Base fit-to-screen scale (read-only, recomputed on every layout update). */
    get baseScale(): number;
    /**
     * Set the user zoom level.
     * @param level Desired zoom (1 = fit-to-screen). Clamped to [0.1, 10].
     * @param originX Optional canvas-space X to zoom toward (defaults to center).
     * @param originY Optional canvas-space Y to zoom toward (defaults to center).
     */
    setZoom(level: number, originX?: number, originY?: number): void;
    /**
     * Apply a relative pan (delta in screen pixels).
     */
    pan(dx: number, dy: number): void;
    /**
     * Reset zoom to 1× (fit-to-screen) and clear pan offset.
     */
    resetView(): void;
    setAllowZoom(enabled: boolean): void;
    setAllowPan(enabled: boolean): void;
    private _initViewportEvents;
    private _destroyViewportEvents;
    /**
     * Get the canvas element (creates one if not provided)
     */
    getCanvas(): HTMLCanvasElement;
    addTransition(transitionKey: string, duration?: number, fromClipId?: string | null, toClipId?: string | null, id?: string): Promise<void>;
    findTrackIdByClipId(clipId: string): string | undefined;
    /**
     * Add a clip (or clips) to the studio
     * @param clipOrClips The clip or array of clips to add
     * @param options Options for addition (trackId, etc.)
     */
    addClip(clipOrClips: IClip | IClip[], options?: {
        trackId?: string;
        audioSource?: string | File | Blob;
    } | string | File | Blob): Promise<void>;
    /**
     * Add a new track to the studio
     */
    addTrack(track: {
        name: string;
        type: string;
        id?: string;
    }, index?: number): StudioTrack;
    setTracks(tracks: StudioTrack[]): Promise<void>;
    /**
     * Move a track to a new index
     */
    moveTrack(trackId: string, newIndex: number): Promise<void>;
    /**
     * Set the order of tracks by ID
     */
    setTrackOrder(trackIds: string[]): Promise<void>;
    removeTrack(trackId: string): Promise<void>;
    /**
     * Get a clip by its ID
     */
    getClipById(id: string): IClip | undefined;
    updateClip(id: string, updates: Partial<IClip>): Promise<void>;
    /**
     * Centers object vertically and horizontally in the studio
     */
    centerClip(clipOrId: IClip | string): Promise<void>;
    /**
     * Centers object horizontally in the studio
     */
    centerClipH(clipOrId: IClip | string): Promise<void>;
    /**
     * Centers object vertically in the studio
     */
    centerClipV(clipOrId: IClip | string): Promise<void>;
    /**
     * Scale clip to fit within the studio dimensions while maintaining aspect ratio
     */
    scaleToFit(clipOrId: IClip | string): Promise<void>;
    /**
     * Scale clip to fill the studio dimensions while maintaining aspect ratio
     */
    scaleToCover(clipOrId: IClip | string): Promise<void>;
    updateClips(updates: {
        id: string;
        updates: Partial<IClip>;
    }[]): Promise<void>;
    suspendRendering(): void;
    resumeRendering(): void;
    getTracks(): StudioTrack[];
    getClip(id: string): IClip | undefined;
    findClip(id: string): IClip | undefined;
    /**
     * Setup sprite interactivity for click selection
     * Delegated to SelectionManager
     */
    setupSpriteInteractivity(clip: IClip): void;
    /**
     * Setup playback element for a clip (if it supports playback)
     */
    /**
     * Remove a clip from the studio
     */
    removeClip(clipOrId: IClip | string): Promise<void>;
    removeClips(clips: IClip[]): Promise<void>;
    removeClipById(clipId: string): Promise<void>;
    removeClipsById(clipIds: string[]): Promise<void>;
    /**
     * Delete all currently selected clips
     */
    deleteSelected(): Promise<void>;
    duplicateSelected(): Promise<void>;
    splitSelected(splitTime?: number): Promise<void>;
    trimSelected(trimFromSeconds: number): Promise<void>;
    updateSelected(updates: Partial<IClip>): Promise<void>;
    /**
     * Clear all clips from the studio
     */
    clear(): Promise<void>;
    /**
     * Start playback
     */
    play(): Promise<void>;
    /**
     * Pause playback
     */
    pause(): void;
    /**
     * Stop playback and reset to start
     */
    stop(): Promise<void>;
    /**
     * Seek to a specific time (in microseconds)
     */
    seek(time: number): Promise<void>;
    /**
     * Move to the next frame
     */
    frameNext(): Promise<void>;
    /**
     * Move to the previous frame
     */
    framePrev(): Promise<void>;
    /**
     * Renders the frame at the given time and returns it as a base64-encoded PNG.
     *
     * The artboard is temporarily reset to 1:1 scale during extraction so the
     * output image always matches the project's configured width × height,
     * regardless of the current viewport zoom.
     *
     * @param timeMs Time in milliseconds
     * @returns Base64 data-URL string (e.g. "data:image/png;base64,...")
     *
     * @example
     * await studio.ready;
     * const frame = await studio.renderFrame(1500); // frame at 1.5 s
     * const img = document.createElement('img');
     * img.src = frame;
     */
    renderFrame(timeMs: number): Promise<string>;
    /**
     * Get current playback time (in microseconds)
     */
    getCurrentTime(): number;
    /**
     * Get maximum duration (in microseconds)
     */
    getMaxDuration(): number;
    /**
     * Check if currently playing
     */
    getIsPlaying(): boolean;
    /**
     * Get currently selected clips
     */
    getSelectedClips(): IClip[];
    private getVideoTexture;
    private isPlaybackCapable;
    updateFrame(timestamp: number): Promise<void>;
    /**
     * Apply global effect to the current scene
     */
    moveClipToEffectContainer(clip: IClip, toEffect?: boolean): void;
    applyGlobalEffect(key: EffectKey, options: {
        startTime: number;
        duration?: number;
        id?: string;
    }, clips: IClip[]): string;
    getTrackIndex(clipId: string): number;
    /**
     * Get the frame from the previous clip on the same track for transition
     */
    getTransitionFromFrame(clip: IClip, timestamp: number): Promise<VideoFrame | ImageBitmap | null | Texture>;
    private getPreviousClipOnTrack;
    /**
     * Renders a clip frame onto a transition texture with red background
     */
    private renderClipToTransitionTexture;
    removeGlobalEffect(id: string): void;
    clearGlobalEffects(): void;
    private updateActiveGlobalEffect;
    private applyGlobalEffects;
    /**
     * Destroy the studio and clean up resources
     */
    destroy(): void;
    /**
     * Select a clip and show transform controls
     * Delegated to InteractionManager
     */
    selectClip(clip: IClip, addToSelection?: boolean): void;
    /**
     * Set the selection to a specific list of clips
     */
    setSelection(clips: IClip[]): void;
    /**
     * Select clips by their IDs
     */
    selectClipsByIds(ids: string[]): void;
    /**
     * Deselect the current clip and hide transform controls
     */
    deselectClip(): void;
    /**
     * Toggle lock state of a clip
     */
    lockClip(clipId: string, locked: boolean): Promise<void>;
    /**
     * Export current studio state to JSON
     * @param sourceUrlMap Optional map of clips to their source URLs (required for proper serialization)
     */
    exportToJSON(): ProjectJSON;
    loadFromJSON(json: ProjectJSON): Promise<void>;
}
export {};
