import { typeguards, compareArray } from '@neutrium/utilities';

import { Quantity } from '../Quantity.js'
import { throwIncompatibleUnits } from './errors.js';
import { inverse } from './maths.js';

const UNITY_ARRAY = ["<1>"];

export function eq(a: Quantity, b: string | number | Quantity): boolean
{
	return compareTo(a, b) === 0;
}

export function lt(a: Quantity, b: string | number | Quantity): boolean
{
	return compareTo(a, b) === -1;
}

export function lte(a: Quantity, b: string | number | Quantity): boolean
{
	return eq(a,b) || lt(a,b);
}

export function gt(a: Quantity, b: string | number | Quantity): boolean
{
	return compareTo(a, b) === 1;
}

export function gte(a: Quantity, b: string | number | Quantity): boolean
{
	return eq(a,b) || gt(a,b);
}

// Return true if quantities and units match
// Quantity("100 cm").same(Quantity("100 cm"))  # => true
// Quantity("100 cm").same(Quantity("1 m"))     # => false
export function same(a: Quantity, b: Quantity): boolean
{
	return a.scalar.eq(b.scalar) && (a.units() === b.units());
}

export function isInverse(a: Quantity, b: string | Quantity) : boolean
{
	return isCompatible(inverse(a), b);
}

// Compare two Qty objects. Throws an exception if they are not of compatible types.
// Comparisons are done based on the value of the quantity in base SI units.
//
// NOTE: We cannot compare inverses as that breaks the general compareTo contract:
//   if a.compareTo(b) < 0 then b.compareTo(a) > 0
//   if a.compareTo(b) == 0 then b.compareTo(a) == 0
//
//   Since "10S" == ".1ohm" (10 > .1) and "10ohm" == ".1S" (10 > .1)
//     Qty("10S").inverse().compareTo("10ohm") == -1
//     Qty("10ohm").inverse().compareTo("10S") == -1
//
//   If including inverses in the sort is needed, I suggest writing: Qty.sort(qtyArray,units)
export function compareTo(a: Quantity, b: string | number | Quantity)
{
	if (typeguards.isString(b) || typeguards.isNumber(b))
	{
		return compareTo(a, new Quantity(b));
	}

	if (!isCompatible(a, b))
	{
		throwIncompatibleUnits();
	}

	if (a.baseScalar.lt(b.baseScalar))
	{
		return -1;
	}
	else if (a.baseScalar.eq(b.baseScalar))
	{
		return 0;
	}
	else if (a.baseScalar.gt(b.baseScalar))
	{
		return 1;
	}
}

//
// Check to see if units are compatible, but not the scalar part
// this check is done by comparing signatures for performance reasons
// if passed a string, it will create a unit object with the string and then do the comparison
// this permits a syntax like:
// unit =~ "mm"
// if you want to do a regexp on the unit string do this ...
// unit.units =~ /regexp/
//
export function isCompatible(a: Quantity, b: string | number | Quantity) : boolean
{
	if (typeguards.isString(b))
	{
		return isCompatible(a, new Quantity(b));
	}

	if (!(b instanceof Quantity))
	{
		return false;
	}

	if (b.signature !== undefined)
	{
		return a.signature === b.signature;
	}
	else
	{
		return false;
	}
}

// returns true if no associated units
// false, even if the units are "unitless" like 'radians, each, etc'
export function isUnitless(a: Quantity) : boolean
{
	return compareArray(a.numerator, UNITY_ARRAY) && compareArray(a.denominator, UNITY_ARRAY);
}
