import { Button } from "../../core/render/Button";
import { ConfirmButton } from "../../core/render/ConfirmButton";
import { Graphics } from "../../core/render/Graphics";
import { percent } from "../../core/util/Percent";
import { GanttDateAxis } from "./GanttDateAxis";
import { XYChart } from "../xy/XYChart";
import { GanttDefaultTheme } from "./GanttDefaultTheme";
import { GanttSeries } from "./GanttSeries";
import { color, Container, Rectangle, Scrollbar, Tooltip } from "../../..";
import { GanttCategoryAxis } from "./GanttCategoryAxis";
import { GanttCategoryAxisRenderer } from "./GanttCategoryAxisRenderer";
import { GanttDateAxisRenderer } from "./GanttDateAxisRenderer";
import { XYCursor } from "../xy/XYCursor";
import { ColorPicker } from "../../plugins/colorPicker/ColorPicker";
import { ColorPickerButton } from "../../plugins/colorPicker/ColorPickerButton";
import { registry } from "../../core/Registry";
import * as $utils from "../../core/util/Utils";
import * as $array from "../../core/util/Array";
import * as $time from "../../core/util/Time";
/**
 * Creates a [[Gantt]] chart.
 *
 * @see {@link https://www.amcharts.com/docs/v5/charts/gantt/} for more info
 * @important
 * @since 5.14.0
 */
