import { SimpleCommandState } from "../CommandStates";
import { CanvasItemsManager } from "../../Render/CanvasItemsManager";
import { Size } from "@devexpress/utils/lib/geometry/size";
import { CanvasExportManager } from "../../Render/CanvasExportManager";
import { ExportImportCommandBase } from "./ExportImportCommandBase";
import { DiagramControl } from "../../Diagram";
import { Exporter } from "../../ImportAndExport/Exporter";
import { RenderHelper } from "../../Render/RenderHelper";
import { ITextMeasurer } from "../../Render/Measurer/ITextMeasurer";
import { TextMeasurer } from "../../Render/Measurer/TextMeasurer";
import { ExportDOMManipulator } from "../../Render/DOMManipulator";
import { ImageCache } from "../../Images/ImageCache";

export abstract class ExportImageCommand extends ExportImportCommandBase {
    private svgElement: SVGElement;
    private exportManager: CanvasExportManager;
    private tempMeasurer: TextMeasurer;

    exporter: Exporter;

    constructor(control: DiagramControl) {
        super(control);
        this.exporter = new Exporter();
    }

    isEnabled(): boolean {
        return !ImageCache.instance.hasNonLoadedImages();
    }

    executeCore(state: SimpleCommandState, parameter: (url: string, ext: string) => void): boolean {
        try {
            const exportManager = this.getExportManager();
            const exportFunc = this.getExportFunc();
            exportFunc(this.control.model.size.clone(), this.control.model.pageColor, exportManager, (url) => {
                parameter(url, this.getExtension());
                this.tryDispose();
            }, this.control.settings.useCanvgForExportToImage);
        }
        catch(e) {
            this.tryDispose();
            throw e;
        }
        return true;
    }
    private getExportManager(): CanvasExportManager {
        const measurer = this.getOrCreateMeasurer();
        const itemsManager = (this.control.render && this.control.render.items) || this.createItemsManager(measurer);
        return this.exportManager || (this.exportManager = new CanvasExportManager(itemsManager.itemsContainer, measurer, this.control.instanceId));
    }
    private createItemsManager(measurer: ITextMeasurer): CanvasItemsManager {
        this.svgElement = RenderHelper.createSvgElement(document.body, true);

        const canvasManager = new CanvasItemsManager(this.svgElement, 1, new ExportDOMManipulator(measurer), this.control.instanceId);
        this.control.modelManipulator.onModelChanged.add(canvasManager);
        this.control.modelManipulator.commitItemsCreateChanges();
        return canvasManager;
    }
    private tryDispose() {
        if(this.svgElement) {
            document.body.removeChild(this.svgElement);
            delete this.svgElement;
        }
        if(this.tempMeasurer) {
            this.tempMeasurer.clean();
            this.tempMeasurer = undefined;
        }
        this.exportManager = undefined;
    }
    private getOrCreateMeasurer() {
        return this.control.measurer || (this.tempMeasurer = new TextMeasurer(document.body));
    }

    abstract getExportFunc(): ExportCommandFunc;
    abstract getExtension(): string;
}

export type ExportCommandFunc = (modelSize: Size, pageColor: number, exportManager: CanvasExportManager, callback: (url: string) => void,
    useCanvgForExportToImage?: boolean) => void;
