import { funPrefixNamer } from "../../Naming.js";
import { allLowerWordStyle, combineWords, escapeNonPrintableMapper, firstUpperWordStyle, intToHex, isAscii, isLetterOrUnderscore, isLetterOrUnderscoreOrDigit, isPrintable, legalizeCharacters, splitIntoWords, utf32ConcatMap, } from "../../support/Strings.js";
export var Density;
(function (Density) {
    Density["Normal"] = "Normal";
    Density["Dense"] = "Dense";
})(Density || (Density = {}));
export var Visibility;
(function (Visibility) {
    Visibility["Private"] = "Private";
    Visibility["Crate"] = "Crate";
    Visibility["Public"] = "Public";
})(Visibility || (Visibility = {}));
export const namingStyles = {
    snake_case: {
        regex: /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/,
        toParts: (name) => name.split("_"),
        fromParts: (parts) => parts.map((p) => p.toLowerCase()).join("_"),
    },
    SCREAMING_SNAKE_CASE: {
        regex: /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/,
        toParts: (name) => name.split("_"),
        fromParts: (parts) => parts.map((p) => p.toUpperCase()).join("_"),
    },
    camelCase: {
        regex: /^[a-z]+([A-Z0-9][a-z]*)*$/,
        toParts: (name) => namingStyles.snake_case.toParts(name.replace(/(.)([A-Z])/g, "$1_$2")),
        fromParts: (parts) => parts
            .map((p, i) => i === 0
            ? p.toLowerCase()
            : p.substring(0, 1).toUpperCase() +
                p.substring(1).toLowerCase())
            .join(""),
    },
    PascalCase: {
        regex: /^[A-Z][a-z]*([A-Z0-9][a-z]*)*$/,
        toParts: (name) => namingStyles.snake_case.toParts(name.replace(/(.)([A-Z])/g, "$1_$2")),
        fromParts: (parts) => parts
            .map((p) => p.substring(0, 1).toUpperCase() +
            p.substring(1).toLowerCase())
            .join(""),
    },
    "kebab-case": {
        regex: /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,
        toParts: (name) => name.split("-"),
        fromParts: (parts) => parts.map((p) => p.toLowerCase()).join("-"),
    },
    "SCREAMING-KEBAB-CASE": {
        regex: /^[A-Z][A-Z0-9]*(-[A-Z0-9]+)*$/,
        toParts: (name) => name.split("-"),
        fromParts: (parts) => parts.map((p) => p.toUpperCase()).join("-"),
    },
    lowercase: {
        regex: /^[a-z][a-z0-9]*$/,
        toParts: (name) => [name],
        fromParts: (parts) => parts.map((p) => p.toLowerCase()).join(""),
    },
    UPPERCASE: {
        regex: /^[A-Z][A-Z0-9]*$/,
        toParts: (name) => [name],
        fromParts: (parts) => parts.map((p) => p.toUpperCase()).join(""),
    },
};
namingStyles;
const isAsciiLetterOrUnderscoreOrDigit = (codePoint) => {
    if (!isAscii(codePoint)) {
        return false;
    }
    return isLetterOrUnderscoreOrDigit(codePoint);
};
const isAsciiLetterOrUnderscore = (codePoint) => {
    if (!isAscii(codePoint)) {
        return false;
    }
    return isLetterOrUnderscore(codePoint);
};
const legalizeName = legalizeCharacters(isAsciiLetterOrUnderscoreOrDigit);
function rustStyle(original, isSnakeCase) {
    const words = splitIntoWords(original);
    const wordStyle = isSnakeCase ? allLowerWordStyle : firstUpperWordStyle;
    const combined = combineWords(words, legalizeName, wordStyle, wordStyle, wordStyle, wordStyle, isSnakeCase ? "_" : "", isAsciiLetterOrUnderscore);
    return combined === "_" ? "_underscore" : combined;
}
export const snakeNamingFunction = funPrefixNamer("default", (original) => rustStyle(original, true));
export const camelNamingFunction = funPrefixNamer("camel", (original) => rustStyle(original, false));
const standardUnicodeRustEscape = (codePoint) => {
    if (codePoint <= 0xffff) {
        return "\\u{" + intToHex(codePoint, 4) + "}";
    }
    else {
        return "\\u{" + intToHex(codePoint, 6) + "}";
    }
};
export const rustStringEscape = utf32ConcatMap(escapeNonPrintableMapper(isPrintable, standardUnicodeRustEscape));
export function getPreferredNamingStyle(namingStyleOccurences, defaultStyle) {
    const occurrences = Object.fromEntries(Object.keys(namingStyles).map((key) => [key, 0]));
    namingStyleOccurences.forEach((style) => ++occurrences[style]);
    const max = Math.max(...Object.values(occurrences));
    const preferedStyles = Object.entries(occurrences).flatMap(([style, num]) => (num === max ? [style] : []));
    if (preferedStyles.includes(defaultStyle)) {
        return defaultStyle;
    }
    return preferedStyles[0];
}
export function listMatchingNamingStyles(name) {
    return Object.entries(namingStyles).flatMap(([namingStyleKey, { regex }]) => regex.test(name) ? [namingStyleKey] : []);
}
export function nameWithNamingStyle(name, style) {
    if (namingStyles[style].regex.test(name)) {
        return name;
    }
    const fromStyle = listMatchingNamingStyles(name)[0];
    if (fromStyle === undefined) {
        return name;
    }
    return namingStyles[style].fromParts(namingStyles[fromStyle].toParts(name));
}
