import { Component, ChangeDetectionStrategy, HostBinding, HostListener, ViewChild, ElementRef, Input, Inject, ChangeDetectorRef, EventEmitter, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MetadataHistoryManager } from '@anglr/dynamic';
import { select } from 'd3';
import { Subscription } from 'rxjs';
import { RelationsNodeManager } from '../../services';
import { NodeRelationPath } from '../../misc/nodeRelationPath';
import { MouseButton } from '../../misc/enums';
import { clamp } from '../../misc/utils';
import { RelationsNodeRendererSADirective } from '../../directives';
import { RELATIONS_HISTORY_MANAGER } from '../../misc/tokens';
import * as i0 from "@angular/core";
import * as i1 from "../../services";
import * as i2 from "@angular/common";
import * as i3 from "@anglr/dynamic";
/**
 * Default background size in pixels
 */
const DEFAULT_BACKGROUND_SIZE = 16;
/**
 * Minimum scale factor
 */
const SCALE_FACTOR_MIN = 0.2;
/**
 * Maximum scale factor
 */
const SCALE_FACTOR_MAX = 2;
/**
 * Maximal movement delta
 */
const MOVEMENT_DELTA_MAX = 10;
/**
 * Component used as designer component wrapper for relations component
 */
export class RelationsCanvasSAComponent {
    //######################### protected properties - host bindings #########################
    /**
     * Background size css styles
     */
    get backgroundSizeStyle() {
        return `${this.backgroundSize}px ${this.backgroundSize}px`;
    }
    /**
     * Background position css styles
     */
    get _backgroundPositionStyle() {
        return `${this.canvasPosition.x % this.backgroundSize}px ${this.canvasPosition.y % this.backgroundSize}px`;
    }
    //######################### protected properties - getters and setters #########################
    /**
     * Canvas bounding box
     */
    get boundingBox() {
        return this.element.nativeElement.getBoundingClientRect();
    }
    //######################### constructor #########################
    constructor(element, relationManager, _changeDetector, history) {
        this.element = element;
        this.relationManager = relationManager;
        this._changeDetector = _changeDetector;
        this.history = history;
        //######################### private properties #########################
        /**
         * Identifier of canvas move timer
         */
        this._canvasMoveTimer = null;
        //######################### protected properties #########################
        /**
         * Subscriptions created during initialization
         */
        this._initSubscriptions = new Subscription();
        /**
         * Background pattern size
         */
        this.backgroundSize = DEFAULT_BACKGROUND_SIZE;
        /**
         * Drag start position coordinates
         */
        this.dragStartPosition = {
            x: 0,
            y: 0,
        };
        //######################### protected properties - template bindings #########################
        /**
         * Canvas position
         */
        this.canvasPosition = { x: 0, y: 0 };
        /**
         * Mouse position in canvas
         */
        this.mousePosition = { x: 0, y: 0 };
        /**
         * Zoom level
         */
        this.zoomLevel = 1;
        /**
         * Indication whether canvas is being dragged
         */
        this.isDragging = false;
        //######################### public properties - inputs #########################
        /**
         * Node definition data to render
         */
        this.nodeDefinitions = [];
        //######################### public properties - outputs #########################
        this.convasPositionChanged = new EventEmitter();
    }
    //######################### public methods - implementation of OnInit #########################
    /**
     * Initialize component
     */
    ngOnInit() {
        this._initSubscriptions.add(this.relationManager.activeNodeChange.subscribe(() => this.focusNode(this.relationManager.activeNode)));
    }
    //######################### public methods - implementation of OnDestroy #########################
    /**
     * Called when component is destroyed
     */
    ngOnDestroy() {
        this._initSubscriptions.unsubscribe();
    }
    //######################### public methods #########################
    /**
     * Focuses canvas to selected node
     * @param id Relations node identifier
     * @returns
     */
    focusNode(id) {
        var _a, _b, _c, _d;
        if (!id) {
            return;
        }
        const node = (_a = this.nodeDefinitions) === null || _a === void 0 ? void 0 : _a.find(node => node.id === id);
        if ((_b = node === null || node === void 0 ? void 0 : node.nodeMetadata) === null || _b === void 0 ? void 0 : _b.coordinates) {
            // this._setZoomLevel(1);
            this.canvasPosition =
                {
                    x: (-((_c = node === null || node === void 0 ? void 0 : node.nodeMetadata) === null || _c === void 0 ? void 0 : _c.coordinates.x)) * this.zoomLevel + this.boundingBox.width / 2,
                    y: (-((_d = node === null || node === void 0 ? void 0 : node.nodeMetadata) === null || _d === void 0 ? void 0 : _d.coordinates.y)) * this.zoomLevel + this.boundingBox.height / 2,
                };
            this._changeDetector.detectChanges();
        }
    }
    /**
     * Creates node relation path
     */
    createRelation() {
        var _a;
        return new NodeRelationPath(select((_a = this.relationsGroup) === null || _a === void 0 ? void 0 : _a.nativeElement), this.relationManager, this.history, null, null);
    }
    getPositionInCanvas(point) {
        return {
            x: (point.x - this.boundingBox.left - this.canvasPosition.x) / this.zoomLevel,
            y: (point.y - this.boundingBox.top - this.canvasPosition.y) / this.zoomLevel
        };
    }
    //######################### protected methods - host listeners #########################
    /**
     * Mouse down event, handles moving of canvas
     * @param event - Mouse event that occured
     */
    onMouseDown(event) {
        if (event.buttons == MouseButton.LEFT) {
            this.dragStartPosition =
                {
                    x: event.clientX - this.canvasPosition.x,
                    y: event.clientY - this.canvasPosition.y
                };
            this.isDragging = true;
        }
    }
    /**
     * Mouse move event, handles moving of canvas
     * @param event - Mouse event that occured
     */
    onMouseMove(event) {
        this.mousePosition = {
            x: event.clientX - this.boundingBox.left,
            y: event.clientY - this.boundingBox.top
        };
        if (this.isDragging) {
            this.canvasPosition =
                {
                    x: event.clientX - this.dragStartPosition.x,
                    y: event.clientY - this.dragStartPosition.y
                };
        }
        else {
            if ((event === null || event === void 0 ? void 0 : event.buttons) === MouseButton.LEFT) {
                if (!this._canvasMoveTimer) {
                    this._canvasMoveTimer = setInterval(() => {
                        const delta = {
                            x: 0,
                            y: 0,
                        };
                        if (this.mousePosition.x >= this.boundingBox.width * 0.9) {
                            delta.x -= Math.min((this.mousePosition.x - this.boundingBox.width * 0.9) / (this.boundingBox.width / 10) * MOVEMENT_DELTA_MAX, MOVEMENT_DELTA_MAX);
                        }
                        else if (this.mousePosition.x <= this.boundingBox.width / 10) {
                            delta.x += Math.min((this.boundingBox.width / 10 - this.mousePosition.x) / (this.boundingBox.width / 10) * MOVEMENT_DELTA_MAX, MOVEMENT_DELTA_MAX);
                        }
                        if (this.mousePosition.y >= this.boundingBox.height * 0.9) {
                            delta.y -= Math.min((this.mousePosition.y - this.boundingBox.height * 0.9) / (this.boundingBox.height / 10) * MOVEMENT_DELTA_MAX, MOVEMENT_DELTA_MAX);
                        }
                        else if (this.mousePosition.y <= this.boundingBox.height / 10) {
                            delta.y += Math.min((this.boundingBox.height / 10 - this.mousePosition.y) / (this.boundingBox.height / 10) * MOVEMENT_DELTA_MAX, MOVEMENT_DELTA_MAX);
                        }
                        if (delta.x != 0 || delta.y != 0) {
                            this.convasPositionChanged.emit();
                            this.canvasPosition.x += delta.x;
                            this.canvasPosition.y += delta.y;
                            this._changeDetector.detectChanges();
                        }
                    }, 33);
                }
            }
        }
    }
    /**
     * Mouse up event, handles moving of canvas
     * @param event - Mouse event that occured
     */
    onMouseUp(_) {
        if (this.isDragging) {
            this.isDragging = false;
        }
        if (this._canvasMoveTimer) {
            clearInterval(this._canvasMoveTimer);
            this._canvasMoveTimer = null;
        }
    }
    /**
     * Wheel event, used for zooming
     * @param event - Wheel event that occured
     */
    onWheel(event) {
        if (event.deltaY) {
            const newZoomLevel = this._calculateNewZoomLevel(event.deltaY > 1 ? -1 : 1);
            const posX = (event.clientX - this.canvasPosition.x - this.boundingBox.left) / this.zoomLevel;
            const posY = (event.clientY - this.canvasPosition.y - this.boundingBox.top) / this.zoomLevel;
            this.canvasPosition.x = event.clientX - this.boundingBox.left - posX * newZoomLevel;
            this.canvasPosition.y = event.clientY - this.boundingBox.top - posY * newZoomLevel;
            this._setZoomLevel(newZoomLevel);
        }
        event.preventDefault();
        event.stopImmediatePropagation();
    }
    //######################### public methods #########################
    /**
     * Zooms canvas in by one step
     */
    zoomIn() {
        this._setZoomLevel(this._calculateNewZoomLevel(1));
    }
    /**
     * Zooms canvas out by one step
     */
    zoomOut() {
        this._setZoomLevel(this._calculateNewZoomLevel(-1));
    }
    /**
     * Resets zoom level
     */
    resetZoom() {
        this._setZoomLevel(1);
    }
    //######################### protected methods - template bindings #########################
    /**
     * Destroys node
     * @param node - Definition of node to be destroyed
     */
    destroyNode(node) {
        const index = this.nodeDefinitions.indexOf(node);
        if (index < 0) {
            return;
        }
        this.nodeDefinitions.splice(index, 1);
        this.history.getNewState();
    }
    //######################### private methods #########################
    /**
     * Calculates new zoom level based on dalte
     * @param delta
     * @returns
     */
    _calculateNewZoomLevel(delta) {
        return clamp(this.zoomLevel + delta * 0.05, SCALE_FACTOR_MIN, SCALE_FACTOR_MAX);
    }
    /**
     * Sets zoom level
     * @param newZoomLevel new zoom level
     */
    _setZoomLevel(newZoomLevel) {
        this.zoomLevel = newZoomLevel;
        this.backgroundSize = DEFAULT_BACKGROUND_SIZE * this.zoomLevel;
    }
}
RelationsCanvasSAComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: RelationsCanvasSAComponent, deps: [{ token: i0.ElementRef }, { token: i1.RelationsNodeManager }, { token: i0.ChangeDetectorRef }, { token: RELATIONS_HISTORY_MANAGER }], target: i0.ɵɵFactoryTarget.Component });
RelationsCanvasSAComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.0.4", type: RelationsCanvasSAComponent, isStandalone: true, selector: "relations-canvas", inputs: { nodeDefinitions: "nodeDefinitions" }, outputs: { convasPositionChanged: "convasPositionChanged" }, host: { listeners: { "mousedown": "onMouseDown($event)", "mousemove": "onMouseMove($event)", "window:mouseup": "onMouseUp($event)", "wheel": "onWheel($event)" }, properties: { "style.backgroundSize": "this.backgroundSizeStyle", "style.backgroundPosition": "this._backgroundPositionStyle" } }, viewQueries: [{ propertyName: "relationsGroup", first: true, predicate: ["relationsGroup"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"flow-area\" [style.transform]=\"'translate(' + canvasPosition.x + 'px, ' + canvasPosition.y + 'px) scale(' + zoomLevel + ')'\">\n    <svg class=\"relations-wrapper\">\n        <g #relationsGroup></g>\n    </svg>\n\n    <ng-container *ngFor=\"let nodeDefinition of nodeDefinitions\">\n        <ng-container [relationsNodeRenderer]=\"nodeDefinition\" [zoomLevel]=\"zoomLevel\" (destroy)=\"destroyNode($event)\"></ng-container>\n    </ng-container>\n</div>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: RelationsNodeRendererSADirective, selector: "[relationsNodeRenderer]", inputs: ["relationsNodeRenderer", "zoomLevel"], outputs: ["create", "destroy"], exportAs: ["relationsNodeRenderer"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: RelationsCanvasSAComponent, decorators: [{
            type: Component,
            args: [{ selector: 'relations-canvas', standalone: true, imports: [
                        CommonModule,
                        RelationsNodeRendererSADirective,
                    ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flow-area\" [style.transform]=\"'translate(' + canvasPosition.x + 'px, ' + canvasPosition.y + 'px) scale(' + zoomLevel + ')'\">\n    <svg class=\"relations-wrapper\">\n        <g #relationsGroup></g>\n    </svg>\n\n    <ng-container *ngFor=\"let nodeDefinition of nodeDefinitions\">\n        <ng-container [relationsNodeRenderer]=\"nodeDefinition\" [zoomLevel]=\"zoomLevel\" (destroy)=\"destroyNode($event)\"></ng-container>\n    </ng-container>\n</div>" }]
        }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.RelationsNodeManager }, { type: i0.ChangeDetectorRef }, { type: i3.MetadataHistoryManager, decorators: [{
                    type: Inject,
                    args: [RELATIONS_HISTORY_MANAGER]
                }] }], propDecorators: { backgroundSizeStyle: [{
                type: HostBinding,
                args: ['style.backgroundSize']
            }], _backgroundPositionStyle: [{
                type: HostBinding,
                args: ['style.backgroundPosition']
            }], relationsGroup: [{
                type: ViewChild,
                args: ['relationsGroup', { read: ElementRef }]
            }], nodeDefinitions: [{
                type: Input
            }], convasPositionChanged: [{
                type: Output
            }], onMouseDown: [{
                type: HostListener,
                args: ['mousedown', ['$event']]
            }], onMouseMove: [{
                type: HostListener,
                args: ['mousemove', ['$event']]
            }], onMouseUp: [{
                type: HostListener,
                args: ['window:mouseup', ['$event']]
            }], onWheel: [{
                type: HostListener,
                args: ['wheel', ['$event']]
            }] } });
//# sourceMappingURL=relationsCanvas.component.js.map