import { __awaiter } from "tslib";
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
     */
    ngOnChanges(changes) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
        return __awaiter(this, void 0, void 0, function* () {
            console.log('ngOnChanges', (_a = this.componentMetadata) === null || _a === void 0 ? void 0 : _a.id, changes);
            (_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug('LayoutComponentRendererSADirective: rendering component {{@id}}', { id: (_c = this.componentMetadata) === null || _c === void 0 ? void 0 : _c.id });
            this.ngOnDestroy();
            this.viewContainerRef.clear();
            // component metadata is present
            if (nameof('componentMetadata') in changes && this.componentMetadata) {
                const injector = (_d = this.customInjector) !== null && _d !== void 0 ? _d : 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 = yield this.loader.loadItem(componentMetadata);
                if (!layoutComponentType) {
                    (_e = this.logger) === null || _e === void 0 ? void 0 : _e.warn('LayoutComponentRendererSADirective: Unable to find layout component type {{@type}}', { type: { name: componentMetadata.name, package: componentMetadata.package } });
                    switch ((_f = this.options) === null || _f === void 0 ? void 0 : _f.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: (_g = scopeId !== null && scopeId !== void 0 ? scopeId : this.scopeId) !== null && _g !== void 0 ? _g : null,
                        },
                        {
                            provide: LAYOUT_COMPONENT_CHILD_EXTENSIONS,
                            useValue: layoutComponentType.childExtensions,
                        }
                    ]
                });
                this.ɵComponentRef = this.viewContainerRef.createComponent(layoutComponentType.data, {
                    injector: usedInjector,
                });
                (_h = this.logger) === null || _h === void 0 ? void 0 : _h.debug('LayoutComponentRendererSADirective: component rendered {{@id}}', { id: componentMetadata === null || componentMetadata === void 0 ? void 0 : componentMetadata.id });
                const component = this.component;
                if (component) {
                    //registers extensions and child extensions
                    component.registerExtensions([
                        ...(_k = (_j = this.childExtensions) === null || _j === void 0 ? void 0 : _j.map(itm => new itm(componentMetadata))) !== null && _k !== void 0 ? _k : [],
                        ...(_m = (_l = layoutComponentType === null || layoutComponentType === void 0 ? void 0 : layoutComponentType.extensions) === null || _l === void 0 ? void 0 : _l.map(itm => new itm(componentMetadata))) !== null && _m !== void 0 ? _m : [],
                    ]);
                    const changes = {};
                    addSimpleChange(changes, 'options', componentMetadata.options, component.options, true);
                    (_o = this.logger) === null || _o === void 0 ? void 0 : _o.debug('LayoutComponentRendererSADirective: setting options for component {{@id}}', { id: componentMetadata === null || componentMetadata === void 0 ? void 0 : componentMetadata.id });
                    component.options = componentMetadata.options;
                    (_p = this.logger) === null || _p === void 0 ? void 0 : _p.debug('LayoutComponentRendererSADirective: setting changes for component {{@id}}', { id: componentMetadata === null || componentMetadata === void 0 ? void 0 : componentMetadata.id });
                    yield ((_q = component.ngOnChanges) === null || _q === void 0 ? void 0 : _q.call(component, changes));
                    (_r = this.logger) === null || _r === void 0 ? void 0 : _r.debug('LayoutComponentRendererSADirective: initializing component {{@id}}', { id: componentMetadata === null || componentMetadata === void 0 ? void 0 : componentMetadata.id });
                    yield ((_s = component.ngOnInit) === null || _s === void 0 ? void 0 : _s.call(component));
                    (_t = this.logger) === null || _t === void 0 ? void 0 : _t.debug('LayoutComponentRendererSADirective: invalidating component visuals {{@id}}', { id: componentMetadata === null || componentMetadata === void 0 ? void 0 : componentMetadata.id });
                    component.invalidateVisuals();
                    (_u = this.ɵComponentRef) === null || _u === void 0 ? void 0 : _u.changeDetectorRef.markForCheck();
                    this.componentElementChange.next((_x = (_w = (_v = this.ɵComponentRef) === null || _v === void 0 ? void 0 : _v.hostView) === null || _w === void 0 ? void 0 : _w.rootNodes) === null || _x === void 0 ? void 0 : _x[0]);
                    this.componentChange.next(this.ɵComponentRef);
                }
            }
        });
    }
    //######################### public methods - implementation of OnDestroy #########################
    /**
     * Called when component is destroyed
     */
    ngOnDestroy() {
        var _a, _b, _c;
        if (this.ɵComponentRef) {
            (_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug('LayoutComponentRendererSADirective: destroying component {{@id}}', { id: (_b = this.componentMetadata) === null || _b === void 0 ? void 0 : _b.id, designer: this.disableTransformer });
            (_c = this.ɵComponentRef) === null || _c === void 0 ? void 0 : _c.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