import { ClipboardCommand } from "./ClipboardCommand";
import { SimpleCommandState } from "../CommandStates";
import { Importer } from "../../ImportAndExport/Importer";
import { Shape } from "../../Model/Shapes/Shape";
import { ImportShapeHistoryItem } from "../../History/Common/ImportShapeHistoryItem";
import { Connector } from "../../Model/Connectors/Connector";
import { ImportConnectorHistoryItem } from "../../History/Common/ImportConnectorHistoryItem";
import { ModelUtils } from "../../Model/ModelUtils";
import { SetSelectionHistoryItem } from "../../History/Common/SetSelectionHistoryItem";
import { DiagramItem } from "../../Model/DiagramItem";
import { Point } from "@devexpress/utils/lib/geometry/point";
import { UnitConverter } from "@devexpress/utils/lib/class/unit-converter";

export abstract class PasteSelectionCommandBase extends ClipboardCommand {
    static readonly positionOffset = UnitConverter.pixelsToTwips(10);

    isEnabled(): boolean {
        return super.isEnabled() && (this.isPasteSupportedByBrowser() || ClipboardCommand.clipboardData !== undefined);
    }
    isVisible(): boolean {
        return this.isPasteSupportedByBrowser() || ClipboardCommand.clipboardData !== undefined;
    }
    parseClipboardData(data: string): DiagramItem[] {
        let items: DiagramItem[] = [];
        const importer = new Importer(this.control.shapeDescriptionManager, data);
        items = importer.importItems(this.control.model);
        const offset = this.getEventPositionOffset(items, this.control.contextMenuPosition);
        for(let i = 0; i < items.length; i++) {
            const item = items[i];
            if(item instanceof Shape)
                item.position.offsetByPoint(offset);
            else if(item instanceof Connector) {
                item.points.forEach(p => p.offsetByPoint(offset));
                item.clearRenderPoints();
            }
        }
        return items;
    }
    protected abstract getEventPositionOffset(items: DiagramItem[], evtPosition?: Point): Point;
    executeCore(state: SimpleCommandState, parameter?: string): boolean {
        let ret = true;
        if(parameter === undefined)
            this.getClipboardData(data => {
                ret = this.execute(data); 
            });
        else
            this.performPaste(parameter);
        return ret;
    }
    addItemForSortingRecursive(itemsHashtable: {[key: string]: number}, item: DiagramItem): number {
        if(itemsHashtable[item.key])
            return itemsHashtable[item.key];
        if(item instanceof Connector) {
            if(item.endItem)
                itemsHashtable[item.key] = this.addItemForSortingRecursive(itemsHashtable, item.endItem) - 0.5;
            else if(item.beginItem)
                itemsHashtable[item.key] = this.addItemForSortingRecursive(itemsHashtable, item.beginItem) + 0.5;
            else
                itemsHashtable[item.key] = -1;
            return itemsHashtable[item.key];
        }
        if(item.attachedConnectors.length === 0)
            return itemsHashtable[item.key] = 0;
        else
            for(let i = 0; i < item.attachedConnectors.length; i++) {
                const beginItem = item.attachedConnectors[i].beginItem;
                if(item.attachedConnectors[i].endItem === item && beginItem && beginItem !== item.attachedConnectors[i].endItem)
                    return itemsHashtable[item.key] = this.addItemForSortingRecursive(itemsHashtable, beginItem) + 1;
                else
                    return itemsHashtable[item.key] = 0; 
            }
    }
    getSortedPasteItems(items: DiagramItem[]): DiagramItem[] {
        const sortedItems: DiagramItem[] = [];
        const connectors = [];
        const itemsHashtable = {};
        for(let i = 0; i < items.length; i++) {
            const item = items[i];
            if(item instanceof Shape)
                sortedItems.push(item);
            else if(item instanceof Connector) {
                connectors.push(item);
                this.addItemForSortingRecursive(itemsHashtable, item);
            }
        }
        connectors.sort((a, b) => itemsHashtable[b.key] - itemsHashtable[a.key]);
        return sortedItems.concat(connectors);
    }
    performPaste(data: string): void {
        this.control.beginUpdateCanvas();
        this.control.history.beginTransaction();
        const idsForSelection: {[id: string]: boolean} = {};
        let items = this.parseClipboardData(data);
        items = this.getSortedPasteItems(items);
        for(let i = 0; i < items.length; i++) {
            const item = items[i];
            if(item instanceof Shape)
                this.control.history.addAndRedo(new ImportShapeHistoryItem(item));

            else if(item instanceof Connector)
                this.control.history.addAndRedo(new ImportConnectorHistoryItem(item));

            const containerKey = item.container && item.container.key;
            if(!containerKey || idsForSelection[containerKey] === undefined)
                idsForSelection[item.key] = true;
            else if(containerKey && idsForSelection[containerKey] !== undefined)
                idsForSelection[item.key] = false;
        }
        ModelUtils.tryUpdateModelRectangle(this.control.history);
        this.control.history.addAndRedo(new SetSelectionHistoryItem(this.control.selection, Object.keys(idsForSelection).filter(id => idsForSelection[id])));
        this.control.history.endTransaction();
        this.control.endUpdateCanvas();
        this.control.barManager.updateItemsState();
    }

    protected get isPermissionsRequired(): boolean { return true; }
}
