import { Size } from "@devexpress/utils/lib/geometry/size";
import { Dependency } from "../../Model/Entities/Dependency";
import { Resource } from "../../Model/Entities/Resource";
import { Task } from "../../Model/Entities/Task";
import { ViewVisualModelItem } from "../../Model/VisualModel/ViewVisualModelItem";
import { TaskTitlePosition } from "../Helpers/Enums";
import { GridElementInfo } from "../Helpers/GridElementInfo";
import { GridLayoutCalculator } from "../Helpers/GridLayoutCalculator";
import { RenderElementUtils } from "./RenderElementUtils";
import { RenderHelper } from "./RenderHelper";
import { TaskRender } from "./TaskRender";

export class CustomTaskRender {
    private _renderHelper: RenderHelper;
    private _taskRender: TaskRender;

    private _pendingTemplateFuncsToRender: Record<number, Array<(container: HTMLElement, model: any, ...args) => void>> = [];

    constructor(renderHelepr: RenderHelper, taskRender: TaskRender) {
        this._renderHelper = renderHelepr;
        this._taskRender = taskRender;
    }

    protected get gridLayoutCalculator(): GridLayoutCalculator {
        return this._renderHelper.gridLayoutCalculator;
    }

    protected get tickSize(): Size {
        return this._renderHelper.tickSize;
    }

    protected get taskTitlePosition(): TaskTitlePosition {
        return this._renderHelper.taskTitlePosition;
    }

    protected get taskElements(): HTMLElement[] {
        return this._taskRender.taskElements;
    }

    protected get taskArea(): HTMLElement {
        return this._renderHelper.taskArea;
    }

    getViewItem(index: number): ViewVisualModelItem {
        return this._renderHelper.getViewItem(index);
    }

    getTask(index: number): Task {
        return this._renderHelper.getTask(index);
    }

    destroyTemplate(container: HTMLElement): void {
        this._renderHelper.destroyTemplate(container);
    }

    getTaskDependencies(taskInternalId: any): Dependency[] {
        return this._renderHelper.getTaskDependencies(taskInternalId);
    }

    getTaskResources(key: any): Resource[] {
        return this._renderHelper.getTaskResources(key);
    }

    attachEventsOnTask(taskIndex: number): void {
        this._renderHelper.attachEventsOnTask(taskIndex);
    }

    recreateConnectorLineElement(dependencyId: string, forceRender: boolean = false): void {
        this._renderHelper.recreateConnectorLineElement(dependencyId, forceRender);
    }

    createTaskSelectionElement(taskIndex: number): void {
        this._taskRender.createTaskSelectionElement(taskIndex);
    }


    createCustomTaskElement(index: number, taskTemplateFunction: (container: HTMLElement, model: any, ...args) => void): void {
        this._saveTemplateFuncToStack(index, taskTemplateFunction);
        if(this._hasRepeatedTemplateRenderCall(index))
            return;

        const viewItem = this.getViewItem(index);
        if(viewItem) {
            viewItem.isCustom = false;
            const taskTemplateContainer = document.createElement("DIV");
            const taskInformation = this.createCustomTaskInformation(index);
            viewItem.isCustom = true;
            taskTemplateFunction(taskTemplateContainer, taskInformation, this.onTaskTemplateContainerRendered.bind(this), index);
        }
        this._clearTemplateFuncsStack(index);
    }
    onTaskTemplateContainerRendered(taskTemplateContainer: HTMLElement, taskIndex: number): void {
        if(this._hasRepeatedTemplateRenderCall(taskIndex)) {
            if(taskTemplateContainer)
                this._renderHelper.destroyTemplate(taskTemplateContainer as HTMLElement);
            const templateFunc = this._getLastPendingTemplateFunc(taskIndex);
            setTimeout(() => this.createCustomTaskElement(taskIndex, templateFunc));
        }
        else
            this.drawCustomTask(taskTemplateContainer, taskIndex);
        this._clearTemplateFuncsStack(taskIndex);
    }
    _saveTemplateFuncToStack(index: number, taskTemplateFunction: (container: HTMLElement, model: any, ...args) => void): void {
        this._pendingTemplateFuncsToRender[index] ??= [];
        this._pendingTemplateFuncsToRender[index].push(taskTemplateFunction);
    }
    _clearTemplateFuncsStack(index: number): void { this._pendingTemplateFuncsToRender[index] = []; }
    _hasRepeatedTemplateRenderCall(index: number): boolean { return this._pendingTemplateFuncsToRender[index].length > 1; }
    _getLastPendingTemplateFunc(index: number): (container: HTMLElement, model: any, ...args) => void {
        const pendingStack = this._pendingTemplateFuncsToRender[index];
        return pendingStack[pendingStack.length - 1];
    }

