import * as i0 from '@angular/core';
import { inject, ElementRef, Renderer2, input, booleanAttribute, signal, computed, HostListener, Directive } from '@angular/core';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
import { SI_FORM_ITEM_CONTROL } from '@siemens/element-ng/form';

const isDigit = (c) => c >= '0' && c <= '9';
const isHex = (c) => (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F');
const recursiveSplitIpV4 = (options, input, sections = [{ value: '', partNo: 0 }], index = 0, cursorDelta = 0) => {
    // Base case: no more input to process
    if (input.length === 0) {
        return { sections, cursorDelta };
    }
    const current = sections.at(-1);
    const char = input[0];
    input = input.substring(1);
    if (isDigit(char)) {
        const part = `${current.value}${char}`;
        const limit = current.mask ? 32 : 255;
        // Append digits to current part until the part limit exceeds
        if (part.length <= 3 && parseInt(part, 10) <= limit) {
            current.value = part;
        }
        else {
            // Base case: IP completely entered
            if ((options.cidr && current.mask) || (!options.cidr && current.partNo === 3)) {
                return { sections, cursorDelta };
            }
            // Force separators since the part exceeded his limit
            if (current.partNo === 3) {
                input = `/${char}${input.replace('.', '').replace('/', '')}`;
            }
            else {
                const dotIndex = input.indexOf('.');
                if (dotIndex >= 0) {
                    input = input.substring(0, dotIndex) + input.substring(dotIndex + 1);
                }
                input = `.${char}${input}`;
            }
            // In case the cursor position is at the the position of the exceeded digit it is necessary
            // to move the cursor one position forward since a separator will be added before the digit
            if (index === options.pos) {
                cursorDelta = 1;
            }
        }
    }
    else if (char === '.' || char === '/') {
        // Handle separators
        if ('partNo' in current && current.partNo < 3) {
            sections.push({ value: '.' }, { value: '', partNo: current.partNo + 1 });
        }
        else if (options.cidr && !current.mask) {
            sections.push({ value: '/' }, { value: '', mask: true });
        }
    }
    return recursiveSplitIpV4(options, input, sections, index + 1, cursorDelta);
};
/**
 * Parse IPv4 input string into IPv4 address section array.
 */
const splitIpV4Sections = (options) => {
    const { input } = options;
    if (!input) {
        return { value: '', cursorDelta: 0 };
    }
    const { sections, cursorDelta } = recursiveSplitIpV4(options, input);
    return {
        value: sections.map(s => s.value).join(''),
        cursorDelta
    };
};
const splitIpV6Sections = (options) => {
    const { type, input, pos, zeroCompression, cidr } = options;
    const sections = [{ value: '' }];
    let cursorDelta = 0;
    if (!input) {
        return { value: '', cursorDelta: 0 };
    }
    for (let i = 0; i < input.length; i++) {
        const c = input.charAt(i).toUpperCase();
        if (isHex(c)) {
            sections.at(-1).value += c;
        }
        else if (c === ':') {
            if (input.charAt(i - 1) === c) {
                // Merge :: characters
                sections.at(-2).value += c;
            }
            else {
                sections.push({ value: c }, { value: '' });
            }
        }
        else if (cidr && c === '/') {
            sections.push({ value: c }, { value: '', mask: true });
        }
        if (pos === i) {
            sections.at(-1).current = true;
        }
    }
    // Split values > FFFF in multiple sections:
    // - 1FFFF will be split into 1FFF and F
    for (let i = 0; i < sections.length; i++) {
        const { value, current } = sections[i];
        if (value.length > 4) {
            const append = [];
            let charsProcessed = 0;
            for (let p = 0; p < value.length; p += 4) {
                const part = value.substring(p, p + 4);
                append.push({ value: part });
                if (part.length === 4) {
                    append.push({ value: ':' });
                    if (current && pos >= charsProcessed) {
                        cursorDelta++;
                    }
                }
                charsProcessed += 4;
            }
            sections.splice(i, 1, ...append);
            if (current) {
                sections[i + append.length - 1].current = true;
            }
        }
    }
    // Drop invalid zero compression indicators '::'
    const removeEnd = pos === input.length || type === 'paste';
    let matches = sections.filter(s => s.value.startsWith('::'));
    if (matches) {
        matches = removeEnd ? matches : matches.reverse();
        if (zeroCompression) {
            matches.shift();
        }
        // Only allow one occurrence of ::
        for (const drop of matches) {
            drop.value = drop.value.substring(1);
        }
    }
    // Ensure the that the CIDR divider is a slash
    if (cidr) {
        const startCidr = matches.length > 0 ? 13 : 15;
        if (startCidr < sections.length && sections[startCidr].value === ':') {
            sections[startCidr].value = '/';
        }
        const prefixPos = startCidr + 1;
        if (prefixPos < sections.length) {
            const prefixLength = sections[prefixPos].value;
            if (parseInt(prefixLength, 10) > 128) {
                sections[prefixPos].value = prefixLength.substring(0, 2);
            }
        }
    }
    const value = sections
        .splice(0, cidr ? 17 : 15)
        .map(s => s.value)
        .join('');
    return { value, cursorDelta };
};

const ipv4Regex = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
const ipv4CIDRRegex = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([1-9]|1[0-9]|2[0-9]|3[0-2]))$/;
const ipV6Regex = /^((?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|:(?::[0-9A-Fa-f]{1,4}){1,7}|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,6}))$/;
/**
 * Validator factory for a IPV6 address.
 */
