import { isPlatformServer } from '@angular/common';
import * as i0 from '@angular/core';
import { inject, PLATFORM_ID, DOCUMENT, ElementRef, ChangeDetectorRef, afterNextRender, DestroyRef, numberAttribute, Input, Component, NgModule } from '@angular/core';

/**
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
 */

/**
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
 */
/** Returns the ratio of the device's physical pixel resolution to the css pixel resolution */
function getPixelRatio() {
    return window.devicePixelRatio || 1;
}
function toLowercaseSeparator(key) {
    return key.replace(/([A-Z])/g, '-$1').toLowerCase();
}
function getStyleStr(style) {
    const keys = Object.keys(style);
    const styleCss = keys.map((key) => `${toLowercaseSeparator(key)}: ${style[key]};`);
    return styleCss.join(' ');
}
/** Whether to re-render the watermark */
function reRendering(mutation, watermarkElement) {
    let flag = false;
    // Whether to delete the watermark node
    if (mutation.removedNodes.length) {
        flag = Array.from(mutation.removedNodes).some(node => node === watermarkElement);
    }
    // Whether the watermark dom property value has been modified
    if (mutation.type === 'attributes' && mutation.target === watermarkElement) {
        flag = true;
    }
    return flag;
}
/** Rotate with the watermark as the center point */
function rotateWatermark(ctx, rotateX, rotateY, rotate) {
    ctx.translate(rotateX, rotateY);
    ctx.rotate((Math.PI / 180) * Number(rotate));
    ctx.translate(-rotateX, -rotateY);
}

/**
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
 */
/**
 * Base size of the canvas, 1 for parallel layout and 2 for alternate layout
 * Only alternate layout is currently supported
 */