    createCustomTaskWrapperElement(index: number, taskWrapperInfo: GridElementInfo): void {
        RenderElementUtils.create(taskWrapperInfo, index, this.taskArea, this.taskElements);
    }
    createCustomTaskVisualElement(index: number, taskElementInfo: GridElementInfo): HTMLElement {
        const taskElement = RenderElementUtils.create(taskElementInfo, index, this.taskElements[index]);
        return taskElement;
    }
    drawCustomTask(taskTemplateContainer: HTMLElement, taskIndex: number):void {
        const viewItem = this.getViewItem(taskIndex);

        if(!viewItem || !this.taskElements[taskIndex])
            return;

        viewItem.visible = !!taskTemplateContainer.innerHTML;
        this.taskElements[taskIndex].innerHTML = taskTemplateContainer.innerHTML;
        viewItem.size.height = this.taskElements[taskIndex].offsetHeight;
        viewItem.size.width = this.taskElements[taskIndex].offsetWidth;
        this.destroyTemplate(this.taskElements[taskIndex]);
        this._taskRender.removeTaskElement(taskIndex);
        if(viewItem.visible && viewItem.task.isValid()) {
            const taskWrapperInfo = this.gridLayoutCalculator.getTaskWrapperElementInfo(taskIndex);
            this.createCustomTaskWrapperElement(taskIndex, taskWrapperInfo);
            this.taskElements[taskIndex].appendChild(taskTemplateContainer);
            this.attachEventsOnTask(taskIndex);
        }
        else {
            const taskDependencies = this.getTaskDependencies(viewItem.task.internalId);
            if(taskDependencies.length) {
                this._taskRender.addInvalidTaskDependencies(taskDependencies);
                taskDependencies.forEach(d => this.recreateConnectorLineElement(d.internalId, true));
            }
        }
        if(this._taskRender.isHighlightRowElementAllowed(taskIndex))
            this._taskRender.createHighlightRowElement(taskIndex);
        if(viewItem.selected)
            this.createTaskSelectionElement(taskIndex);
    }
    createCustomTaskInformation(index: number):any {
        const task = this.getTask(index);
        const viewItem = this.getViewItem(index);
        const taskWrapperInfo = this.gridLayoutCalculator.getTaskWrapperElementInfo(index);
        const taskElementInfo = this.gridLayoutCalculator.getTaskElementInfo(index, this.taskTitlePosition !== TaskTitlePosition.Inside);
        this.createCustomTaskWrapperElement(index, taskWrapperInfo);
        const taskParent = this.taskElements[index];
        if(this._renderHelper.taskTitlePosition === TaskTitlePosition.Outside)
            this._taskRender.createTaskTextElement(index, taskParent);
        this.createCustomTaskVisualElement(index, taskElementInfo);
        const taskResources = this.getTaskResources(task.id);
        const taskInformation = {
            cellSize: this.tickSize,
            isMilestone: task.isMilestone(),
            isParent: !!viewItem?.children.length,
            taskData: task,
            taskHTML: taskParent.children,
            taskPosition: taskWrapperInfo.position,
            taskResources: taskResources,
            taskSize: taskElementInfo.size,
        };

        return taskInformation;
    }
}