export class Gantt extends Container {
    constructor() {
        super(...arguments);
        /**
         * A scrollbar for horizontal scrolling.
         */
        this.scrollbarX = Scrollbar.new(this._root, {
            orientation: "horizontal"
        });
        /**
         * A scrollbar for vertical scrolling.
         */
        this.scrollbarY = Scrollbar.new(this._root, {
            orientation: "vertical"
        });
        /**
         * Controls (add, color picker, expand, collaps, clear buttons) container.
         */
        this.controls = Container.new(this._root, {
            themeTags: ["controls"],
            layout: this._root.horizontalLayout,
        });
        this.zoomControls = Container.new(this._root, {
            themeTagsSelf: ["zoomcontrols"]
        });
        /**
         * The [[Button]] element to add new tasks.
         */
        this.addButton = this.controls.children.push(Button.new(this._root, {
            themeTags: ["add", "plus"],
            tooltip: this.root.systemTooltip,
            icon: Graphics.new(this._root, { themeTags: ["icon"] })
        }));
        /**
         * The [[ColorPickerButton]] element to select colors for tasks.
         */
        this.colorPickerButton = this.controls.children.push(ColorPickerButton.new(this._root, {
            disableOpacity: true,
            tooltip: this.root.systemTooltip
        }));
        /**
         * The [[Button]] elements to expand and collapse all tasks.
         */
        this.expandButton = this.controls.children.push(Button.new(this._root, {
            themeTags: ["expand", "secondary", "fixedwidth"],
            icon: Graphics.new(this._root, { themeTags: ["icon"] }),
            tooltip: this.root.systemTooltip
        }));
        /**
         * The [[Button]] elements to collapse all tasks.
         */
        this.collapseButton = this.controls.children.push(Button.new(this._root, {
            themeTags: ["collapse", "secondary", "fixedwidth"],
            icon: Graphics.new(this._root, { themeTags: ["icon"] }),
            tooltip: this.root.systemTooltip
        }));
        /**
         * The [[Button]] element to toggle `linkNewTasks` setting.
         */
        this.linkButton = this.controls.children.push(Button.new(this._root, {
            themeTags: ["link", "secondary", "fixedwidth"],
            icon: Graphics.new(this._root, { themeTags: ["icon"] }),
            tooltip: this.root.systemTooltip
        }));
        /**
         * The [[Button]] element to horizontally fit visible tasks into a view.
         */
        this.fitButton = this.zoomControls.children.push(Button.new(this._root, {
            themeTags: ["fit", "secondary", "zoombutton"],
            icon: Graphics.new(this._root, { themeTags: ["icon"] }),
            tooltip: this.root.systemTooltip
        }));
        /**
         * The [[Button]] element to zoom out the X axis.
         */
        this.zoomOutButton = this.zoomControls.children.push(Button.new(this._root, {
            themeTags: ["zoomout", "secondary", "zoombutton"],
            icon: Graphics.new(this._root, { themeTags: ["icon"] }),
            tooltip: this.root.systemTooltip
        }));
        /**
         * The [[Button]] element to toggle edit mode.
         *
         * @since 5.14.1
         */
        this.editButton = this.controls.children.push(Button.new(this._root, {
            themeTags: ["edit", "secondary", "fixedwidth"],
            icon: Graphics.new(this._root, { themeTags: ["icon"] }),
            tooltip: this.root.systemTooltip
        }));
        /**
         * The [[Button]] element to clear all tasks.
         */
        this.clearButton = this.controls.children.push(ConfirmButton.new(this._root, {
            themeTags: ["clear", "secondary", "confirm"],
            icon: Graphics.new(this._root, { themeTags: ["icon"] }),
            tooltip: this.root.systemTooltip,
            active: false
        }));
        /**
         * The [[ColorPicker]] element to select colors for tasks.
         */
        this.colorPicker = this.children.push(ColorPicker.new(this._root, { visible: false }));
    }
    _applyThemes(force = false) {
        const colors = this.get("colors");
        if (colors) {
            colors.reset();
        }
        return super._applyThemes(force);
    }
    _afterNew() {
        this.addTag("gantt");
        this._defaultThemes.push(GanttDefaultTheme.new(this._root));
        super._afterNew();
        const root = this._root;
        this.colorPickerButton.events.on("click", () => {
            this.yAxis.xButton.hide(0);
            this.colorPicker.setAll({
                colorButton: this.colorPickerButton
            });
            this.children.moveValue(this.colorPicker, this.children.length - 1);
            this.yAxis._disposeXHideDP();
        });
        this.colorPicker.events.on("colorchanged", () => {
            this._customColor = this.colorPicker.get("color");
            this.yAxis.unselectDataItems();
        });
        this.addButton.events.on("click", () => {
            this.addNewTask();
        });
        const chart = this.children.push(XYChart.new(root, {}));
        chart.plotContainer.children.push(this.zoomControls);
        chart.zoomOutButton.set("forceHidden", true);
        chart.children.push(this.controls);
        chart.set("cursor", XYCursor.new(root, {
            xAxis: this.xAxis,
            yAxis: this.yAxis
        }));
        const yRenderer = GanttCategoryAxisRenderer.new(root, {});
        // to prevent moving horizontally while dragging
        yRenderer.containers.template.adapters.add("x", () => {
            return 0;
        });
        // can't set fields in theme, as data is set before theme is applied
        const yAxis = chart.yAxes.push(GanttCategoryAxis.new(root, {
            categoryField: "id",
            cellSizeField: "cellSize",
            collapsedField: "collapsed",
            parentIdField: "parentId",
            colorField: "color",
            idField: "id",
            nameField: "name",
            renderer: yRenderer
        }));
        yAxis.gantt = this;
        // update widht of scrollbar and controls container
        yAxis.events.on("boundschanged", () => {
            this._updateScrollbar();
        });
        chart.plotContainer.events.on("boundschanged", () => {
            this._updateScrollbar();
        });
        this.on("visible", () => {
            this.root.events.once("frameended", () => {
                this._updateScrollbar();
            });
        });
        // minor x axis
        const ganttAxisRenderrerMinor = GanttDateAxisRenderer.new(root, { themeTags: ["minor"] });
        // so that grid would be hidden if axis range is added
        ganttAxisRenderrerMinor.grid.template.adapters.add("forceHidden", (forceHidden, target) => {
            const dataItem = target.dataItem;
            if (dataItem && dataItem.get("active")) {
                return true;
            }
            return forceHidden;
        });
        const xAxisMinor = chart.xAxes.push(GanttDateAxis.new(root, {
            themeTags: ["minor"],
            baseInterval: { timeUnit: "day", count: 1 },
            renderer: ganttAxisRenderrerMinor,
            background: Rectangle.new(root, {
                fill: color(0xfff),
                fillOpacity: 0
            })
        }));
        xAxisMinor.gantt = this;
        // return default color for grid lines
        xAxisMinor.events.on("pointerout", () => {
            ganttAxisRenderrerMinor.grid.each((grid) => {
                grid.unhover();
            });
        });
        // add axis range on click
        xAxisMinor.events.on("click", () => {
            if (closestGrid) {
                const dataItem = closestGrid.dataItem;
                if (dataItem) {
                    const value = dataItem.get("endValue");
                    if (value !== undefined) {
                        let found = false;
                        xAxisMinor.axisRanges.each((axisRange) => {
                            // remove axis range if already exists
                            if (axisRange.get("value") === value) {
                                this.unmarkDate(value);
                                found = true;
                            }
                        });
                        // add axis range if not exists
                        if (!found) {
                            dataItem.set("active", true);
                            this.markDate(value);
                        }
                    }
                }
            }
        });
        // find closest grid line on hover
        let closestGrid;
        xAxisMinor.events.on("globalpointermove", (ev) => {
            if (xAxisMinor.isHover()) {
                // find most close grid line
                const point = xAxisMinor.toLocal({ x: ev.point.x, y: ev.point.y });
                let minX = Infinity;
                ganttAxisRenderrerMinor.grid.each((grid) => {
                    if (grid.isVisible()) {
                        const dataItem = grid.dataItem;
                        if (dataItem) {
                            if (!dataItem.get("isRange")) {
                                let distance = Math.abs(grid.x() - point.x);
                                if (!grid.get("active")) {
                                    grid.unhover();
                                }
                                if (distance < minX) {
                                    minX = distance;
                                    closestGrid = grid;
                                }
                            }
                        }
                    }
                });
                if (closestGrid) {
                    closestGrid.hover();
                }
            }
        });
        const ganttAxisRenderrer = GanttDateAxisRenderer.new(root, {});
        const xAxis = chart.xAxes.push(GanttDateAxis.new(root, {
            baseInterval: { timeUnit: "day", count: 1 },
            renderer: ganttAxisRenderrer,
            tooltip: Tooltip.new(root, {})
        }));
        xAxis.gantt = this;
        // sync minor x axis with main x axis
        xAxis.onPrivate("min", (value) => {
            xAxisMinor.setPrivate("min", value);
        });
        xAxis.onPrivate("max", (value) => {
            xAxisMinor.setPrivate("max", value);
        });
        // sync minor x axis with main x axis
        xAxis.on("start", (value) => {
            xAxisMinor.set("start", value);
        });
        xAxis.on("end", (value) => {
            xAxisMinor.set("end", value);
        });
        // set min max if not set
        const baseDuration = xAxis.baseDuration();
        if (xAxis.getPrivate("min") === undefined) {
            xAxis.setPrivate("min", new Date().getTime());
        }
        if (xAxis.getPrivate("max") === undefined) {
            xAxis.setPrivate("max", new Date().getTime() + baseDuration * 3);
        }
        // set grid interval
        xAxis.onPrivate("gridInterval", (value) => {
            const gridIntervals = this.get("gridIntervals");
            if (gridIntervals && gridIntervals[value.timeUnit]) {
                xAxisMinor.set("gridIntervals", gridIntervals[value.timeUnit]);
            }
        });
        // Add series
        // https://www.amcharts.com/docs/v5/charts/xy-chart/series/
        var series = chart.series.push(GanttSeries.new(root, {
            xAxis: xAxis,
            yAxis: yAxis,
            baseAxis: yAxis,
            openValueXField: "start",
            valueXField: "end",
            progressField: "progress",
            durationField: "duration",
            linkToField: "linkTo",
            categoryYField: "id",
            idField: "id"
        }));
        series.gantt = this;
        // Add scrollbars
        this.scrollbarX = chart.set("scrollbarX", this.scrollbarX);
        this.scrollbarY = chart.set("scrollbarY", this.scrollbarY);
        this.scrollbarY.startGrip.set("forceHidden", true);
        this.scrollbarY.endGrip.set("forceHidden", true);
        this.children.push(this.scrollbarX);
        this.xyChart = chart;
        this.series = series;
        this.xAxis = xAxis;
        this.yAxis = yAxis;
        this.xAxisMinor = xAxisMinor;
        const eventType = "valueschanged";
        this.series.events.on(eventType, () => {
            if (this.get("editable")) {
                if (this.events.isEnabled(eventType)) {
                    this.events.dispatch(eventType, { type: eventType, target: this });
                }
            }
        });
        // ad dbehavior to buttons
        this.expandButton.events.on("click", () => {
            this.yAxis.expandAll();
        });
        this.collapseButton.events.on("click", () => {
            this.yAxis.collapseAll();
        });
        this.clearButton.events.on("confirmed", () => {
            this.clearAll();
        });
        this.linkButton.on("active", (active) => {
            this.set("linkNewTasks", active);
        });
        this.colorPicker.on("color", (c) => {
            this.yAxis.setDataItemColor(this.yAxis.get("selectedDataItem"), c);
        });
        this.editButton.on("active", (active) => {
            this.set("editable", active);
        });
        this.fitButton.events.on("click", () => {
            let min = series.getPrivate("selectionMinX", 0);
            let max = series.getPrivate("selectionMaxX", 0);
            const extraMin = xAxis.get("extraMin", 0.1);
            const extraMax = xAxis.get("extraMax", 0.1);
            xAxis.zoomToValues(min - (max - min) * extraMin, max + (max - min) * extraMax);
            /*
            xAxis.setAll({
                "strictMinMax": false,
                "strictMinMaxSelection": false,
                "autoZoom": true,
            })

    
            else {
                xAxis.setAll({
                    "strictMinMax": true,
                    "strictMinMaxSelection": true,
                    "autoZoom": false
                })

                xAxis.setPrivate("selectionMin", undefined);
                xAxis.setPrivate("selectionMax", undefined);
            }*/
        });
        this.zoomOutButton.events.on("click", () => {
            xAxis.zoom(0, 1);
        });
        let license = false;
        for (let i = 0; i < registry.licenses.length; i++) {
            if (registry.licenses[i].match(/^AM5G.{5,}/i)) {
                license = true;
            }
        }
        if (!license) {
            this._root._showBranding();
        }
        else {
            this._root._licenseApplied();
        }
    }
    // end _afterNew()
    _prepareChildren() {
        super._prepareChildren();
        if (this.isDirty("editable")) {
            this._toggleEditable(this.get("editable", true));
        }
    }
    clearAll() {
        var _a;
        this.yAxis.deleteAll();
        this._customColor = undefined;
        (_a = this.get("colors")) === null || _a === void 0 ? void 0 : _a.reset();
        this.colorPickerButton.set("color", undefined);
        this._nextColor();
        const today = new Date();
        today.setHours(0, 0, 0, 0);
        this.xAxis.setPrivate("min", today.getTime());
        this.xAxis.setPrivate("max", today.getTime() + this.xAxis.baseDuration() * 5);
        for (let i = this.xAxisMinor.axisRanges.length - 1; i >= 0; i--) {
            const axisRange = this.xAxisMinor.axisRanges.getIndex(i);
            if (axisRange) {
                this.unmarkDate(axisRange.get("value"));
            }
        }
        ;
    }
    _updateChildren() {
        super._updateChildren();
        if (this.isDirty("linkNewTasks")) {
            this.linkButton.set("active", this.get("linkNewTasks", false));
        }
        if (this.isDirty("editable")) {
            this.editButton.set("active", this.get("editable", true));
        }
        if (this._sizeDirty || this.isDirty("sidebarWidth")) {
            let width = $utils.relativeToValue(this.get("sidebarWidth", percent(20)), this.innerWidth());
            width = Math.max(width, this.yAxis.get("minWidth", 100));
            this.yAxis.labelsContainer.set("width", width);
            this._updateScrollbar();
        }
        if (this.isDirty("durationUnit")) {
            const durationUnit = this.get("durationUnit");
            if (durationUnit) {
                this.xAxis.set("baseInterval", { timeUnit: durationUnit, count: 1 });
                this.xAxisMinor.set("baseInterval", { timeUnit: durationUnit, count: 1 });
            }
        }
        if (this.colorPickerButton.getPrivate("color") === undefined) {
            this._nextColor();
        }
    }
    _updateScrollbar() {
        const point = this.xyChart.plotContainer.toGlobal({ x: 0, y: 0 });
        this.scrollbarX.setAll({
            x: point.x,
            width: this.xyChart.plotContainer.width()
        });
    }
    /**
     * Adds a new task to the Gantt chart.
     *
     * @param category        The category name for the task. If not provided, will be auto-generated.
     * @param start           The start time of the task in milliseconds. If not provided, will be determined based on context.
     * @param duration        The duration of the task. If not provided, defaults to 1.
     * @param parentId        The parent category if this is a subtask.
     * @param progress        The initial progress of the task (0-1). Defaults to 0.
     * @param linkToPrevious  Should the task automatically link to the previous one?
     */
    addNewTask(name, start, duration = 1, parentId, progress = 0, linkToPrevious) {
        // Generate a unique ID for the new task
        let len = this.series.dataItems.length;
        let c = this.yAxis.dataItems.length;
        $array.each(this.yAxis.dataItems, (dataItem) => {
            if (dataItem.get("id") == "gantt_" + c) {
                c++;
            }
        });
        const uid = "gantt_" + c;
        if (!name) {
            name = "New Task";
        }
        if (linkToPrevious === undefined) {
            linkToPrevious = this.get("linkNewTasks", false);
        }
        let parentDataItem;
        // Find parent data item either by specified category or selected item
        if (parentId != undefined) {
            parentDataItem = this.yAxis.getDataItemById(parentId);
        }
        else {
            parentDataItem = this.yAxis.get("selectedDataItem");
        }
        let childCount = 0;
        // Handle parent-child relationship if a parent exists
        if (parentDataItem) {
            parentId = parentDataItem.get("id");
            if (parentId) {
                // If start time not specified, try to use parent's start time
                if (start === undefined) {
                    const seriesDataItem = this.series.getDataItemById(parentId);
                    if (seriesDataItem) {
                        start = seriesDataItem.get("valueX", new Date().getTime());
                    }
                }
            }
            // Initialize or get the children array for the parent
            let children = parentDataItem.get("children");
            if (!children) {
                children = parentDataItem.set("children", []);
            }
            childCount = children.length;
        }
        // Determine start time if not specified
        if (start === undefined) {
            start = $time.roun(this.xAxis.getPrivate("min", 0), this.get("durationUnit", "day"), 1, this.root);
            if (this.series.dataItems.length > 0) {
                start = this.series.dataItems[len - 1].get("valueX", new Date().getTime());
            }
        }
        const dataObject = {
            name: name,
            id: uid
        };
        if (parentId) {
            dataObject["parentId"] = parentId;
        }
        // Add the category data
        let index = 0;
        let previousDataItem;
        if (parentDataItem) {
            index = this.yAxis.dataItems.indexOf(parentDataItem) + 1 + childCount;
            const children = parentDataItem.get("children", []);
            let len = this.yAxis.dataItems.length;
            if (index >= len) {
                if (children.length > 0) {
                    previousDataItem = children[children.length - 1];
                }
                this.yAxis.data.push(dataObject);
            }
            else {
                if (children.length > 0) {
                    previousDataItem = this.yAxis.dataItems[index - 1];
                }
                this.yAxis.data.insertIndex(index, dataObject);
            }
            index++;
        }
        else {
            // go backwards and find the first item that has no parent
            for (let i = this.yAxis.dataItems.length - 1; i >= 0; i--) {
                const dataItem = this.yAxis.dataItems[i];
                if (!dataItem.get("parentId")) {
                    previousDataItem = dataItem;
                    break;
                }
            }
            this.yAxis.data.push(dataObject);
            index = this.yAxis.dataItems.length;
        }
        // Add the task data with slightly offset start time to avoid overlapping
        const baseDuration = this.xAxis.baseDuration();
        const seriesDataObject = {
            start: start,
            duration: duration,
            progress: progress,
            id: uid,
            name: name
        };
        // create link to previous task if needed
        if (linkToPrevious && previousDataItem) {
            const seriesDataItem = this.series.getDataItemById(previousDataItem.get("id"));
            if (seriesDataItem) {
                const linkTo = seriesDataItem.get("linkTo", []);
                if (!linkTo.includes(uid)) {
                    linkTo.push(uid);
                    seriesDataItem.set("linkTo", linkTo);
                }
            }
        }
        this.series.data.moveValue(seriesDataObject, index - 1);
        const newSeriesDataItem = this.series.dataItems[index - 1];
        newSeriesDataItem.animate({ key: "valueX", to: start + baseDuration * duration, duration: this.series.get("interpolationDuration", 0), easing: this.series.get("interpolationEasing") });
        this.yAxis._disposeXHideDP();
        this.yAxis.xButton.hide(0);
        const maxZoomCount = this.yAxis.get("maxZoomCount", 20);
        let startIndex = Math.max(0, Math.round(index - maxZoomCount / 2));
        let endIndex = startIndex + maxZoomCount;
        len = this.yAxis.dataItems.length;
        if (endIndex > len) {
            endIndex = len;
            startIndex = Math.max(0, endIndex - maxZoomCount);
        }
        this.yAxis.zoomToIndexes(startIndex, endIndex);
        this.yAxis.adjustZoom();
    }
    // Get next color from the color set
    _nextColor() {
        var _a;
        if (this._customColor) {
            return this._customColor;
        }
        const fill = (_a = this.get("colors")) === null || _a === void 0 ? void 0 : _a.next();
        this.colorPickerButton.setPrivate("color", fill);
        return fill;
    }
    /**
     * Marks a date on the minor date axis.
     *
     * @param date  Date to be marked
     */
    markDate(date) {
        const xAxisMinor = this.xAxisMinor;
        const dataItem = xAxisMinor.createAxisRange(xAxisMinor.makeDataItem({
            value: date
        }));
        const eventType = "datemarked";
        if (this.events.isEnabled(eventType)) {
            this.events.dispatch(eventType, { type: eventType, target: this, dataItem: dataItem, date: date });
        }
    }
    /**
     * Unmarks a date on the minor date axis.
     *
     * @param date  Date to be unmarked
     */
    unmarkDate(date) {
        const xAxisMinor = this.xAxisMinor;
        let dataItem = undefined;
        xAxisMinor.axisRanges.each((axisRange) => {
            // remove axis range if already exists
            if (axisRange && axisRange.get("value") === date) {
                xAxisMinor.axisRanges.removeValue(axisRange);
                dataItem = axisRange;
            }
        });
        const eventType = "dateunmarked";
        if (this.events.isEnabled(eventType)) {
            this.events.dispatch(eventType, { type: eventType, target: this, dataItem: dataItem, date: date });
        }
    }
    _toggleEditable(value) {
        const forceHidden = "forceHidden";
        const forceInactive = "forceInactive";
        const draggable = "draggable";
        this.series.markDirtyValues();
        this.addButton.set(forceHidden, !value);
        this.colorPickerButton.set(forceHidden, !value);
        this.clearButton.set(forceHidden, !value);
        this.linkButton.set(forceHidden, !value);
        const renderer = this.yAxis.get("renderer");
        renderer.labels.template.set(forceInactive, !value);
        renderer.grips.template.set(forceHidden, !value);
        renderer.containers.template.set(draggable, value);
        renderer.controlsContainers.template.set(forceInactive, !value);
        this.series.columns.template.set(draggable, value);
        this.series.startGrips.template.set(forceHidden, !value);
        this.series.endGrips.template.set(forceHidden, !value);
        this.series.progressGrips.template.set(forceHidden, !value);
        this.series.startBullets.template.set(forceHidden, !value);
        this.series.endBullets.template.set(forceHidden, !value);
        this.series.links.template.set(forceInactive, !value);
        this.xAxisMinor.set(forceInactive, !value);
    }
}
Gantt.className = "Gantt";
Gantt.classNames = Container.classNames.concat([Gantt.className]);
//# sourceMappingURL=Gantt.js.map