"use strict";
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var _a, _b;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDomainPolarity = exports.ScaleContinuous = exports.logBaseMap = exports.LogBase = exports.limitLogScaleDomain = void 0;
var d3_array_1 = require("d3-array");
var d3_scale_1 = require("d3-scale");
var screenspace_marker_scale_compressor_1 = require("../solvers/screenspace_marker_scale_compressor");
var common_1 = require("../utils/common");
var date_time_1 = require("../utils/data/date_time");
var constants_1 = require("./constants");
var SCALES = (_a = {},
    _a[constants_1.ScaleType.Linear] = d3_scale_1.scaleLinear,
    _a[constants_1.ScaleType.Log] = d3_scale_1.scaleLog,
    _a[constants_1.ScaleType.Sqrt] = d3_scale_1.scaleSqrt,
    _a[constants_1.ScaleType.Time] = d3_scale_1.scaleUtc,
    _a);
var isUnitRange = function (_a) {
    var _b = __read(_a, 2), r1 = _b[0], r2 = _b[1];
    return r1 === 0 && r2 === 1;
};
function limitLogScaleDomain(_a, logMinLimit) {
    var _b = __read(_a, 2), min = _b[0], max = _b[1];
    var absLimit = logMinLimit !== undefined ? Math.abs(logMinLimit) : undefined;
    if (absLimit !== undefined && absLimit > 0) {
        if (min > 0 && min < absLimit) {
            if (max > absLimit) {
                return [absLimit, max];
            }
            return [absLimit, absLimit];
        }
        if (max < 0 && max > -absLimit) {
            if (min < -absLimit) {
                return [min, -absLimit];
            }
            return [-absLimit, -absLimit];
        }
    }
    var fallbackLimit = absLimit || constants_1.LOG_MIN_ABS_DOMAIN;
    if (min === 0) {
        if (max > 0) {
            return [fallbackLimit, max];
        }
        if (max < 0) {
            return [-fallbackLimit, max];
        }
        return [fallbackLimit, fallbackLimit];
    }
    if (max === 0) {
        if (min > 0) {
            return [min, fallbackLimit];
        }
        if (min < 0) {
            return [min, -fallbackLimit];
        }
        return [fallbackLimit, fallbackLimit];
    }
    if (min < 0 && max > 0) {
        var isD0Min = Math.abs(max) - Math.abs(min) >= 0;
        if (isD0Min) {
            return [fallbackLimit, max];
        }
        return [min, -fallbackLimit];
    }
    if (min > 0 && max < 0) {
        var isD0Max = Math.abs(min) - Math.abs(max) >= 0;
        if (isD0Max) {
            return [min, fallbackLimit];
        }
        return [-fallbackLimit, max];
    }
    return [min, max];
}
exports.limitLogScaleDomain = limitLogScaleDomain;
function getPixelPaddedDomain(chartHeight, domain, desiredPixelPadding, constrainDomainPadding, intercept) {
    if (intercept === void 0) { intercept = 0; }
    var inverted = domain[1] < domain[0];
    var orderedDomain = inverted ? domain.slice().reverse() : domain;
    var scaleMultiplier = screenspace_marker_scale_compressor_1.screenspaceMarkerScaleCompressor(orderedDomain, [2 * desiredPixelPadding, 2 * desiredPixelPadding], chartHeight).scaleMultiplier;
    var paddedDomainLo = orderedDomain[0] - desiredPixelPadding / scaleMultiplier;
    var paddedDomainHigh = orderedDomain[1] + desiredPixelPadding / scaleMultiplier;
    if (constrainDomainPadding) {
        if (paddedDomainLo < intercept && orderedDomain[0] >= intercept) {
            var scaleMultiplier_1 = screenspace_marker_scale_compressor_1.screenspaceMarkerScaleCompressor([intercept, orderedDomain[1]], [0, 2 * desiredPixelPadding], chartHeight).scaleMultiplier;
            paddedDomainLo = intercept;
            paddedDomainHigh = orderedDomain[1] + desiredPixelPadding / scaleMultiplier_1;
        }
        else if (paddedDomainHigh > 0 && orderedDomain[1] <= 0) {
            var scaleMultiplier_2 = screenspace_marker_scale_compressor_1.screenspaceMarkerScaleCompressor([orderedDomain[0], intercept], [2 * desiredPixelPadding, 0], chartHeight).scaleMultiplier;
            paddedDomainLo = orderedDomain[0] - desiredPixelPadding / scaleMultiplier_2;
            paddedDomainHigh = intercept;
        }
    }
    return inverted ? [paddedDomainHigh, paddedDomainLo] : [paddedDomainLo, paddedDomainHigh];
}
exports.LogBase = Object.freeze({
    Common: 'common',
    Binary: 'binary',
    Natural: 'natural',
});
exports.logBaseMap = (_b = {},
    _b[exports.LogBase.Common] = 10,
    _b[exports.LogBase.Binary] = 2,
    _b[exports.LogBase.Natural] = Math.E,
    _b);
