/**
 * Copyright © Volker Schukai and all contributing authors, {{copyRightYear}}. All rights reserved.
 * Node module: @schukai/monster
 *
 * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
 * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
 *
 * For those who do not wish to adhere to the AGPLv3, a commercial license is available.
 * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
 * For more information about purchasing a commercial license, please contact Volker Schukai.
 *
 * SPDX-License-Identifier: AGPL-3.0
 */

import { proxyInstanceMarker } from "../constants.mjs";

export {
	isIterable,
	isPrimitive,
	isSymbol,
	isBoolean,
	isString,
	isObject,
	isInstance,
	isArray,
	isFunction,
	isInteger,
	isNumber,
	isProxy,
	isElement,
};

/**
 * Checks whether the value passed is a DOM Element.
 * @param {*} value
 * @returns {boolean}
 */
function isElement(value) {
	if (typeof Element === "undefined") {
		return false;
	}
	return value instanceof Element;
}

/**
 * Checks whether the value passed is a Proxy.
 *
 * @param {*} value
 * @returns {boolean}
 */
function isProxy(value) {
	return value?.[proxyInstanceMarker] === proxyInstanceMarker;
}

/**
 * With this function you can check if a value is iterable.
 *
 * This method is used in the library to have consistent names.
 *
 * You can call the method via the monster namespace `Monster.Types.isPrimitive()`.
 *
 * @externalExample ../../example/types/is-1.mjs
 * @param {*} value
 * @return {boolean}
 * @license AGPLv3
 * @since 1.2.0
 * @copyright Volker Schukai
 */
function isIterable(value) {
	if (value === undefined) return false;
	if (value === null) return false;
	return typeof value?.[Symbol.iterator] === "function";
}

/**
 * Checks whether the value passed is a primitive (string, number, boolean, NaN, undefined, null or symbol)
 *
 * This method is used in the library to have consistent names.
 *
 * @externalExample ../../example/types/is-2.mjs
 * @param {*} value
 * @return {boolean}
 * @license AGPLv3
 * @since 1.0.0
 * @copyright Volker Schukai
 */
function isPrimitive(value) {
	var type;

	if (value === undefined || value === null) {
		return true;
	}

	type = typeof value;

	if (
		type === "string" ||
		type === "number" ||
		type === "boolean" ||
		type === "symbol"
	) {
		return true;
	}

	return false;
}

/**
 * Checks whether the value passed is a symbol
 *
 * This method is used in the library to have consistent names.
 *
 * @externalExample ../../example/types/is-3.mjs
 * @param {*} value
 * @return {boolean}
 * @license AGPLv3
 * @since 1.9.0
 * @copyright Volker Schukai
 */
function isSymbol(value) {
	return "symbol" === typeof value ? true : false;
}

/**
 * Checks whether the value passed is a boolean.
 *
 * This method is used in the library to have consistent names.
 *
 * @externalExample ../../example/types/is-4.mjs
 * @param {*} value
 * @return {boolean}
 * @license AGPLv3
 * @since 1.0.0
 * @copyright Volker Schukai
 */
function isBoolean(value) {
	if (value === true || value === false) {
		return true;
	}

	return false;
}

/**
 * Checks whether the value passed is a string
 *
 * This method is used in the library to have consistent names.
 *
 * @externalExample ../../example/types/is-5.mjs
 * @param {*} value
 * @return {boolean}
 * @license AGPLv3
 * @since 1.0.0
 * @copyright Volker Schukai
 */
function isString(value) {
	if (value === undefined || typeof value !== "string") {
		return false;
	}
	return true;
}

/**
 * Checks whether the value passed is a object
 *
 * This method is used in the library to have consistent names.
 *
 * @externalExample ../../example/types/is-6.mjs
 * @param {*} value
 * @return {boolean}
 * @license AGPLv3
 * @since 1.0.0
 * @copyright Volker Schukai
 */
function isObject(value) {
	if (isArray(value)) return false;
	if (isPrimitive(value)) return false;

	if (typeof value === "object") {
		return true;
	}

	return false;
}

/**
 * Checks whether the value passed is a object and instance of instance.
 *
 * This method is used in the library to have consistent names.
 *
 * @externalExample ../../example/types/is-7.mjs
 * @param {*} value
 * @param {*} instance
 * @return {boolean}
 * @license AGPLv3
 * @since 1.5.0
 * @copyright Volker Schukai
 */
function isInstance(value, instance) {
	if (!isObject(value)) return false;
	if (!isFunction(instance)) return false;
	if (!instance.hasOwnProperty("prototype")) return false;
	if (value instanceof instance) return true;

	let proto = Object.getPrototypeOf(value);
	while (proto != null) {
		if (proto === instance.prototype) return true;
		proto = Object.getPrototypeOf(proto);
	}

	return false;
}

/**
 * Checks whether the value passed is a array
 *
 * This method is used in the library to have consistent names.
 *
 * @externalExample ../../example/types/is-8.mjs
 * @param {*} value
 * @return {boolean}
 * @license AGPLv3
 * @since 1.0.0
 * @copyright Volker Schukai
 * @see https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
 */
function isArray(value) {
	return Array.isArray(value);
}

/**
 * Checks whether the value passed is a function
 *
 * This method is used in the library to have consistent names.
 *
 * @externalExample ../../example/types/is-9.mjs
 * @param {*} value
 * @return {boolean}
 * @license AGPLv3
 * @since 1.0.0
 * @copyright Volker Schukai
 */
function isFunction(value) {
	if (isArray(value)) return false;
	if (isPrimitive(value)) return false;

	if (typeof value === "function") {
		return true;
	}

	return false;
}

/**
 * Cechs whether the value passed is a number.
 *
 * This method is used in the library to have consistent names.
 *
 * @externalExample ../../example/types/is-number.mjs
 *
 * @param {*} value
 * @return {boolean}
 *
 * @license AGPLv3
 * @since 4.47.0
 */
function isNumber(value) {
	return typeof value === "number" && !isNaN(value);
}

/**
 * Checks whether the value passed is an integer.
 *
 * This method is used in the library to have consistent names.
 *
 * @externalExample ../../example/types/is-10.mjs
 * @param {*} value
 * @return {boolean}
 * @license AGPLv3
 * @since 1.4.0
 * @copyright Volker Schukai
 */
function isInteger(value) {
	return Number.isInteger(value);
}
