"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.splitRangePattern = exports.splitFallbackRangePattern = exports.parseDateTimeSkeleton = exports.processDateTimePattern = void 0;
var tslib_1 = require("tslib");
var ecma402_abstract_1 = require("@formatjs/ecma402-abstract");
/**
 * https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
 * Credit: https://github.com/caridy/intl-datetimeformat-pattern/blob/master/index.js
 * with some tweaks
 */
var DATE_TIME_REGEX = /(?:[Eec]{1,6}|G{1,5}|[Qq]{1,5}|(?:[yYur]+|U{1,5})|[ML]{1,5}|d{1,2}|D{1,3}|F{1}|[abB]{1,5}|[hkHK]{1,2}|w{1,2}|W{1}|m{1,2}|s{1,2}|[zZOvVxX]{1,4})(?=([^']*'[^']*')*[^']*$)/g;
// trim patterns after transformations
var expPatternTrimmer = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
function matchSkeletonPattern(match, result) {
    var len = match.length;
    switch (match[0]) {
        // Era
        case 'G':
            result.era = len === 4 ? 'long' : len === 5 ? 'narrow' : 'short';
            return '{era}';
        // Year
        case 'y':
        case 'Y':
        case 'u':
        case 'U':
        case 'r':
            result.year = len === 2 ? '2-digit' : 'numeric';
            return '{year}';
        // Quarter
        case 'q':
        case 'Q':
            throw new RangeError('`w/Q` (quarter) patterns are not supported');
        // Month
        case 'M':
        case 'L':
            result.month = ['numeric', '2-digit', 'short', 'long', 'narrow'][len - 1];
            return '{month}';
        // Week
        case 'w':
        case 'W':
            throw new RangeError('`w/W` (week of year) patterns are not supported');
        case 'd':
            result.day = ['numeric', '2-digit'][len - 1];
            return '{day}';
        case 'D':
        case 'F':
        case 'g':
            result.day = 'numeric';
            return '{day}';
        // Weekday
        case 'E':
            result.weekday = len === 4 ? 'long' : len === 5 ? 'narrow' : 'short';
            return '{weekday}';
        case 'e':
            result.weekday = [
                undefined,
                undefined,
                'short',
                'long',
                'narrow',
                'short',
            ][len - 1];
            return '{weekday}';
        case 'c':
            result.weekday = [
                undefined,
                undefined,
                'short',
                'long',
                'narrow',
                'short',
            ][len - 1];
            return '{weekday}';
        // Period
        case 'a': // AM, PM
        case 'b': // am, pm, noon, midnight
        case 'B': // flexible day periods
            result.hour12 = true;
            return '{ampm}';
        // Hour
        case 'h':
            result.hour = ['numeric', '2-digit'][len - 1];
            result.hour12 = true;
            return '{hour}';
        case 'H':
            result.hour = ['numeric', '2-digit'][len - 1];
            return '{hour}';
        case 'K':
            result.hour = ['numeric', '2-digit'][len - 1];
            result.hour12 = true;
            return '{hour}';
        case 'k':
            result.hour = ['numeric', '2-digit'][len - 1];
            return '{hour}';
        case 'j':
        case 'J':
        case 'C':
            throw new RangeError('`j/J/C` (hour) patterns are not supported, use `h/H/K/k` instead');
        // Minute
        case 'm':
            result.minute = ['numeric', '2-digit'][len - 1];
            return '{minute}';
        // Second
        case 's':
            result.second = ['numeric', '2-digit'][len - 1];
            return '{second}';
        case 'S':
        case 'A':
            result.second = 'numeric';
            return '{second}';
        // Zone
        case 'z': // 1..3, 4: specific non-location format
        case 'Z': // 1..3, 4, 5: The ISO8601 varios formats
        case 'O': // 1, 4: miliseconds in day short, long
        case 'v': // 1, 4: generic non-location format
        case 'V': // 1, 2, 3, 4: time zone ID or city
        case 'X': // 1, 2, 3, 4: The ISO8601 varios formats
        case 'x': // 1, 2, 3, 4: The ISO8601 varios formats
            result.timeZoneName = len < 4 ? 'short' : 'long';
            return '{timeZoneName}';
    }
    return '';
}
function skeletonTokenToTable2(c) {
    switch (c) {
        // Era
        case 'G':
            return 'era';
        // Year
        case 'y':
        case 'Y':
        case 'u':
        case 'U':
        case 'r':
            return 'year';
        // Month
        case 'M':
        case 'L':
            return 'month';
        // Day
        case 'd':
        case 'D':
        case 'F':
        case 'g':
            return 'day';
        // Period
        case 'a': // AM, PM
        case 'b': // am, pm, noon, midnight
        case 'B': // flexible day periods
            return 'ampm';
        // Hour
        case 'h':
        case 'H':
        case 'K':
        case 'k':
            return 'hour';
        // Minute
        case 'm':
            return 'minute';
        // Second
        case 's':
        case 'S':
        case 'A':
            return 'second';
        default:
            throw new RangeError('Invalid range pattern token');
    }
}
function processDateTimePattern(pattern, result) {
    var literals = [];
    // Use skeleton to populate result, but use mapped pattern to populate pattern
    var pattern12 = pattern
        // Double apostrophe
        .replace(/'{2}/g, '{apostrophe}')
        // Apostrophe-escaped
        .replace(/'(.*?)'/g, function (_, literal) {
        literals.push(literal);
        return "$$".concat(literals.length - 1, "$$");
    })
        .replace(DATE_TIME_REGEX, function (m) { return matchSkeletonPattern(m, result || {}); });
    //Restore literals
    if (literals.length) {
        pattern12 = pattern12
            .replace(/\$\$(\d+)\$\$/g, function (_, i) {
            return literals[+i];
        })
            .replace(/\{apostrophe\}/g, "'");
    }
    // Handle apostrophe-escaped things
    return [
        pattern12
            .replace(/([\s\uFEFF\xA0])\{ampm\}([\s\uFEFF\xA0])/, '$1')
            .replace('{ampm}', '')
            .replace(expPatternTrimmer, ''),
        pattern12,
    ];
}
exports.processDateTimePattern = processDateTimePattern;
/**
 * Parse Date time skeleton into Intl.DateTimeFormatOptions
 * Ref: https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
 * @public
 * @param skeleton skeleton string
 */
function parseDateTimeSkeleton(skeleton, rawPattern, rangePatterns, intervalFormatFallback) {
    if (rawPattern === void 0) { rawPattern = skeleton; }
    var result = {
        pattern: '',
        pattern12: '',
        skeleton: skeleton,
        rawPattern: rawPattern,
        rangePatterns: {},
        rangePatterns12: {},
    };
    if (rangePatterns) {
        for (var k in rangePatterns) {
            var key = skeletonTokenToTable2(k);
            var rawPattern_1 = rangePatterns[k];
            var intervalResult = {
                patternParts: [],
            };
            var _a = processDateTimePattern(rawPattern_1, intervalResult), pattern_1 = _a[0], pattern12_1 = _a[1];
            result.rangePatterns[key] = tslib_1.__assign(tslib_1.__assign({}, intervalResult), { patternParts: splitRangePattern(pattern_1) });
            result.rangePatterns12[key] = tslib_1.__assign(tslib_1.__assign({}, intervalResult), { patternParts: splitRangePattern(pattern12_1) });
        }
    }
    if (intervalFormatFallback) {
        var patternParts = splitFallbackRangePattern(intervalFormatFallback);
        result.rangePatterns.default = {
            patternParts: patternParts,
        };
        result.rangePatterns12.default = {
            patternParts: patternParts,
        };
    }
    // Process skeleton
    skeleton.replace(DATE_TIME_REGEX, function (m) { return matchSkeletonPattern(m, result); });
    var _b = processDateTimePattern(rawPattern), pattern = _b[0], pattern12 = _b[1];
    result.pattern = pattern;
    result.pattern12 = pattern12;
    return result;
}
exports.parseDateTimeSkeleton = parseDateTimeSkeleton;
function splitFallbackRangePattern(pattern) {
    var parts = pattern.split(/(\{[0|1]\})/g).filter(Boolean);
    return parts.map(function (pattern) {
        switch (pattern) {
            case '{0}':
                return {
                    source: ecma402_abstract_1.RangePatternType.startRange,
                    pattern: pattern,
                };
            case '{1}':
                return {
                    source: ecma402_abstract_1.RangePatternType.endRange,
                    pattern: pattern,
                };
            default:
                return {
                    source: ecma402_abstract_1.RangePatternType.shared,
                    pattern: pattern,
                };
        }
    });
}
exports.splitFallbackRangePattern = splitFallbackRangePattern;
function splitRangePattern(pattern) {
    var PART_REGEX = /\{(.*?)\}/g;
    // Map of part and index within the string
    var parts = {};
    var match;
    var splitIndex = 0;
    while ((match = PART_REGEX.exec(pattern))) {
        if (!(match[0] in parts)) {
            parts[match[0]] = match.index;
        }
        else {
            splitIndex = match.index;
            break;
        }
    }
    if (!splitIndex) {
        return [
            {
                source: ecma402_abstract_1.RangePatternType.startRange,
                pattern: pattern,
            },
        ];
    }
    return [
        {
            source: ecma402_abstract_1.RangePatternType.startRange,
            pattern: pattern.slice(0, splitIndex),
        },
        {
            source: ecma402_abstract_1.RangePatternType.endRange,
            pattern: pattern.slice(splitIndex),
        },
    ];
}
exports.splitRangePattern = splitRangePattern;
