import { equals as uint8ArrayEquals } from 'uint8arrays/equals';
import { bytesToComponents, componentsToBytes, componentsToString, stringToComponents } from "./components.js";
import { InvalidMultiaddrError, InvalidParametersError } from "./errors.js";
import { isMultiaddr } from "./index.js";
import { registry } from "./registry.js";
const inspect = Symbol.for('nodejs.util.inspect.custom');
export const symbol = Symbol.for('@multiformats/multiaddr');
function toComponents(addr) {
    if (addr == null) {
        addr = '/';
    }
    if (isMultiaddr(addr)) {
        return addr.getComponents();
    }
    if (addr instanceof Uint8Array) {
        return bytesToComponents(addr);
    }
    if (typeof addr === 'string') {
        addr = addr
            .replace(/\/(\/)+/, '/')
            .replace(/(\/)+$/, '');
        if (addr === '') {
            addr = '/';
        }
        return stringToComponents(addr);
    }
    if (Array.isArray(addr)) {
        return addr;
    }
    throw new InvalidMultiaddrError('Must be a string, Uint8Array, Component[], or another Multiaddr');
}
/**
 * Creates a {@link Multiaddr} from a {@link MultiaddrInput}
 */
export class Multiaddr {
    [symbol] = true;
    #components;
    // cache string representation
    #string;
    // cache byte representation
    #bytes;
    constructor(addr = '/', options = {}) {
        this.#components = toComponents(addr);
        if (options.validate !== false) {
            validate(this);
        }
    }
    get bytes() {
        if (this.#bytes == null) {
            this.#bytes = componentsToBytes(this.#components);
        }
        return this.#bytes;
    }
    toString() {
        if (this.#string == null) {
            this.#string = componentsToString(this.#components);
        }
        return this.#string;
    }
    toJSON() {
        return this.toString();
    }
    getComponents() {
        return [
            ...this.#components.map(c => ({ ...c }))
        ];
    }
    encapsulate(addr) {
        const ma = new Multiaddr(addr);
        return new Multiaddr([
            ...this.#components,
            ...ma.getComponents()
        ], {
            validate: false
        });
    }
    decapsulate(addr) {
        const addrString = addr.toString();
        const s = this.toString();
        const i = s.lastIndexOf(addrString);
        if (i < 0) {
            throw new InvalidParametersError(`Address ${this.toString()} does not contain subaddress: ${addrString}`);
        }
        return new Multiaddr(s.slice(0, i), {
            validate: false
        });
    }
    decapsulateCode(code) {
        let index;
        for (let i = this.#components.length - 1; i > -1; i--) {
            if (this.#components[i].code === code) {
                index = i;
                break;
            }
        }
        return new Multiaddr(this.#components.slice(0, index), {
            validate: false
        });
    }
    equals(addr) {
        return uint8ArrayEquals(this.bytes, addr.bytes);
    }
    /**
     * Returns Multiaddr as a human-readable string
     * https://nodejs.org/api/util.html#utilinspectcustom
     *
     * @example
     * ```js
     * import { multiaddr } from '@multiformats/multiaddr'
     *
     * console.info(multiaddr('/ip4/127.0.0.1/tcp/4001'))
     * // 'Multiaddr(/ip4/127.0.0.1/tcp/4001)'
     * ```
     */
    [inspect]() {
        return `Multiaddr(${this.toString()})`;
    }
}
/**
 * Ensures all multiaddr tuples are correct. Throws if any invalid protocols or
 * values are encountered.
 */
export function validate(addr) {
    addr.getComponents()
        .forEach(component => {
        const codec = registry.getProtocol(component.code);
        if (component.value == null) {
            return;
        }
        codec.validate?.(component.value);
    });
}
//# sourceMappingURL=multiaddr.js.map