import { Directive, EventEmitter, Inject, Injector, Input, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core';
import { LOGGER } from '@anglr/common';
import { addSimpleChange, DynamicItemLoader, SCOPE_ID } from '@anglr/dynamic';
import { nameof } from '@jscrpt/common';
import { LayoutComponentRendererDirectiveOptions } from './layoutComponentRenderer.options';
import { MissingTypeBehavior } from './layoutComponentRenderer.types';
import { NotFoundLayoutTypeSAComponent } from '../../components';
import { LAYOUT_COMPONENTS_LOADER, LAYOUT_COMPONENT_CHILD_EXTENSIONS, LAYOUT_COMPONENT_TRANSFORM } from '../../misc/tokens';
import * as i0 from "@angular/core";
import * as i1 from "./layoutComponentRenderer.options";
import * as i2 from "@anglr/dynamic";
//TODO: refactor input, output names
/**
 * Renders layout component from metadata
 */
export class LayoutComponentRendererSADirective {
    //######################### protected properties #########################
    /**
     * Instance of dynamically created component
     */
    get component() {
        if (!this.ɵComponentRef) {
            return null;
        }
        return this.ɵComponentRef.instance;
    }
    //######################### public properties #########################
    /**
     * Gets component ref of created component or null
     */
    get componentRef() {
        return this.ɵComponentRef;
    }
    //######################### constructor #########################
    constructor(viewContainerRef, loader, childExtensions, scopeId, options, metadataTransformer, logger) {
        this.viewContainerRef = viewContainerRef;
        this.loader = loader;
        this.childExtensions = childExtensions;
        this.scopeId = scopeId;
        this.options = options;
        this.metadataTransformer = metadataTransformer;
        this.logger = logger;
        //######################### protected properties #########################
        /**
         * Created component reference
         */
        this.ɵComponentRef = null;
        //######################### public properties - inputs #########################
        /**
         * Type that should be dynamically created into current container
         */
        this.componentMetadata = null;
        /**
         * Disables component metadata transformer
         */
        this.disableTransformer = false;
        //######################### public properties - outputs #########################
        /**
         * Occurs when rendered component changes
         */
        this.componentChange = new EventEmitter();
        /**
         * Occurs when components element changes
         */
        this.componentElementChange = new EventEmitter();
        if (!this.options || !(this.options instanceof LayoutComponentRendererDirectiveOptions)) {
            this.options = new LayoutComponentRendererDirectiveOptions();
        }
    }
    //######################### public methods - implementation of OnChanges #########################
    /**
     * Called when input value changes
     */
    async ngOnChanges(changes) {
        console.log('ngOnChanges', this.componentMetadata?.id, changes);
        this.logger?.debug('LayoutComponentRendererSADirective: rendering component {{@id}}', { id: this.componentMetadata?.id });
        this.ngOnDestroy();
        this.viewContainerRef.clear();
        // component metadata is present
        if (nameof('componentMetadata') in changes && this.componentMetadata) {
            const injector = this.customInjector ?? this.viewContainerRef.injector;
            let componentMetadata = this.componentMetadata;
            const scopeId = this.componentMetadata.scope;
            if (this.metadataTransformer && !this.disableTransformer) {
                componentMetadata = this.metadataTransformer(this.componentMetadata, injector);
            }
            const layoutComponentType = await this.loader.loadItem(componentMetadata);
            if (!layoutComponentType) {
                this.logger?.warn('LayoutComponentRendererSADirective: Unable to find layout component type {{@type}}', { type: { name: componentMetadata.name, package: componentMetadata.package } });
                switch (this.options?.missingTypeBehavior) {
                    default:
                        //case MissingTypeBehavior.ShowNotFound:
                        {
                            this.viewContainerRef.createComponent(NotFoundLayoutTypeSAComponent);
                            break;
                        }
                    case MissingTypeBehavior.Ignore:
                        {
                            //do nothing
                            break;
                        }
                    case MissingTypeBehavior.ThrowError:
                        {
                            throw new Error(`LayoutComponentRendererSADirective: Unable to find layout component type Name: ${componentMetadata.name} Package: ${componentMetadata.package}`);
                        }
                }
                return;
            }
            const usedInjector = Injector.create({
                parent: injector,
                providers: [
                    {
                        provide: SCOPE_ID,
                        useValue: scopeId ?? this.scopeId ?? null,
                    },
                    {
                        provide: LAYOUT_COMPONENT_CHILD_EXTENSIONS,
                        useValue: layoutComponentType.childExtensions,
                    }
                ]
            });
            this.ɵComponentRef = this.viewContainerRef.createComponent(layoutComponentType.data, {
                injector: usedInjector,
            });
            this.logger?.debug('LayoutComponentRendererSADirective: component rendered {{@id}}', { id: componentMetadata?.id });
            const component = this.component;
            if (component) {
                //registers extensions and child extensions
                component.registerExtensions([
                    ...this.childExtensions?.map(itm => new itm(componentMetadata)) ?? [],
                    ...layoutComponentType?.extensions?.map(itm => new itm(componentMetadata)) ?? [],
                ]);
                const changes = {};
                addSimpleChange(changes, 'options', componentMetadata.options, component.options, true);
                this.logger?.debug('LayoutComponentRendererSADirective: setting options for component {{@id}}', { id: componentMetadata?.id });
                component.options = componentMetadata.options;
                this.logger?.debug('LayoutComponentRendererSADirective: setting changes for component {{@id}}', { id: componentMetadata?.id });
                await component.ngOnChanges?.(changes);
                this.logger?.debug('LayoutComponentRendererSADirective: initializing component {{@id}}', { id: componentMetadata?.id });
                await component.ngOnInit?.();
                this.logger?.debug('LayoutComponentRendererSADirective: invalidating component visuals {{@id}}', { id: componentMetadata?.id });
                component.invalidateVisuals();
                this.ɵComponentRef?.changeDetectorRef.markForCheck();
                this.componentElementChange.next(this.ɵComponentRef?.hostView?.rootNodes?.[0]);
                this.componentChange.next(this.ɵComponentRef);
            }
        }
    }
    //######################### public methods - implementation of OnDestroy #########################
    /**
     * Called when component is destroyed
     */
    ngOnDestroy() {
        if (this.ɵComponentRef) {
            this.logger?.debug('LayoutComponentRendererSADirective: destroying component {{@id}}', { id: this.componentMetadata?.id, designer: this.disableTransformer });
            this.ɵComponentRef?.destroy();
            this.ɵComponentRef = null;
            this.componentChange.next(null);
            this.componentElementChange.next(null);
        }
    }
}
LayoutComponentRendererSADirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: LayoutComponentRendererSADirective, deps: [{ token: i0.ViewContainerRef }, { token: LAYOUT_COMPONENTS_LOADER }, { token: LAYOUT_COMPONENT_CHILD_EXTENSIONS, optional: true, skipSelf: true }, { token: SCOPE_ID, optional: true }, { token: i1.LayoutComponentRendererDirectiveOptions, optional: true }, { token: LAYOUT_COMPONENT_TRANSFORM, optional: true }, { token: LOGGER, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
LayoutComponentRendererSADirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.4", type: LayoutComponentRendererSADirective, isStandalone: true, selector: "[layoutComponentRenderer]", inputs: { componentMetadata: ["layoutComponentRenderer", "componentMetadata"], customInjector: ["layoutComponentRendererInjector", "customInjector"], disableTransformer: ["layoutComponentRendererDisableTransformer", "disableTransformer"] }, outputs: { componentChange: "layoutComponentRendererComponentChange", componentElementChange: "componentElementChange" }, providers: [
        {
            provide: LAYOUT_COMPONENT_CHILD_EXTENSIONS,
            useValue: null,
        }
    ], exportAs: ["layoutComponentRenderer"], usesOnChanges: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.4", ngImport: i0, type: LayoutComponentRendererSADirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[layoutComponentRenderer]',
                    exportAs: 'layoutComponentRenderer',
                    providers: [
                        {
                            provide: LAYOUT_COMPONENT_CHILD_EXTENSIONS,
                            useValue: null,
                        }
                    ],
                    standalone: true
                }]
        }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i2.DynamicItemLoader, decorators: [{
                    type: Inject,
                    args: [LAYOUT_COMPONENTS_LOADER]
                }] }, { type: undefined, decorators: [{
                    type: Inject,
                    args: [LAYOUT_COMPONENT_CHILD_EXTENSIONS]
                }, {
                    type: Optional
                }, {
                    type: SkipSelf
                }] }, { type: undefined, decorators: [{
                    type: Inject,
                    args: [SCOPE_ID]
                }, {
                    type: Optional
                }] }, { type: i1.LayoutComponentRendererDirectiveOptions, decorators: [{
                    type: Optional
                }] }, { type: undefined, decorators: [{
                    type: Inject,
                    args: [LAYOUT_COMPONENT_TRANSFORM]
                }, {
                    type: Optional
                }] }, { type: undefined, decorators: [{
                    type: Inject,
                    args: [LOGGER]
                }, {
                    type: Optional
                }] }], propDecorators: { componentMetadata: [{
                type: Input,
                args: ['layoutComponentRenderer']
            }], customInjector: [{
                type: Input,
                args: ['layoutComponentRendererInjector']
            }], disableTransformer: [{
                type: Input,
                args: ['layoutComponentRendererDisableTransformer']
            }], componentChange: [{
                type: Output,
                args: ['layoutComponentRendererComponentChange']
            }], componentElementChange: [{
                type: Output
            }] } });
//# sourceMappingURL=layoutComponentRenderer.directive.js.map