import { Engine } from "../engine";
import { Element, Link, Marker, Model } from "../Model/modelData";
import { EngineOptions } from "../options";
import { Container } from "./container/container";
import { SV } from '../StructV';
import { MainContainer } from "./container/main";
import { FreedContainer } from "./container/freed";
import { LeakContainer } from "./container/leak";
import { Layouter } from "./layouter";
import { LayoutGroup, LayoutGroupTable } from "../Model/modelConstructor";
import { Util } from "../Common/util";
import { EventBus } from "../Common/eventBus";



export class ViewManager {
    private engine: Engine;
    private layouter: Layouter;
    private mainContainer: Container;
    private freedContainer: FreedContainer;
    private leakContainer: LeakContainer;

    private prevLayoutGroupTable: LayoutGroupTable;
    private prevModelList: Model[];
    private prevFreedElements: Element[];

    private shadowG6Instance;

    constructor(engine: Engine, DOMContainer: HTMLElement) {
        this.engine = engine;
        this.layouter = new Layouter(engine);
        this.mainContainer = new MainContainer(engine, DOMContainer, { tooltip: true });
        this.prevLayoutGroupTable = new Map();
        this.prevFreedElements = [];
        this.prevModelList = [];

        const options: EngineOptions = this.engine.engineOptions;

        if (options.freedContainer) {
            this.freedContainer = new FreedContainer(engine, options.freedContainer, { fitCenter: true, tooltip: true });
        }

        if (options.leakContainer) {
            this.leakContainer = new LeakContainer(engine, options.leakContainer, { fitCenter: true, tooltip: false });
        }

        this.shadowG6Instance = new SV.G6.Graph({
            container: DOMContainer.cloneNode()
        });
    }

    /**
     * 对每一个 model 在离屏 Canvas 上构建 G6 item，用作布局
     * @param constructList
     */
    private build(modelList: Model[]) {
        modelList.forEach(item => {
            const type = item instanceof Link ? 'edge' : 'node';
            this.shadowG6Instance.addItem(type, item.cloneProps());
            item.shadowG6Item = this.shadowG6Instance.findById(item.id);
        });
    }

    /**
     * 
     * @param freedElement 
     * @param prevLayoutGroup 
     */
    private handleFreedLabel(freedElement: Element[], prevLayoutGroup: LayoutGroup) {
        if (prevLayoutGroup === undefined) {
            return;
        }

        const prevElementList: Element[] = prevLayoutGroup.element;

        freedElement.map(item => {
            let prevElement = prevElementList.find(el => el.id === item.id),
                prevLabel = prevElement.get('label') ?? '';

            item.set('label', prevLabel);
        });
    }

    /**
     * 获取被 free 的节点
     * @param layoutGroupTable 
     * @returns 
     */
    private getFreedModelList(prevLayoutGroupTable: LayoutGroupTable, layoutGroupTable: LayoutGroupTable): Model[] {
        let freedElements: Element[] = [],
            freedMarkers: Marker[] = [],
            removeModels: Model[] = [],
            freedGroupName: string;

        layoutGroupTable.forEach((group, key) => {
            let targetElements: Element[] = group.element.filter(item => item.freed);

            targetElements.forEach(fItem => {
                removeModels.push(
                    ...Util.removeFromList(group.element, item => item.id === fItem.id),
                    ...Util.removeFromList(group.link, item => item.element.id === fItem.id || item.target.id === fItem.id),
                    ...Util.removeFromList(group.marker, item => item.target.id === fItem.id),
                );
            });

            removeModels.map(model => {
                Util.removeFromList(group.modelList, item => item.id === model.id);
            });

            // 找出最新的freed节点（防止上一次的freed节点被遗留在该次渲染，此时会出现大于一个freed节点）
            targetElements.forEach(item => {
                if (this.prevFreedElements.find(prevEle => prevEle.id === item.id) === undefined) {
                    freedGroupName = key;
                    freedElements.push(item);
                }
            });
        });

        freedElements.map(item => {
            const markers = Object.keys(item.markers).map(name => item.markers[name]);
            freedMarkers.push(...markers);
        });

        this.handleFreedLabel(freedElements, prevLayoutGroupTable.get(freedGroupName));
        this.prevFreedElements = freedElements;

        return [...freedElements, ...freedMarkers];
    }