const ipV6Validator = (options) => {
    return (control) => {
        const value = control.value;
        if (!value) {
            return null;
        }
        const parts = value.split('/');
        const error = { ipv6Address: true };
        if (options.cidr) {
            if (parts.length < 2 || !validateSubnet(parts[1])) {
                return error;
            }
        }
        else {
            if (parts.length > 1) {
                return error;
            }
        }
        if (parts[0].split('::').length > (options.zeroCompression ? 2 : 1)) {
            return error;
        }
        if (!matchIpV6(parts[0])) {
            return error;
        }
        return null;
    };
};
/**
 * Validates a IPV4 address.
 */
const ipV4Validator = (control) => {
    const v = control.value;
    const valid = v === '' || v?.toString().match(ipv4Regex);
    return valid ? null : { ipv4Address: true };
};
/**
 * Validates a IPV4 address including CIDR.
 */
const ipV4CIDRValidator = (control) => {
    const v = control.value;
    const valid = v === '' || v?.toString().match(ipv4CIDRRegex);
    return valid ? null : { ipv4Address: true };
};
const validateSubnet = (cidr) => {
    const subnet = parseInt(cidr, 10);
    return subnet > 0 && subnet <= 128;
};
const matchIpV6 = (ip) => !!ip.match(ipV6Regex) && !!URL.parse(`http://[${ip}]`);

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
const eventMap = new Map([
    ['insertText', 'insert'],
    ['insertFromPaste', 'paste'],
    ['deleteContentBackward', 'delete']
]);
/**
 * Base directive for ip address input fields.
 */