var defaultScaleOptions = {
    bandwidth: 0,
    minInterval: 0,
    timeZone: 'utc',
    totalBarsInCluster: 1,
    barsPadding: 0,
    constrainDomainPadding: true,
    domainPixelPadding: 0,
    desiredTickCount: 10,
    isSingleValueHistogram: false,
    integersOnly: false,
    logBase: exports.LogBase.Common,
};
var ScaleContinuous = (function () {
    function ScaleContinuous(_a, options) {
        var _this = this;
        var _b = _a.type, type = _b === void 0 ? constants_1.ScaleType.Linear : _b, _c = _a.domain, domain = _c === void 0 ? [0, 1] : _c, _d = _a.range, range = _d === void 0 ? [0, 1] : _d, _e = _a.nice, nice = _e === void 0 ? false : _e;
        var _f = common_1.mergePartial(defaultScaleOptions, options, { mergeOptionalPartialValues: true }), bandwidth = _f.bandwidth, minInterval = _f.minInterval, timeZone = _f.timeZone, totalBarsInCluster = _f.totalBarsInCluster, barsPadding = _f.barsPadding, desiredTickCount = _f.desiredTickCount, isSingleValueHistogram = _f.isSingleValueHistogram, integersOnly = _f.integersOnly, logBase = _f.logBase, logMinLimit = _f.logMinLimit, domainPixelPadding = _f.domainPixelPadding, constrainDomainPadding = _f.constrainDomainPadding;
        this.d3Scale = SCALES[type]();
        if (type === constants_1.ScaleType.Log) {
            this.d3Scale.base(exports.logBaseMap[logBase]);
            this.domain = limitLogScaleDomain(domain, logMinLimit);
        }
        else {
            this.domain = domain;
        }
        this.d3Scale.domain(this.domain);
        if (nice && type !== constants_1.ScaleType.Time) {
            this.d3Scale.domain(this.domain).nice(desiredTickCount);
            this.domain = this.d3Scale.domain();
        }
        var safeBarPadding = common_1.maxValueWithUpperLimit(barsPadding, 0, 1);
        this.barsPadding = safeBarPadding;
        this.bandwidth = bandwidth * (1 - safeBarPadding);
        this.bandwidthPadding = bandwidth * safeBarPadding;
        this.d3Scale.range(range);
        this.step = this.bandwidth + this.barsPadding + this.bandwidthPadding;
        this.type = type;
        this.range = range;
        this.minInterval = minInterval;
        this.isInverted = this.domain[0] > this.domain[1];
        this.timeZone = timeZone;
        this.totalBarsInCluster = totalBarsInCluster;
        this.isSingleValueHistogram = isSingleValueHistogram;
        var _g = __read(this.range, 2), r1 = _g[0], r2 = _g[1];
        var totalRange = Math.abs(r1 - r2);
        if (type !== constants_1.ScaleType.Time && domainPixelPadding && !isUnitRange(range) && domainPixelPadding * 2 < totalRange) {
            var newDomain = getPixelPaddedDomain(totalRange, this.domain, domainPixelPadding, constrainDomainPadding);
            if (nice) {
                this.d3Scale.domain(newDomain).nice(desiredTickCount);
                this.domain = this.d3Scale.domain();
            }
            else {
                this.domain = newDomain;
                this.d3Scale.domain(newDomain);
            }
        }
        if (type === constants_1.ScaleType.Time) {
            var startDomain = date_time_1.getMomentWithTz(this.domain[0], this.timeZone);
            var endDomain = date_time_1.getMomentWithTz(this.domain[1], this.timeZone);
            var offset_1 = startDomain.utcOffset();
            var shiftedDomainMin = startDomain.add(offset_1, 'minutes').valueOf();
            var shiftedDomainMax = endDomain.add(offset_1, 'minutes').valueOf();
            var tzShiftedScale = d3_scale_1.scaleUtc().domain([shiftedDomainMin, shiftedDomainMax]);
            var rawTicks = tzShiftedScale.ticks(desiredTickCount);
            var timePerTick = (shiftedDomainMax - shiftedDomainMin) / rawTicks.length;
            var hasHourTicks_1 = timePerTick < 1000 * 60 * 60 * 12;
            this.tickValues = rawTicks.map(function (d) {
                var currentDateTime = date_time_1.getMomentWithTz(d, _this.timeZone);
                var currentOffset = hasHourTicks_1 ? offset_1 : currentDateTime.utcOffset();
                return currentDateTime.subtract(currentOffset, 'minutes').valueOf();
            });
        }
        else {
            if (minInterval > 0 && bandwidth > 0) {
                var intervalCount = Math.floor((this.domain[1] - this.domain[0]) / this.minInterval);
                this.tickValues = new Array(intervalCount + 1).fill(0).map(function (_, i) { return _this.domain[0] + i * _this.minInterval; });
            }
            else {
                this.tickValues = this.getTicks(desiredTickCount, integersOnly);
            }
        }
    }
    ScaleContinuous.prototype.getScaledValue = function (value) {
        if (typeof value !== 'number' || isNaN(value)) {
            return null;
        }
        var scaledValue = this.d3Scale(value);
        return isNaN(scaledValue) ? null : scaledValue;
    };
    ScaleContinuous.prototype.getTicks = function (ticks, integersOnly) {
        return integersOnly
            ? this.d3Scale
                .ticks(ticks)
                .filter(function (item) { return typeof item === 'number' && item % 1 === 0; })
                .map(function (item) { return parseInt(item.toFixed(0), 10); })
            : this.d3Scale.ticks(ticks);
    };
    ScaleContinuous.prototype.scaleOrThrow = function (value) {
        var scaleValue = this.scale(value);
        if (scaleValue === null) {
            throw new Error("Unable to scale value: " + scaleValue + ")");
        }
        return scaleValue;
    };
    ScaleContinuous.prototype.scale = function (value) {
        var scaledValue = this.getScaledValue(value);
        return scaledValue === null ? null : scaledValue + (this.bandwidthPadding / 2) * this.totalBarsInCluster;
    };
    ScaleContinuous.prototype.pureScale = function (value) {
        if (this.bandwidth === 0) {
            return this.getScaledValue(value);
        }
        if (typeof value !== 'number' || isNaN(value)) {
            return null;
        }
        return this.getScaledValue(value + this.minInterval / 2);
    };
    ScaleContinuous.prototype.ticks = function () {
        return this.tickValues;
    };
    ScaleContinuous.prototype.invert = function (value) {
        var invertedValue = this.d3Scale.invert(value);
        if (this.type === constants_1.ScaleType.Time) {
            invertedValue = date_time_1.getMomentWithTz(invertedValue, this.timeZone).valueOf();
        }
        return invertedValue;
    };
    ScaleContinuous.prototype.invertWithStep = function (value, data) {
        if (data.length === 0) {
            return null;
        }
        var invertedValue = this.invert(value);
        var bisectValue = this.bandwidth === 0 ? invertedValue + this.minInterval / 2 : invertedValue;
        var leftIndex = d3_array_1.bisectLeft(data, bisectValue);
        if (leftIndex === 0) {
            if (invertedValue < data[0]) {
                return {
                    value: data[0] - this.minInterval * Math.ceil((data[0] - invertedValue) / this.minInterval),
                    withinBandwidth: false,
                };
            }
            return {
                value: data[0],
                withinBandwidth: true,
            };
        }
        var currentValue = data[leftIndex - 1];
        if (this.minInterval === 0) {
            var nextValue = data[leftIndex];
            var nextDiff = Math.abs(nextValue - invertedValue);
            var prevDiff = Math.abs(invertedValue - currentValue);
            return {
                value: nextDiff <= prevDiff ? nextValue : currentValue,
                withinBandwidth: true,
            };
        }
        if (invertedValue - currentValue <= this.minInterval) {
            return {
                value: currentValue,
                withinBandwidth: true,
            };
        }
        return {
            value: currentValue + this.minInterval * Math.floor((invertedValue - currentValue) / this.minInterval),
            withinBandwidth: false,
        };
    };
    ScaleContinuous.prototype.isSingleValue = function () {
        if (this.isSingleValueHistogram) {
            return true;
        }
        if (this.domain.length < 2) {
            return true;
        }
        var min = this.domain[0];
        var max = this.domain[this.domain.length - 1];
        return max === min;
    };
    ScaleContinuous.prototype.isValueInDomain = function (value) {
        return value >= this.domain[0] && value <= this.domain[1];
    };
    ScaleContinuous.prototype.handleDomainPadding = function () { };
    return ScaleContinuous;
}());
exports.ScaleContinuous = ScaleContinuous;
function getDomainPolarity(domain) {
    var _a = __read(domain, 2), min = _a[0], max = _a[1];
    if (min >= 0 && max >= 0) {
        return 1;
    }
    if (min <= 0 && max <= 0) {
        return -1;
    }
    return 0;
}
exports.getDomainPolarity = getDomainPolarity;
//# sourceMappingURL=scale_continuous.js.map