import { asWriteable } from '../Types';
/**
 * Represents the access levels a {@link Permission} can be given.
 */
export var Access;
(function (Access) {
    /** No access. */
    Access["none"] = "none";
    /** Can view. */
    Access["view"] = "view";
    /** Can add. */
    Access["add"] = "add";
    /** Can edit. */
    Access["edit"] = "edit";
    /** Can delete. */
    Access["delete"] = "delete";
})(Access || (Access = {}));
/**
 * Predefined permission masks. More will be added in the future.
 * Why not an enum? Enums can only use numbers but we need bigints sinec
 * the permission mask can have more bits than what number supports.
 */
const permissionMask = {
    [Access.none]: BigInt(0),
    [Access.view]: BigInt(1),
    [Access.add]: BigInt(2),
    [Access.edit]: BigInt(4),
    [Access.delete]: BigInt(8)
};
const permissionMasksInStringOutputOrder = (() => {
    return Object.entries(permissionMask)
        .filter((i) => typeof i[1] === 'bigint')
        .sort(([, a], [, b]) => {
        // sort must return a number and subtracting bigints
        // will yield a bigint. Hence we use the if statements instead.
        // Converting a bigint to a number will lead to loss of precision.
        if (a > b)
            return 1;
        if (a < b)
            return -1;
        return 0;
    });
})();
/**
 * Represents a set of permissions.
 */
export class Permissions {
    /**
     * Permission bitmask.
     * Since permissions are Uint64 bitmasks it must be represented
     * as a bigint. Watch out for `number` type coercion if
     * reading this property directly.
     * While it is possible to use bitwise operations on the mask, to determine
     * the permissions, it is better to use the provided methods such as {@link hasAny} and {@link hasAll}.
     */
    mask = permissionMask.none;
    /**
     * Constructor
     * @param permissions Permission instances to create the {@link Permissions} instance from.
     */
    constructor(...permissions) {
        this.mask = Permissions.join(permissions);
    }
    /**
     * Create a {@link Permissions} instance from a permission mask represented
     * as a bigint string. This is how permissions are represented in JSON as it is
     * not possible to send 64bit integers in JSON.
     * @param permission If a real bigint string then it is converted to a {@link Permissions} instance.
     * If it is a `falsy` returns a {@link Permissions} instance without any permissions.
     * If it is a `truthy` but not a bigint string then a exception is generated.
     * @returns {@link Permissions} instance.
     */
    static fromJsonObject(permission) {
        return !permission ? new Permissions() : new Permissions(BigInt(permission));
    }
    /**
     * Copies the permissions defined by `permissions` to the `dst`.permissions property.
     * @param permissions Permission to copy to `dst`.permissions property.
     * @param dst Change to use.
     * @returns Reference to `dst`.
     */
    static copyToRef(permissions, dst) {
        let p;
        if (permissions instanceof Permissions) {
            p = new Permissions(permissions.mask);
        }
        else if (typeof permissions === 'bigint' || typeof permissions === 'string') {
            p = new Permissions(permissions);
        }
        else {
            p = Permissions.fromJsonObject(permissions.json);
        }
        asWriteable(dst).permissions = p;
        return dst;
    }
    /**
     * Converts a number of {@link Permission} instances into a human readable string.
     * @param permissions Permissions to convert.
     * @returns Human readable string.
     */
    static toString(...permissions) {
        return new Permissions(Permissions.join(permissions)).toString();
    }
    /**
     * Join one or more permissions into a single numeric permission mask. Permissions are bitwise OR:ed together.
     * @param permissions Permissions to join.
     * @returns Numeric permission mask. Can be compared directly with {@link mask}.
     * Can also be used in constructor of {@link Permissions}.
     */
    static join(...permissions) {
        let mask = permissionMask.none;
        for (const permOrArray of permissions) {
            if (Array.isArray(permOrArray)) {
                for (const p of permOrArray) {
                    mask |= Permissions.toMask(p);
                }
            }
            else {
                mask |= Permissions.toMask(permOrArray);
            }
        }
        return mask;
    }
    /**
     * Checks if {@link mask} has any of the specified permissions.
     * @param permissions Permissions to compare with {@link mask}.
     * @returns `true` if {@link mask} holds any of the specified permissions.
     */
    hasAny(...permissions) {
        return (this.mask & Permissions.join(permissions)) !== permissionMask.none;
    }
    /**
     * Checks if {@link mask} has all of the specified permissions.
     * @param permissions Permissions to compare with {@link mask}.
     * @returns `true` if {@link mask} holds all of the specified permissions.
     */
    hasAll(...permissions) {
        const tmp = Permissions.join(permissions);
        return (this.mask & tmp) === tmp;
    }
    /**
     * Converts to a human readable string.
     * @returns Human readable string.
     */
    toString() {
        const segments = [];
        for (const [name, mask] of permissionMasksInStringOutputOrder) {
            const isEnabled = (this.mask & mask) !== permissionMask.none ||
                (mask === permissionMask.none && this.mask === permissionMask.none);
            if (isEnabled) {
                segments.push(name);
            }
        }
        return segments.join('|');
    }
    /**
     * Converts the Permission object to a JSON representation.
     * @returns The JSON representation of the Permission object.
     */
    toJson() {
        return this.mask.toString();
    }
    static toMask(perm) {
        if (typeof perm === 'bigint')
            return perm;
        // if perm is string then it is either a PermissionIndex or a bigint in the form of a string. Anything else is
        // a error.
        if (typeof perm === 'string')
            return permissionMask[perm] ?? BigInt(perm);
        return permissionMask.none;
    }
}
//# sourceMappingURL=Permission.js.map