class SiIpInputDirective {
    static idCounter = 0;
    elementRef = inject((ElementRef));
    renderer = inject(Renderer2);
    inputEl = this.elementRef.nativeElement;
    /**
     * @defaultValue
     * ```
     * `si-ip-input-${SiIpInputDirective.idCounter++}`
     * ```
     */
    id = input(`si-ip-input-${SiIpInputDirective.idCounter++}`, ...(ngDevMode ? [{ debugName: "id" }] : []));
    /**
     * Enable CIDR (Classless Inter-Domain Routing) notation.
     * @defaultValue false
     */
    cidr = input(false, { ...(ngDevMode ? { debugName: "cidr" } : {}), transform: booleanAttribute });
    /**
     * Whether the ip address input is disabled.
     * @defaultValue false
     */
    // eslint-disable-next-line @angular-eslint/no-input-rename
    disabledInput = input(false, { ...(ngDevMode ? { debugName: "disabledInput" } : {}), alias: 'disabled' });
    disabledNgControl = signal(false, ...(ngDevMode ? [{ debugName: "disabledNgControl" }] : []));
    disabled = computed(() => this.disabledInput() || this.disabledNgControl(), ...(ngDevMode ? [{ debugName: "disabled" }] : []));
    onTouched = () => { };
    onChange = () => { };
    value = '';
    registerOnChange(fn) {
        this.onChange = fn;
    }
    registerOnTouched(fn) {
        this.onTouched = fn;
    }
    setDisabledState(isDisabled) {
        this.disabledNgControl.set(isDisabled);
    }
    writeValue(value) {
        this.value = value;
        this.renderer.setProperty(this.inputEl, 'value', value ?? '');
    }
    onInput(e) {
        const el = e.target;
        const selStart = el.selectionStart ?? 0;
        const { inputType, data } = e;
        this.maskInput({
            value: el.value,
            type: eventMap.get(inputType),
            change: data,
            pos: selStart,
            previous: this.value
        });
        this.value = el.value;
        this.onChange(this.value);
    }
    /** @internal */
    blur() {
        this.leaveInput();
        this.onTouched();
    }
    /**
     * Allow to adjust the input value when leaving the input field.
     */
    leaveInput() { }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiIpInputDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.8", type: SiIpInputDirective, isStandalone: true, selector: "input[siIpInput]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, cidr: { classPropertyName: "cidr", publicName: "cidr", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "input": "onInput($event)", "blur": "blur()" }, properties: { "id": "id()", "disabled": "disabled() || null" } }, providers: [
            {
                provide: SI_FORM_ITEM_CONTROL,
                useExisting: SiIpInputDirective
            }
        ], ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiIpInputDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'input[siIpInput]',
                    providers: [
                        {
                            provide: SI_FORM_ITEM_CONTROL,
                            useExisting: SiIpInputDirective
                        }
                    ],
                    host: {
                        '[id]': 'id()',
                        '[disabled]': 'disabled() || null'
                    }
                }]
        }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], cidr: [{ type: i0.Input, args: [{ isSignal: true, alias: "cidr", required: false }] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], onInput: [{
                type: HostListener,
                args: ['input', ['$event']]
            }], blur: [{
                type: HostListener,
                args: ['blur']
            }] } });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
/**
 * Directive for IPv4 address input fields.
 *
 * Usage:
 *
 * ```ts
 * import { SiFormItemComponent } from '@siemens/element-ng/form';
 * import { SiIp4InputDirective } from '@siemens/element-ng/ip-input';
 *
 * @Component({
 *   template: `
 *     <si-form-item label="IPv4 address">
 *       <input type="text" class="form-control" siIpV4 />
 *     </si-form-item>
 *   `,
 *   imports: [SiFormItemComponent, SiIp4InputDirective, ...]
 * })
 * ```
 */
