/*!
 * OpenUI5
 * (c) Copyright 2009-2026 SAP SE or an SAP affiliate company.
 * Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
 */
/*
 * IMPORTANT: This is a private module, its API must not be used and is subject to change.
 * Code other than the OpenUI5 libraries must not introduce dependencies to this module.
 */
/*global UNorm */
sap.ui.define(["sap/ui/thirdparty/unorm", "sap/ui/thirdparty/unormdata"], function() {
	"use strict";

	function apply() {
		/*eslint-disable no-extend-native */
		String.prototype.normalize = function(str) {
			switch (str) {
				case 'NFC':
					return nfcQuickCheck(this) ? this : UNorm.nfc(this);
				case 'NFD':
					return UNorm.nfd(this);
				case 'NFKC':
					return UNorm.nfkc(this);
				case 'NFKD':
					return UNorm.nfkd(this);
				default:
					return nfcQuickCheck(this) ? this : UNorm.nfc(this);
			}
		};
		/*eslint-enable no-extend-native */
	}


	// build a map for quick access
	var mData = {};
	(function() {
		// derived from http://www.unicode.org/Public/8.0.0/ucd/DerivedNormalizationProps.txt
		var NFC_QC =  [
			[0x0340, 0x0341], [0x0343, 0x0344], 0x374, 0x037E, 0x387, [0x0958, 0x095F], [0x09DC, 0x09DD], 0x09DF, 0x0A33,
			0x0A36, [0x0A59, 0x0A5B], 0x0A5E, [0x0B5C, 0x0B5D], 0x0F43, 0x0F4D, 0x0F52, 0x0F57, 0x0F5C, 0x0F69,0x0F73,
			[0x0F75, 0x0F76], 0x0F78, 0x0F81, 0x0F93, 0x0F9D, 0x0FA2, 0x0FA7, 0x0FAC, 0x0FB9, 0x1F71, 0x1F73, 0x1F75, 0x1F77,
			0x1F79, 0x1F7B, 0x1F7D, 0x1FBB, 0x1FBE, 0x1FC9, 0x1FCB, 0x1FD3, 0x1FDB, 0x1FE3, 0x1FEB, [0x1FEE, 0x1FEF], 0x1FF9,
			0x1FFB, 0x1FFD, [0x2000, 0x2001], 0x2126, [0x212A, 0x212B], 0x2329, 0x232A, 0x2ADC, [0xF900, 0xFA0D], 0xFA10,
			0xFA12, [0xFA15, 0xFA1E], 0xFA20, 0xFA22, [0xFA25, 0xFA26], [0xFA2A, 0xFA6D], [0xFA70, 0xFAD9], 0xFB1D, 0xFB1F,
			[0xFB2A, 0xFB36], [0xFB38, 0xFB3C], 0xFB3E, [0xFB40, 0xFB41], [0xFB43, 0xFB44], [0xFB46, 0xFB4E], [0x1D15E,
			0x1D164], [0x1D1BB, 0x1D1C0], [0x2F800, 0x2FA1D], [0x0300, 0x0304], [0x0306, 0x030C], 0x030F, 0x311, [0x0313,
			0x0314], 0x031B, [0x0323, 0x0328], [0x032D, 0x032E], [0x0330, 0x0331], 0x338, 0x342, 0x345, [0x0653, 0x0655],
			0x093C, 0x09BE, 0x09D7, 0x0B3E, 0x0B56, 0x0B57, 0x0BBE, 0x0BD7, 0x0C56, 0x0CC2, [0x0CD5, 0x0CD6], 0x0D3E, 0x0D57,
			0x0DCA, 0x0DCF, 0x0DDF, 0x102E, [0x1161, 0x1175], [0x11A8, 0x11C2], 0x1B35, [0x3099, 0x309A], 0x110BA, 0x11127,
			0x1133E, 0x11357, 0x114B0, 0x114BA, 0x114BD, 0x115AF
		];

		// convert the reference list into a map containing all charcodes or ranges
		for (var i = 0; i < NFC_QC.length; i++) {
			if (typeof NFC_QC[i] == "number") {
				mData[NFC_QC[i]] = true;
			} else {
				var a = NFC_QC[i][0];
				var b = NFC_QC[i][1];
				while (a <= b) {
					mData[a++] = true;
				}
			}
		}
	}());

	// *** auxiliary functions ***
	function isHighSurrogate(cp) {
		// first part of a supplmementary character
		return cp >= 0xD800 && cp <= 0xDBFF;
	}
	function isLowSurrogate(cp) {
		// second part of a supplmementary character
		return cp >= 0xDC00 && cp <= 0xDFFF;
	}
	function getCanonicalClass(cp) {
		var dunit, hash;
		// access the unormdata
		hash = cp & 0xFF00;
		dunit = UNorm.UChar.udata[hash];
		if (dunit === undefined) {
			dunit = UNorm.UChar.udata[hash] = {};
		} else if (typeof dunit === "string") {
			// evil eval like unorm.js does it, as JSON.parse does not work for the unormdata
			/*eslint-disable no-eval */
			dunit = UNorm.UChar.udata[hash] = eval("(" + dunit + ")");
			/*eslint-enable no-eval */
		}
		// check for existence and validity of canonical class
		return dunit[cp] && !!dunit[cp][1] ? (dunit[cp][1] & 0xff) : 0;
	}
	function isNotAllowed(cp) {
		// check against the reference data
		return mData[cp];
	}

	// quickcheck implementations
	function nfcQuickCheck(s) {
		// check for only ascii characters first to avoid unnecessary processing
		if (!/^[\u0001-\u00ff]*$/.test(s)) {
			var lastCanonicalClass = 0;
			for (var i = 0; i < s.length; ++i) {
				var cp = s.charCodeAt(i);
				// check for supplementary characters, complex, as charCodeAt returns only charcodes smaller than 0xFFFF,
				// which is the Basic Multilingual Plane (BMP), but for normalizations also characters of the Supplementary
				// Multilingual Plane (SMP) have to be considered
				if (isHighSurrogate(cp)) {
					// check the next character
					var ncp = s.charCodeAt(i + 1);
					if (isLowSurrogate(ncp)) {
						// calculate the according char code in SMP and skip further tests for the next character (ncp)
						cp = (cp - 0xD800) * 0x400 + (ncp - 0xDC00) + 0x10000;
						++i;
					}
				}
				var canonicalClass = getCanonicalClass(cp);
				// check canonical class or reference list
				if (lastCanonicalClass > canonicalClass && canonicalClass !== 0 || isNotAllowed(cp)) {
					return false;
				}
				lastCanonicalClass = canonicalClass;
			}
		}
		// nothing has been found
		return true;
	}

	/**
	 * @namespace
	 * @since 1.58
	 * @alias module:sap/base/strings/NormalizePolyfill
	 * @private
	 */
	var NormalizePolyfill = Object.create(null);

	/**
	 * Applies the polyfill.
	 * @static
	 */
	NormalizePolyfill.apply = apply;

	/**
	 * Checks whether a string should be normalized or not. It evaluates NO and MAYBE entries of the exclusion table
	 * NFC_QC to false. This means it is not a definitive statement, but an indicator for normalization.
	 *
	 * @param s The string to be checked
	 * @returns {boolean} Indicating whether s could be NFC formatted or not
	 */
	NormalizePolyfill.isStringNFC = nfcQuickCheck;

	return NormalizePolyfill;

});