// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

import {
    toggleLabels,
    adjustLabelsThatOverflow,
    selectVisibleNodes,
} from "./treemapLabel";
import treemapLayout from "./treemapLayout";
import { changeLevel, returnToLevel } from "./treemapTransitions";
import { parentControls } from "./treemapControls";
import { calculateRootLevelMap, saveLabelMap } from "./treemapLevelCalculation";

export const nodeLevel = {
    leaf: "leafnode",
    branch: "branchnode",
    root: "rootnode",
};
export const calcWidth = (d) => d.x1 - d.x0;
export const calcHeight = (d) => d.y1 - d.y0;
const isLeafNode = (maxDepth, d) => d.depth === maxDepth;
const nodeLevelHelper = (maxDepth, d) =>
    d.depth === 0
        ? nodeLevel.root
        : isLeafNode(maxDepth, d)
        ? nodeLevel.leaf
        : nodeLevel.branch;

export function treemapSeries() {
    let settings = null;
    let root_settings = null;
    let data = null;
    let color = null;
    let treemapDiv = null;
    let parentCtrls = null;

    const _treemapSeries = (treemapSvg) => {
        parentCtrls = parentControls(treemapDiv);
        parentCtrls();

        const maxDepth = data.height;
        if (!settings.treemapLevel) settings.treemapLevel = 0;
        if (!settings.treemapRoute) settings.treemapRoute = [];
        const treemap = treemapLayout(
            treemapDiv.node().getBoundingClientRect().width,
            treemapDiv.node().getBoundingClientRect().height
        );
        treemap(data);

        const nodes = treemapSvg.selectAll("g").data(data.descendants());
        const nodesEnter = nodes.enter().append("g");

        nodesEnter.append("rect");
        nodesEnter.append("text");

        // Draw child nodes first
        const nodesMerge = nodesEnter
            .merge(nodes)
            .sort((a, b) => b.depth - a.depth);

        const rects = nodesMerge
            .select("rect")
            .attr("class", (d) => `treerect ${nodeLevelHelper(maxDepth, d)}`)
            .style("x", (d) => d.x0)
            .style("y", (d) => d.y0)
            .style("width", (d) => calcWidth(d))
            .style("height", (d) => calcHeight(d));

        rects.style("fill", (d) => {
            if (nodeLevelHelper(maxDepth, d) === nodeLevel.leaf) {
                if (d.data.color) {
                    return color(d.data.color);
                } else {
                    return root_settings.colorStyles.series;
                }
            } else {
                return "transparent";
            }
        });

        const labels = nodesMerge
            .filter((d) => d.value !== 0)
            .select("text")
            .attr("x", (d) => d.x0 + calcWidth(d) / 2)
            .attr("y", (d) => d.y0 + calcHeight(d) / 2)
            .text((d) => d.label);

        const rootNode = rects.filter((d) => d.crossValue.length === 0).datum();
        calculateRootLevelMap(nodesMerge, rootNode);

        toggleLabels(nodesMerge, 0, []);
        adjustLabelsThatOverflow(selectVisibleNodes(nodesMerge));
        saveLabelMap(nodesMerge, 0);

        if (settings.treemapRoute.length === 0)
            settings.treemapRoute.push(rootNode.crossValue);
        rects
            .filter((d) => d.children)
            .on("click", (_event, d) =>
                changeLevel(
                    d,
                    rects,
                    nodesMerge,
                    labels,
                    settings,
                    treemapDiv,
                    treemapSvg,
                    rootNode,
                    parentCtrls,
                    root_settings
                )
            );

        returnToLevel(
            rects,
            nodesMerge,
            labels,
            settings,
            treemapDiv,
            treemapSvg,
            rootNode,
            parentCtrls,
            root_settings
        );
    };

    _treemapSeries.settings = (...args) => {
        if (!args.length) {
            return settings;
        }
        settings = args[0];
        root_settings = args[1];
        return _treemapSeries;
    };

    _treemapSeries.data = (...args) => {
        if (!args.length) {
            return data;
        }
        data = args[0];
        return _treemapSeries;
    };

    _treemapSeries.color = (...args) => {
        if (!args.length) {
            return color;
        }
        color = args[0];
        return _treemapSeries;
    };

    _treemapSeries.container = (...args) => {
        if (!args.length) {
            return treemapDiv;
        }
        treemapDiv = args[0];
        return _treemapSeries;
    };

    return _treemapSeries;
}