    /**
     * 获取被泄露的节点
     * @param prevModelList 
     * @param modelList 
     * @returns 
     */
    private getLeakModelList(prevModelList: Model[], modelList: Model[]): Model[] {
        const leakModelList: Model[] = prevModelList.filter(item => !modelList.find(n => n.id === item.id)),
            elements: Element[] = <Element[]>leakModelList.filter(item => item instanceof Element && item.freed === false),
            links: Link[] = <Link[]>leakModelList.filter(item => item instanceof Link),
            elementIds: string[] = [],
            res: Model[] = [];

        elements.forEach(item => {
            elementIds.push(item.id);

            item.set('style', {
                fill: '#ccc'
            });
        });

        for (let i = 0; i < links.length; i++) {
            let sourceId = links[i].element.id,
                targetId = links[i].target.id;

            links[i].set('style', {
                stroke: '#333'
            });

            if (elementIds.find(item => item === sourceId) === undefined || elementIds.find(item => item === targetId) === undefined) {
                links.splice(i, 1);
                i--;
            }
        }

        res.push(...elements, ...links);

        res.map(item => {
            item.isLeak = true;
        });

        return res;
    }

    // ----------------------------------------------------------------------------------------------

    /**
     * 对主视图进行重新布局
     * @param layoutGroupTable
     */
    reLayout(layoutGroupTable: LayoutGroupTable) {
        this.layouter.layoutAll(this.mainContainer, layoutGroupTable);
    }


    /**
     * 获取 g6 实例
     */
    getG6Instance() {
        return this.mainContainer.getG6Instance();
    }

    /**
     * 刷新视图
     */
    refresh() {
        this.mainContainer.getG6Instance().refresh();
    }

    /**
     * 重新调整容器尺寸
     * @param containerName
     * @param width 
     * @param height 
     */
    resize(containerName: string, width: number, height: number) {
        if (containerName === 'main') {
            this.mainContainer.getG6Instance().changeSize(width, height);
        }

        if (containerName === 'freed') {
            this.freedContainer.getG6Instance().changeSize(width, height);
        }

        if (containerName === 'leak') {
            this.leakContainer.getG6Instance().changeSize(width, height);
        }

    }

    /**
     * 渲染所有视图
     * @param models 
     * @param layoutFn 
     */
    renderAll(layoutGroupTable: LayoutGroupTable) {
        this.shadowG6Instance.clear();

        let modelList = Util.convertGroupTable2ModelList(layoutGroupTable),
            leakModelList = [],
            freedModelList = [];

        this.build(modelList);

        if (this.leakContainer) {
            leakModelList = this.getLeakModelList(this.prevModelList, modelList);
            this.build(leakModelList);
        }

        // 进行布局（设置model的x，y）
        this.layouter.layoutAll(this.mainContainer, layoutGroupTable);

        freedModelList = this.getFreedModelList(this.prevLayoutGroupTable, layoutGroupTable);

        if (this.freedContainer && freedModelList.length) {
            this.freedContainer.fitCenter(freedModelList);
            this.freedContainer.render(freedModelList);
            EventBus.emit('onFreed', freedModelList);
        }

        // 从新获取一次，因为第一次获取没有把freed节点筛选出去
        modelList = Util.convertGroupTable2ModelList(layoutGroupTable);
        this.mainContainer.render(modelList);

        if (this.leakContainer && leakModelList.length) {
            this.leakContainer.render(leakModelList);
            EventBus.emit('onLeak', leakModelList);
        }

        this.prevLayoutGroupTable = layoutGroupTable;
        this.prevModelList = modelList;
    }

    /**
     * 销毁
     */
    destroy() {
        this.shadowG6Instance.destroy();
        this.mainContainer.destroy();
        this.freedContainer && this.freedContainer.destroy();
        this.leakContainer && this.leakContainer.destroy();
    }
}











