import { HostListener, ViewChildren, QueryList, ChangeDetectorRef, ElementRef, Directive, inject } from '@angular/core';
import { nameof } from '@jscrpt/common';
import { Subject } from 'rxjs';
import { RelationNodeOutputSAComponent } from './relationsNodeOutput/relationsNodeOutput.component';
import { RelationNodeInputSAComponent } from './relationsNodeInput/relationsNodeInput.component';
import { RELATIONS_HISTORY_MANAGER } from '../misc/tokens';
import { RelationsCanvasSAComponent } from './relationsCanvas/relationsCanvas.component';
import * as i0 from "@angular/core";
/**
 * Base class for relations node components
 */
export class RelationsNodeBase {
    /**
     * @inheritdoc
     */
    get id() {
        return this.metadata?.id ?? '';
    }
    /**
     * @inheritdoc
     */
    get allOutputs() {
        return this.ɵAllOutputs;
    }
    /**
     * @inheritdoc
     */
    get inputs() {
        return this.ɵInputs;
    }
    /**
     * @inheritdoc
     */
    get outputs() {
        return this.ɵOutputs;
    }
    /**
     * @inheritdoc
     */
    get destroy() {
        return this.destroySubject.asObservable();
    }
    /**
     * Node bounding box
     */
    get boundingBox() {
        return this.element.nativeElement.getBoundingClientRect();
    }
    //######################### constructor #########################
    constructor(changeDetector, element) {
        this.changeDetector = changeDetector;
        this.element = element;
        //######################### protected properties #########################
        this.canvas = inject(RelationsCanvasSAComponent);
        /**
         * Canvas position change subscription
         */
        this.canvasPositionChangeSubscription = null;
        /**
         * Metadata history manager
         */
        this.history = inject(RELATIONS_HISTORY_MANAGER);
        /**
         * Indication whether is node initialized
         */
        this.initialized = false;
        /**
         * Indication whether user is dragging
         */
        this.isDragging = false;
        /**
         * Indication whether user moved node during drag
         */
        this.moved = false;
        /**
         * Last mouse down position
         */
        this.lastMouseMovePosition = {
            x: 0,
            y: 0
        };
        /**
         * Last mouse down offset position
         */
        this.lastMouseDownOffset = {
            x: 0,
            y: 0
        };
        /**
         * Node position
         */
        this.nodePosition = {
            x: 0,
            y: 0,
        };
        /**
         * Array of all available outputs
         */
        this.ɵAllOutputs = [];
        /**
         * Object storing inputs by their names
         */
        this.ɵInputs = {};
        /**
         * Object storing outputs by their names
         */
        this.ɵOutputs = {};
        /**
         * Subject used for destroying node by user
         */
        this.destroySubject = new Subject();
        //######################### public properties - implementation of RelationsNode #########################
        /**
         * @inheritdoc
         */
        this.zoomLevel = 1;
        this.element.nativeElement.classList.add('relations-node');
        this.updatePosition();
        this.observer = new ResizeObserver(() => this.updateRelations());
        this.observer.observe(this.element.nativeElement);
    }
    //######################### public methods - implementation of OnChanges #########################
    /**
     * Called when input value changes
     */
    ngOnChanges(changes) {
        //initial change of metadata
        if (nameof('metadata') in changes && this.metadata) {
            if (this.metadata.nodeMetadata?.coordinates) {
                this.nodePosition = this.metadata.nodeMetadata.coordinates;
                this.updatePosition();
            }
            this.metadataSet();
        }
    }
    //######################### public methods - implementation of OnDestroy #########################
    /**
     * Called when component is destroyed
     */
    ngOnDestroy() {
        this.observer?.disconnect();
        this.canvasPositionChangeSubscription?.unsubscribe();
        this.canvasPositionChangeSubscription = null;
    }
    //######################### public methods - implementation of RelationsNode #########################
    /**
     * @inheritdoc
     */
    invalidateVisuals() {
        this.changeDetector.detectChanges();
        if (!this.initialized) {
            this.initialized = true;
            this.initEndpoints();
        }
    }
    /**
     * @inheritdoc
     */
    initialize() {
    }
    //######################### protected methods methods - host listeners #########################
    /**
     * Mouse down event
     * @param event
     */
    onMouseDown(event) {
        this.moved = false;
        this.isDragging = true;
        this.lastMouseDownOffset =
            {
                x: event.clientX - this.boundingBox.left,
                y: event.clientY - this.boundingBox.top
            };
        this.canvasPositionChangeSubscription = this.canvas.convasPositionChanged.subscribe(() => {
            this.nodePosition = this.canvas.getPositionInCanvas({
                x: this.lastMouseMovePosition.x - this.lastMouseDownOffset.x,
                y: this.lastMouseMovePosition.y - this.lastMouseDownOffset.y,
            });
            this.invalidateVisuals();
            this.updatePosition();
            this.updateRelations();
        });
        event.stopImmediatePropagation();
    }
    /**
     * Mouse move event
     * @param event
     */
    onMouseMove(event) {
        if (this.isDragging) {
            this.moved = true;
            this.lastMouseMovePosition =
                {
                    x: event.clientX,
                    y: event.clientY
                };
            this.nodePosition = this.canvas.getPositionInCanvas({
                x: this.lastMouseMovePosition.x - this.lastMouseDownOffset.x,
                y: this.lastMouseMovePosition.y - this.lastMouseDownOffset.y,
            });
            this.updatePosition();
            event.stopImmediatePropagation();
            event.preventDefault();
            this.updateRelations();
        }
    }
    /**
     * Mouse up event
     * @param event
     */
    onMouseUp(event) {
        if (this.isDragging) {
            if (this.moved) {
                this.history.getNewState();
            }
            this.isDragging = false;
            this.canvasPositionChangeSubscription?.unsubscribe();
            this.canvasPositionChangeSubscription = null;
            event.stopImmediatePropagation();
            event.preventDefault();
        }
    }
    //######################### protected methods #########################
    /**
     * Initialize endpoints
     */
    initEndpoints() {
        const updateInputs = () => {
            this.ɵInputs = {};
            this.inputsChildren?.forEach(input => {
                if (input.name) {
                    this.ɵInputs[input.name] = input;
                }
            });
            Object.freeze(this.ɵInputs);
        };
        const updateOutputs = () => {
            this.ɵAllOutputs = this.outputsChildren.toArray();
            this.ɵOutputs = {};
            this.outputsChildren?.forEach(output => {
                if (output.name) {
                    this.ɵOutputs[output.name] = output;
                }
            });
            Object.freeze(this.ɵOutputs);
        };
        this.inputsChildren?.changes.subscribe(() => updateInputs());
        this.outputsChildren?.changes.subscribe(() => updateOutputs());
        updateInputs();
        updateOutputs();
    }
    /**
     * Updates node relations
     */
    updateRelations() {
        this.inputsChildren.forEach(input => {
            input.updateRelation();
        });
        this.outputsChildren.forEach(output => {
            output.updateRelation();
        });
    }
    /**
     * Updates node position
     */
    updatePosition() {
        this.element.nativeElement.style.left = `${this.nodePosition.x}px`;
        this.element.nativeElement.style.top = `${this.nodePosition.y}px`;
        if (this.metadata?.nodeMetadata?.coordinates) {
            this.metadata.nodeMetadata.coordinates.x = this.nodePosition.x;
            this.metadata.nodeMetadata.coordinates.y = this.nodePosition.y;
        }
    }
    /**
     * Allows code to be called after metadata were set
     */
    metadataSet() {
    }
}
RelationsNodeBase.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: RelationsNodeBase, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
RelationsNodeBase.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.4", type: RelationsNodeBase, host: { listeners: { "mousedown": "onMouseDown($event)", "window:mousemove": "onMouseMove($event)", "window:mouseup": "onMouseUp($event)" } }, viewQueries: [{ propertyName: "inputsChildren", predicate: RelationNodeInputSAComponent, descendants: true }, { propertyName: "outputsChildren", predicate: RelationNodeOutputSAComponent, descendants: true }], usesOnChanges: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: RelationsNodeBase, decorators: [{
            type: Directive
        }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }], propDecorators: { inputsChildren: [{
                type: ViewChildren,
                args: [RelationNodeInputSAComponent]
            }], outputsChildren: [{
                type: ViewChildren,
                args: [RelationNodeOutputSAComponent]
            }], onMouseDown: [{
                type: HostListener,
                args: ['mousedown', ['$event']]
            }], onMouseMove: [{
                type: HostListener,
                args: ['window:mousemove', ['$event']]
            }], onMouseUp: [{
                type: HostListener,
                args: ['window:mouseup', ['$event']]
            }] } });
//# sourceMappingURL=relationsNodeBase.js.map