class SiIp4InputDirective extends SiIpInputDirective {
    validate(control) {
        return this.cidr() ? ipV4CIDRValidator(control) : ipV4Validator(control);
    }
    /**
     * Trim leading zeros from each part of the IPv4 address
     */
    leaveInput() {
        const trimmedValue = this.value
            ?.split('.')
            .map(part => part.replace(/^0+(\d)/, '$1'))
            .join('.');
        if (this.value !== trimmedValue) {
            this.renderer.setProperty(this.inputEl, 'value', trimmedValue);
            this.onChange(trimmedValue);
        }
    }
    /** @internal */
    maskInput(e) {
        const { value, pos, type } = e;
        const ipv4 = splitIpV4Sections({ type, input: value, pos, cidr: this.cidr() });
        this.renderer.setProperty(this.inputEl, 'value', ipv4.value);
        const el = this.elementRef.nativeElement;
        if (value?.length === pos) {
            el.setSelectionRange(ipv4.value.length, ipv4.value.length);
        }
        else {
            const newPos = pos + ipv4.cursorDelta;
            el.setSelectionRange(newPos, newPos);
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiIp4InputDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.8", type: SiIp4InputDirective, isStandalone: true, selector: "input[siIpV4]", providers: [
            {
                provide: NG_VALUE_ACCESSOR,
                useExisting: SiIp4InputDirective,
                multi: true
            },
            {
                provide: NG_VALIDATORS,
                useExisting: SiIp4InputDirective,
                multi: true
            }
        ], exportAs: ["siIpV4"], usesInheritance: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiIp4InputDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'input[siIpV4]',
                    providers: [
                        {
                            provide: NG_VALUE_ACCESSOR,
                            useExisting: SiIp4InputDirective,
                            multi: true
                        },
                        {
                            provide: NG_VALIDATORS,
                            useExisting: SiIp4InputDirective,
                            multi: true
                        }
                    ],
                    exportAs: 'siIpV4'
                }]
        }] });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */
/**
 * Directive for IPv6 address input fields.
 *
 * Usage:
 *
 * ```ts
 * import { SiFormItemComponent } from '@siemens/element-ng/form';
 * import { SiIp6InputDirective } from '@siemens/element-ng/ip-input';
 *
 * @Component({
 *   template: `
 *     <si-form-item label="IPv6 address">
 *       <input type="text" class="form-control" siIpV6 />
 *     </si-form-item>
 *   `,
 *   imports: [SiFormItemComponent, SiIp6InputDirective, ...]
 * })
 * ```
 */
class SiIp6InputDirective extends SiIpInputDirective {
    validatorFn = computed(() => ipV6Validator({ zeroCompression: true, cidr: this.cidr() }), ...(ngDevMode ? [{ debugName: "validatorFn" }] : []));
    validate(control) {
        return this.validatorFn()(control);
    }
    maskInput(e) {
        const { value, pos, type } = e;
        if (!value) {
            this.renderer.setProperty(this.inputEl, 'value', '');
            return;
        }
        const ipv6 = splitIpV6Sections({
            type,
            input: value,
            pos,
            zeroCompression: true,
            cidr: this.cidr()
        });
        this.renderer.setProperty(this.inputEl, 'value', ipv6.value);
        if (type === 'insert') {
            const el = this.elementRef.nativeElement;
            if (value?.length === pos) {
                el.setSelectionRange(ipv6.value.length, ipv6.value.length);
            }
            else {
                const newPos = pos + ipv6.cursorDelta;
                el.setSelectionRange(newPos, newPos);
            }
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiIp6InputDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
    static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.8", type: SiIp6InputDirective, isStandalone: true, selector: "input[siIpV6]", providers: [
            {
                provide: NG_VALUE_ACCESSOR,
                useExisting: SiIp6InputDirective,
                multi: true
            },
            {
                provide: NG_VALIDATORS,
                useExisting: SiIp6InputDirective,
                multi: true
            }
        ], exportAs: ["siIpV6"], usesInheritance: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiIp6InputDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: 'input[siIpV6]',
                    providers: [
                        {
                            provide: NG_VALUE_ACCESSOR,
                            useExisting: SiIp6InputDirective,
                            multi: true
                        },
                        {
                            provide: NG_VALIDATORS,
                            useExisting: SiIp6InputDirective,
                            multi: true
                        }
                    ],
                    exportAs: 'siIpV6'
                }]
        }] });

/**
 * Copyright (c) Siemens 2016 - 2026
 * SPDX-License-Identifier: MIT
 */

/**
 * Generated bundle index. Do not edit.
 */

export { SiIp4InputDirective, SiIp6InputDirective, ipV4CIDRValidator, ipV4Validator, ipV6Validator };
//# sourceMappingURL=siemens-element-ng-ip-input.mjs.map