const BaseSize = 2;
const FontGap = 3;
class NzWatermarkComponent {
    isServer = isPlatformServer(inject(PLATFORM_ID));
    document = inject(DOCUMENT);
    el = inject((ElementRef)).nativeElement;
    cdr = inject(ChangeDetectorRef);
    nzWidth = 120;
    nzHeight = 64;
    nzRotate = -22;
    nzZIndex = 9;
    nzImage = '';
    nzContent = '';
    nzFont = {};
    nzGap = [100, 100];
    nzOffset = [this.nzGap[0] / 2, this.nzGap[1] / 2];
    watermarkElement = this.document.createElement('div');
    stopObservation = false;
    observer = null;
    // for test usage
    onImageLoad;
    onImageError;
    constructor() {
        if (this.isServer) {
            return;
        }
        const observer = (this.observer = new MutationObserver(mutations => {
            if (this.stopObservation) {
                return;
            }
            mutations.forEach(mutation => {
                if (reRendering(mutation, this.watermarkElement)) {
                    this.destroyWatermark();
                    this.renderWatermark();
                }
            });
        }));
        afterNextRender(() => this.renderWatermark());
        inject(DestroyRef).onDestroy(() => observer.disconnect());
    }
    ngOnInit() {
        this.observer?.observe(this.el, {
            subtree: true,
            childList: true,
            attributeFilter: ['style', 'class']
        });
    }
    ngOnChanges(changes) {
        const { nzRotate, nzZIndex, nzWidth, nzHeight, nzImage, nzContent, nzFont, gapX, gapY, offsetLeft, offsetTop } = changes;
        if (nzRotate ||
            nzZIndex ||
            nzWidth ||
            nzHeight ||
            nzImage ||
            nzContent ||
            nzFont ||
            gapX ||
            gapY ||
            offsetLeft ||
            offsetTop) {
            this.renderWatermark();
        }
    }
    getFont() {
        const font = {
            color: 'rgba(0,0,0,.15)',
            fontSize: 16,
            fontWeight: 'normal',
            fontFamily: 'sans-serif',
            fontStyle: 'normal'
        };
        this.nzFont = { ...font, ...this.nzFont };
        this.cdr.markForCheck();
    }
    getMarkStyle() {
        const markStyle = {
            zIndex: this.nzZIndex,
            position: 'absolute',
            left: 0,
            top: 0,
            width: '100%',
            height: '100%',
            pointerEvents: 'none',
            backgroundRepeat: 'repeat',
            visibility: 'visible'
        };
        /** Calculate the style of the nzOffset */
        let positionLeft = (this.nzOffset?.[0] ?? this.nzGap[0] / 2) - this.nzGap[0] / 2;
        let positionTop = (this.nzOffset?.[1] ?? this.nzGap[1] / 2) - this.nzGap[1] / 2;
        if (positionLeft > 0) {
            markStyle.left = `${positionLeft}px`;
            markStyle.width = `calc(100% - ${positionLeft}px)`;
            positionLeft = 0;
        }
        if (positionTop > 0) {
            markStyle.top = `${positionTop}px`;
            markStyle.height = `calc(100% - ${positionTop}px)`;
            positionTop = 0;
        }
        markStyle.backgroundPosition = `${positionLeft}px ${positionTop}px`;
        return markStyle;
    }
    destroyWatermark() {
        if (this.watermarkElement) {
            this.watermarkElement.remove();
        }
    }
    appendWatermark(base64Url, markWidth) {
        this.stopObservation = true;
        this.watermarkElement.setAttribute('style', getStyleStr({
            ...this.getMarkStyle(),
            backgroundImage: `url('${base64Url}')`,
            backgroundSize: `${(this.nzGap[0] + markWidth) * BaseSize}px`
        }));
        this.el.append(this.watermarkElement);
        this.cdr.markForCheck();
        // Delayed execution
        setTimeout(() => {
            this.stopObservation = false;
            this.cdr.markForCheck();
        });
    }
    getMarkSize(ctx) {
        let defaultWidth = 120;
        let defaultHeight = 64;
        if (!this.nzImage && ctx.measureText) {
            ctx.font = `${Number(this.nzFont.fontSize)}px ${this.nzFont.fontFamily}`;
            const contents = Array.isArray(this.nzContent) ? this.nzContent : [this.nzContent];
            const widths = contents.map(item => ctx.measureText(item).width);
            defaultWidth = Math.ceil(Math.max(...widths));
            defaultHeight = Number(this.nzFont.fontSize) * contents.length + (contents.length - 1) * FontGap;
        }
        return [this.nzWidth ?? defaultWidth, this.nzHeight ?? defaultHeight];
    }
    fillTexts(ctx, drawX, drawY, drawWidth, drawHeight) {
        const ratio = getPixelRatio();
        const mergedFontSize = Number(this.nzFont.fontSize) * ratio;
        ctx.font = `${this.nzFont.fontStyle} normal ${this.nzFont.fontWeight} ${mergedFontSize}px/${drawHeight}px ${this.nzFont.fontFamily}`;
        if (this.nzFont.color)
            ctx.fillStyle = this.nzFont.color;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'top';
        ctx.translate(drawWidth / 2, 0);
        const contents = Array.isArray(this.nzContent) ? this.nzContent : [this.nzContent];
        contents?.forEach((item, index) => {
            ctx.fillText(item ?? '', drawX, drawY + index * (mergedFontSize + FontGap * ratio));
        });
    }
    drawText(canvas, ctx, drawX, drawY, drawWidth, drawHeight, alternateRotateX, alternateRotateY, alternateDrawX, alternateDrawY, markWidth) {
        this.fillTexts(ctx, drawX, drawY, drawWidth, drawHeight);
        /** Fill the interleaved text after rotation */
        ctx.restore();
        rotateWatermark(ctx, alternateRotateX, alternateRotateY, this.nzRotate);
        this.fillTexts(ctx, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
        this.appendWatermark(canvas.toDataURL(), markWidth);
    }
    renderWatermark() {
        if (this.isServer) {
            return;
        }
        if (!this.nzContent && !this.nzImage) {
            return;
        }
        const canvas = this.document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        if (ctx) {
            if (!this.watermarkElement) {
                this.watermarkElement = this.document.createElement('div');
            }
            this.getFont();
            const ratio = getPixelRatio();
            const [markWidth, markHeight] = this.getMarkSize(ctx);
            const canvasWidth = (this.nzGap[0] + markWidth) * ratio;
            const canvasHeight = (this.nzGap[1] + markHeight) * ratio;
            canvas.setAttribute('width', `${canvasWidth * BaseSize}px`);
            canvas.setAttribute('height', `${canvasHeight * BaseSize}px`);
            const drawX = (this.nzGap[0] * ratio) / 2;
            const drawY = (this.nzGap[1] * ratio) / 2;
            const drawWidth = markWidth * ratio;
            const drawHeight = markHeight * ratio;
            const rotateX = (drawWidth + this.nzGap[0] * ratio) / 2;
            const rotateY = (drawHeight + this.nzGap[1] * ratio) / 2;
            /** Alternate drawing parameters */
            const alternateDrawX = drawX + canvasWidth;
            const alternateDrawY = drawY + canvasHeight;
            const alternateRotateX = rotateX + canvasWidth;
            const alternateRotateY = rotateY + canvasHeight;
            ctx.save();
            rotateWatermark(ctx, rotateX, rotateY, this.nzRotate);
            if (this.nzImage) {
                const img = new Image();
                this.onImageLoad = () => {
                    cleanup();
                    ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight);
                    /** Draw interleaved pictures after rotation */
                    ctx.restore();
                    rotateWatermark(ctx, alternateRotateX, alternateRotateY, this.nzRotate);
                    ctx.drawImage(img, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
                    this.appendWatermark(canvas.toDataURL(), markWidth);
                };
                this.onImageError = () => {
                    cleanup();
                    this.drawText(canvas, ctx, drawX, drawY, drawWidth, drawHeight, alternateRotateX, alternateRotateY, alternateDrawX, alternateDrawY, markWidth);
                };
                const cleanup = () => {
                    img.removeEventListener('load', this.onImageLoad);
                    img.removeEventListener('error', this.onImageError);
                };
                img.addEventListener('load', this.onImageLoad);
                img.addEventListener('error', this.onImageError);
                img.crossOrigin = 'anonymous';
                img.referrerPolicy = 'no-referrer';
                img.src = this.nzImage;
            }
            else {
                this.drawText(canvas, ctx, drawX, drawY, drawWidth, drawHeight, alternateRotateX, alternateRotateY, alternateDrawX, alternateDrawY, markWidth);
            }
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NzWatermarkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "22.0.6", type: NzWatermarkComponent, isStandalone: true, selector: "nz-watermark", inputs: { nzWidth: ["nzWidth", "nzWidth", numberAttribute], nzHeight: ["nzHeight", "nzHeight", numberAttribute], nzRotate: ["nzRotate", "nzRotate", numberAttribute], nzZIndex: ["nzZIndex", "nzZIndex", numberAttribute], nzImage: "nzImage", nzContent: "nzContent", nzFont: "nzFont", nzGap: "nzGap", nzOffset: "nzOffset" }, exportAs: ["nzWatermark"], usesOnChanges: true, ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{position:relative;display:block;overflow:hidden}\n"] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NzWatermarkComponent, decorators: [{
            type: Component,
            args: [{ selector: 'nz-watermark', exportAs: 'nzWatermark', template: `<ng-content />`, styles: [":host{position:relative;display:block;overflow:hidden}\n"] }]
        }], ctorParameters: () => [], propDecorators: { nzWidth: [{
                type: Input,
                args: [{ transform: numberAttribute }]
            }], nzHeight: [{
                type: Input,
                args: [{ transform: numberAttribute }]
            }], nzRotate: [{
                type: Input,
                args: [{ transform: numberAttribute }]
            }], nzZIndex: [{
                type: Input,
                args: [{ transform: numberAttribute }]
            }], nzImage: [{
                type: Input
            }], nzContent: [{
                type: Input
            }], nzFont: [{
                type: Input
            }], nzGap: [{
                type: Input
            }], nzOffset: [{
                type: Input
            }] } });

/**
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
 */
class NzWatermarkModule {
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NzWatermarkModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
    static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.6", ngImport: i0, type: NzWatermarkModule, imports: [NzWatermarkComponent], exports: [NzWatermarkComponent] });
    static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NzWatermarkModule });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NzWatermarkModule, decorators: [{
            type: NgModule,
            args: [{
                    exports: [NzWatermarkComponent],
                    imports: [NzWatermarkComponent]
                }]
        }] });

/**
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
 */

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

export { NzWatermarkComponent, NzWatermarkModule };
//# sourceMappingURL=ng-zorro-antd-watermark.mjs.map
