"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
var config_1 = require("../config/config");
var geometry_1 = require("../geometry");
var config_types_1 = require("../types/config_types");
var viewmodel_types_1 = require("../types/viewmodel_types");
var color_library_wrappers_1 = require("../utils/color_library_wrappers");
var constants_1 = require("../utils/constants");
var group_by_rollup_1 = require("../utils/group_by_rollup");
var math_1 = require("../utils/math");
var sunburst_1 = require("../utils/sunburst");
var treemap_1 = require("../utils/treemap");
var fill_text_layout_1 = require("./fill_text_layout");
var link_text_layout_1 = require("./link_text_layout");
function grooveAccessor(n) {
    return group_by_rollup_1.entryValue(n).depth > 1 ? 1 : [0, 2][group_by_rollup_1.entryValue(n).depth];
}
function topGrooveAccessor(topGroovePx) {
    return function (n) { return (group_by_rollup_1.entryValue(n).depth > 0 ? topGroovePx : grooveAccessor(n)); };
}
function rectangleFillOrigins(n) {
    return [(n.x0 + n.x1) / 2, (n.y0 + n.y1) / 2];
}
exports.ringSectorInnerRadius = function (n) { return n.y0px; };
exports.ringSectorOuterRadius = function (n) { return n.y1px; };
exports.ringSectorMiddleRadius = function (n) { return n.yMidPx; };
function sectorFillOrigins(fillOutside) {
    return function (node) {
        var midAngle = (node.x0 + node.x1) / 2;
        var divider = 10;
        var innerBias = fillOutside ? 9 : 1;
        var outerBias = divider - innerBias;
        var radius = (innerBias * exports.ringSectorInnerRadius(node) + outerBias * exports.ringSectorOuterRadius(node)) / divider;
        var cx = Math.cos(math_1.trueBearingToStandardPositionAngle(midAngle)) * radius;
        var cy = Math.sin(math_1.trueBearingToStandardPositionAngle(midAngle)) * radius;
        return [cx, cy];
    };
}
function makeQuadViewModel(childNodes, layers, sectorLineWidth, sectorLineStroke) {
    return childNodes.map(function (node) {
        var opacityMultiplier = 1;
        var layer = layers[node.depth - 1];
        var fillColorSpec = layer && layer.shape && layer.shape.fillColor;
        var fill = fillColorSpec || 'rgba(128,0,0,0.5)';
        var shapeFillColor = typeof fill === 'function' ? fill(node, node.sortIndex, node.parent.children) : fill;
        var _a = color_library_wrappers_1.stringToRGB(shapeFillColor), r = _a.r, g = _a.g, b = _a.b, opacity = _a.opacity;
        var fillColor = color_library_wrappers_1.argsToRGBString(r, g, b, opacity * opacityMultiplier);
        var strokeWidth = sectorLineWidth;
        var strokeStyle = sectorLineStroke;
        return __assign({ strokeWidth: strokeWidth, strokeStyle: strokeStyle, fillColor: fillColor }, node);
    });
}
exports.makeQuadViewModel = makeQuadViewModel;
function makeOutsideLinksViewModel(outsideFillNodes, rowSets, linkLabelRadiusPadding) {
    return outsideFillNodes
        .map(function (node, i) {
        var rowSet = rowSets[i];
        if (!rowSet.rows.reduce(function (p, row) { return p + row.rowWords.length; }, 0))
            return { points: [] };
        var radius = exports.ringSectorOuterRadius(node);
        var midAngle = math_1.trueBearingToStandardPositionAngle(geometry_1.meanAngle(node.x0, node.x1));
        var cos = Math.cos(midAngle);
        var sin = Math.sin(midAngle);
        var x0 = cos * radius;
        var y0 = sin * radius;
        var x = cos * (radius + linkLabelRadiusPadding);
        var y = sin * (radius + linkLabelRadiusPadding);
        return {
            points: [
                [x0, y0],
                [x, y],
            ],
        };
    })
        .filter(function (_a) {
        var points = _a.points;
        return points.length > 1;
    });
}
exports.makeOutsideLinksViewModel = makeOutsideLinksViewModel;
function rectangleConstruction(treeHeight, topGroove) {
    return function (node) {
        return node.depth < treeHeight
            ? {
                x0: node.x0,
                y0: node.y0px,
                x1: node.x1,
                y1: node.y0px + treemap_1.getTopPadding(topGroove, node.y1px - node.y0px),
            }
            : {
                x0: node.x0,
                y0: node.y0px,
                x1: node.x1,
                y1: node.y1px,
            };
    };
}
function shapeViewModel(textMeasure, config, layers, rawTextGetter, specifiedValueFormatter, specifiedPercentFormatter, valueGetter, tree, topGroove, containerBackgroundColor) {
    var width = config.width, height = config.height, margin = config.margin, emptySizeRatio = config.emptySizeRatio, outerSizeRatio = config.outerSizeRatio, fillOutside = config.fillOutside, linkLabel = config.linkLabel, clockwiseSectors = config.clockwiseSectors, specialFirstInnermostSector = config.specialFirstInnermostSector, minFontSize = config.minFontSize, partitionLayout = config.partitionLayout, sectorLineWidth = config.sectorLineWidth;
    var innerWidth = width * (1 - Math.min(1, margin.left + margin.right));
    var innerHeight = height * (1 - Math.min(1, margin.top + margin.bottom));
    var diskCenter = {
        x: width * margin.left + innerWidth / 2,
        y: height * margin.top + innerHeight / 2,
    };
    if (!(width > 0) || !(height > 0) || tree.length === 0) {
        return viewmodel_types_1.nullShapeViewModel(config, diskCenter);
    }
    var totalValue = tree.reduce(function (p, n) { return p + group_by_rollup_1.mapEntryValue(n); }, 0);
    var sunburstValueToAreaScale = constants_1.TAU / totalValue;
    var sunburstAreaAccessor = function (e) { return sunburstValueToAreaScale * group_by_rollup_1.mapEntryValue(e); };
    var treemapLayout = partitionLayout === config_types_1.PartitionLayout.treemap;
    var treemapInnerArea = treemapLayout ? width * height : 1;
    var treemapValueToAreaScale = treemapInnerArea / totalValue;
    var treemapAreaAccessor = function (e) { return treemapValueToAreaScale * group_by_rollup_1.mapEntryValue(e); };
    var rawChildNodes = treemapLayout
        ? treemap_1.treemap(tree, treemapAreaAccessor, topGrooveAccessor(topGroove), grooveAccessor, {
            x0: -width / 2,
            y0: -height / 2,
            width: width,
            height: height,
        })
        : sunburst_1.sunburst(tree, sunburstAreaAccessor, { x0: 0, y0: -1 }, clockwiseSectors, specialFirstInnermostSector);
    var shownChildNodes = rawChildNodes.filter(function (n) {
        var layerIndex = group_by_rollup_1.entryValue(n.node).depth - 1;
        var layer = layers[layerIndex];
        return !layer || !layer.showAccessor || layer.showAccessor(group_by_rollup_1.entryKey(n.node));
    });
    var circleMaximumSize = Math.min(innerWidth, innerHeight);
    var outerRadius = Math.min(outerSizeRatio * circleMaximumSize, circleMaximumSize - sectorLineWidth) / 2;
    var innerRadius = outerRadius - (1 - emptySizeRatio) * outerRadius;
    var treeHeight = shownChildNodes.reduce(function (p, n) { return Math.max(p, group_by_rollup_1.entryValue(n.node).depth); }, 0);
    var ringThickness = (outerRadius - innerRadius) / treeHeight;
    var partToShapeFn = partToShapeTreeNode(treemapLayout, innerRadius, ringThickness);
    var quadViewModel = makeQuadViewModel(shownChildNodes.slice(1).map(partToShapeFn), layers, config.sectorLineWidth, config.sectorLineStroke);
    var roomCondition = function (n) {
        var diff = n.x1 - n.x0;
        return treemapLayout
            ? n.x1 - n.x0 > minFontSize && n.y1px - n.y0px > minFontSize
            : (diff < 0 ? constants_1.TAU + diff : diff) * exports.ringSectorMiddleRadius(n) > Math.max(minFontSize, linkLabel.maximumSection);
    };
    var nodesWithRoom = quadViewModel.filter(roomCondition);
    var outsideFillNodes = fillOutside && !treemapLayout ? nodesWithRoom : [];
    var textFillOrigins = nodesWithRoom.map(treemapLayout ? rectangleFillOrigins : sectorFillOrigins(fillOutside));
    var valueFormatter = valueGetter === config_1.percentValueGetter ? specifiedPercentFormatter : specifiedValueFormatter;
    var getRowSets = treemapLayout
        ? fill_text_layout_1.fillTextLayout(rectangleConstruction(treeHeight, topGroove), fill_text_layout_1.getRectangleRowGeometry, function () { return 0; }, containerBackgroundColor)
        : fill_text_layout_1.fillTextLayout(fill_text_layout_1.ringSectorConstruction(config, innerRadius, ringThickness), fill_text_layout_1.getSectorRowGeometry, fill_text_layout_1.inSectorRotation(config.horizontalTextEnforcer, config.horizontalTextAngleThreshold), containerBackgroundColor);
    var rowSets = getRowSets(textMeasure, rawTextGetter, valueGetter, valueFormatter, nodesWithRoom, config, layers, textFillOrigins, treemapLayout, !treemapLayout);
    var outsideLinksViewModel = makeOutsideLinksViewModel(outsideFillNodes, rowSets, linkLabel.radiusPadding);
    var currentY = [-height, -height, -height, -height];
    var nodesWithoutRoom = fillOutside || treemapLayout
        ? []
        : quadViewModel.filter(function (n) {
            var id = fill_text_layout_1.nodeId(n);
            var foundInFillText = rowSets.find(function (r) { return r.id === id; });
            return !(foundInFillText && foundInFillText.rows.length !== 0);
        });
    var maxLinkedLabelTextLength = config.linkLabel.maxTextLength;
    var linkLabelViewModels = link_text_layout_1.linkTextLayout(width, height, textMeasure, config, nodesWithoutRoom, currentY, outerRadius, rawTextGetter, valueGetter, valueFormatter, maxLinkedLabelTextLength, diskCenter, containerBackgroundColor);
    var pickQuads = function (x, y) {
        return quadViewModel.filter(treemapLayout
            ? function (_a) {
                var x0 = _a.x0, y0 = _a.y0, x1 = _a.x1, y1 = _a.y1;
                return x0 <= x && x <= x1 && y0 <= y && y <= y1;
            }
            : function (_a) {
                var x0 = _a.x0, y0px = _a.y0px, x1 = _a.x1, y1px = _a.y1px;
                var angleX = (Math.atan2(y, x) + constants_1.TAU / 4 + constants_1.TAU) % constants_1.TAU;
                var yPx = Math.sqrt(x * x + y * y);
                return x0 <= angleX && angleX <= x1 && y0px <= yPx && yPx <= y1px;
            });
    };
    return {
        config: config,
        diskCenter: diskCenter,
        quadViewModel: quadViewModel,
        rowSets: rowSets,
        linkLabelViewModels: linkLabelViewModels,
        outsideLinksViewModel: outsideLinksViewModel,
        pickQuads: pickQuads,
        outerRadius: outerRadius,
    };
}
exports.shapeViewModel = shapeViewModel;
function partToShapeTreeNode(treemapLayout, innerRadius, ringThickness) {
    return function (_a) {
        var node = _a.node, x0 = _a.x0, x1 = _a.x1, y0 = _a.y0, y1 = _a.y1;
        return ({
            dataName: group_by_rollup_1.entryKey(node),
            depth: group_by_rollup_1.depthAccessor(node),
            value: group_by_rollup_1.aggregateAccessor(node),
            parent: group_by_rollup_1.parentAccessor(node),
            sortIndex: group_by_rollup_1.sortIndexAccessor(node),
            x0: x0,
            x1: x1,
            y0: y0,
            y1: y1,
            y0px: treemapLayout ? y0 : innerRadius + y0 * ringThickness,
            y1px: treemapLayout ? y1 : innerRadius + y1 * ringThickness,
            yMidPx: treemapLayout ? (y0 + y1) / 2 : innerRadius + ((y0 + y1) / 2) * ringThickness,
        });
    };
}
//# sourceMappingURL=viewmodel.js.map