var _a;
import { UmbShortcutController } from '../../shortcut/context/shortcut.controller.js';
import { UMB_VIEW_CONTEXT } from './view.context-token.js';
import { UmbClassState, UmbStringState, mergeObservables } from '../../../../libs/observable-api/index.js';
import { UmbControllerBase } from '../../../../libs/class-api/index.js';
import { UmbHintController } from '../../hint/index.js';
import { UmbLocalizationController } from '../../../../libs/localization-api/index.js';
/**
 *
 * The View Context handles the aspects of three Features:
 * Browser Titles — Provide a title for this view and it will be set or joint with parent views depending on the inheritance setting.
 * Hints — Holds Hints for this view, depending on the inheritance setting it will propagate the hints to be displayed at parent views.
 * Shortcuts — Not implemented yet.
 *
 */
export class UmbViewController extends UmbControllerBase {
    //
    static #ActiveView;
    //
    #attached;
    #providerCtrl;
    #consumeParentCtrl;
    #currentProvideHost;
    #localize;
    // State used to know if the context can be auto activated when attached.
    #autoActivate;
    #active;
    get isActive() {
        return this.#active;
    }
    #setActive() {
        this.#active = true;
        if (this.#inherit) {
            // Secure the parent in the inheritance chain is active.
            this.#parentView?._internal_activate();
        }
        else {
            // This is for a single, or top level of the inheritance chain, so we can disable the previous active view.
            if (_a.#ActiveView && _a.#ActiveView !== this) {
                _a.#ActiveView._internal_deactivate();
                _a.#ActiveView = undefined;
            }
            _a.#ActiveView = this;
        }
    }
    #removeActive() {
        this.#active = false;
        if (!this.#inherit) {
            if (_a.#ActiveView === this) {
                _a.#ActiveView = undefined;
            }
        }
    }
    #inherit;
    #explicitInheritance;
    #parentView;
    #title;
    #computedTitle;
    #variantId;
    constructor(host, viewAlias) {
        super(host);
        //
        this.#attached = false;
        this.#localize = new UmbLocalizationController(this);
        // State used to know if the context can be auto activated when attached.
        this.#autoActivate = true;
        this.#active = false;
        this.#inherit = false;
        this.#computedTitle = new UmbStringState(undefined);
        this.computedTitle = this.#computedTitle.asObservable();
        this.#variantId = new UmbClassState(undefined);
        this.variantId = this.#variantId.asObservable();
        this.shortcuts = new UmbShortcutController(this);
        this.#children = [];
        this.viewAlias = viewAlias;
        this.hints = new UmbHintController(this, {
            viewAlias: viewAlias,
        });
        this.firstHintOfVariant = mergeObservables([this.variantId, this.hints.hints], ([variantId, hints]) => {
            // Notice, because we in UI have invariant fields on Variants, then we will accept invariant hints on variants.
            if (variantId) {
                return hints.find((hint) => hint.variantId ? hint.variantId.equal(variantId) || hint.variantId.isInvariant() : true);
            }
            else {
                return hints[0];
            }
        });
        this.#consumeParentCtrl = this.consumeContext(UMB_VIEW_CONTEXT, (parentView) => {
            // In case of explicit inheritance we do not want to overview the parent view.
            if (this.#explicitInheritance)
                return;
            if (parentView) {
                this.#setParentView(parentView);
            }
            // only activate if we had an incoming parentView, cause if not we are in a disassembling state. [NL]
            if (parentView && this.#attached && this.#autoActivate) {
                this._internal_requestActivate();
            }
        }).skipHost();
    }
    #setParentView(view) {
        if (this.#parentView === view)
            return;
        this.#parentView = view;
        if (this.#inherit) {
            this.#inheritFromParent();
        }
    }
    setVariantId(variantId) {
        this.#variantId.setValue(variantId);
        this.hints.updateScaffold({ variantId: variantId });
    }
    setTitle(title) {
        if (this.#title === title)
            return;
        this.#title = title;
        this.#computeTitle();
        this.#updateTitle();
    }
    provideAt(controllerHost) {
        if (this.#currentProvideHost === controllerHost)
            return;
        this.unprovide();
        this.#autoActivate = true;
        this.#currentProvideHost = controllerHost;
        this.#providerCtrl = controllerHost.provideContext(UMB_VIEW_CONTEXT, this);
        this.hints.provideAt(controllerHost);
        this.shortcuts.provideAt(controllerHost);
        if (this.#attached) {
            this._internal_requestActivate();
        }
    }
    unprovide() {
        if (this.#providerCtrl) {
            this.#providerCtrl.destroy();
            this.#providerCtrl = undefined;
        }
        this.hints.unprovide();
        this.shortcuts.unprovide();
        this._internal_deactivate();
        this.#requestActivateParent();
    }
    hostConnected() {
        const wasActive = this.isActive;
        const wasAttached = this.#attached;
        this.#attached = true;
        super.hostConnected();
        if (!wasAttached) {
            this.#parentView?._internal_addChild(this);
        }
        // Check that we have a providerController, otherwise this is not provided. [NL]
        if (this.#autoActivate && !wasActive) {
            this._internal_requestActivate();
        }
    }
    hostDisconnected() {
        const wasAttached = this.#attached;
        this.#attached = false;
        if (wasAttached) {
            this.#parentView?._internal_removeChild(this);
        }
        this._internal_deactivate();
        super.hostDisconnected();
        this.#autoActivate = true;
        this.#requestActivateParent();
    }
    isInheriting() {
        return this.#inherit;
    }
    inherit() {
        this.#inherit = true;
    }
    inheritFrom(context) {
        this.#inherit = true;
        this.#explicitInheritance = true;
        this.#consumeParentCtrl?.destroy();
        this.#consumeParentCtrl = undefined;
        this.#setParentView(context);
    }
    #inheritFromParent() {
        this.observe(this.#parentView?.variantId, (variantId) => {
            this.setVariantId(variantId);
        }, 'observeParentVariantId');
        this.observe(this.#parentView?.computedTitle, () => {
            this.#computeTitle();
            // Check for parent view as it is undefined in a disassembling state and we do not want to update the title in that situation. [NL]
            if (this.#providerCtrl && this.#parentView && this.isActive) {
                this.#updateTitle();
            }
        }, 'observeParentTitle');
        this.hints.inheritFrom(this.#parentView?.hints);
    }
    #requestActivateParent() {
        if (!this.#inherit) {
            if (this.#parentView) {
                this.#parentView._internal_requestActivate();
            }
        }
    }
    /**
     * @internal
     * Notify that a view context has been activated.
     */
    // eslint-disable-next-line @typescript-eslint/naming-convention
    _internal_requestActivate() {
        if (!this.#providerCtrl) {
            // If we are not provided we should not be activated. [NL]
            return false;
        }
        // TODO: Check this one: We do not want a parent to auto activate if a child is having the activation. [NL], well maybe it not that bad because of the asking of the children...
        this.#autoActivate = true;
        if (this.isActive) {
            return true;
        }
        // If not attached then propagate the activation to the parent. [NL]
        if (this.#attached === false) {
            if (!this.#parentView) {
                throw new Error('Cannot activate a view that is not attached to the DOM.');
            }
        }
        else {
            // Check if any of the children likes to be activated instead:
            // A reverse loop ensures latest added child gets first chance to activate. This may matter in some future issue-scenario, I will say it could be that it is not the right way to determine if multiple children wants to be active. [NL]
            let i = this.#children.length;
            while (i--) {
                const child = this.#children[i];
                if (child._internal_requestActivate()) {
                    // If we have an active child we should not update the title.
                    return true;
                }
            }
            // if not then check your self:
            if (this.#autoActivate && this.#attached) {
                this._internal_activate();
                return true;
            }
        }
        return false;
    }
    /**
     * @internal
     * Notify that a view context has been activated.
     */
    // eslint-disable-next-line @typescript-eslint/naming-convention
    _internal_activate() {
        if (this.#attached) {
            this.#autoActivate = true;
            this.#setActive();
            this.#updateTitle();
            this.shortcuts.activate();
        }
    }
    /**
     * @internal
     * Deactivate the view context.
     * We cannot conclude that this means the parent should be activated, it can be because of a child being activated.
     */
    // eslint-disable-next-line @typescript-eslint/naming-convention
    _internal_deactivate() {
        if (!this.isActive)
            return;
        this.#autoActivate = false;
        // Deactive children:
        this.#children.forEach((child) => {
            if (child.isInheriting()) {
                child._internal_deactivate();
            }
        });
        this.shortcuts.deactivate();
        this.#removeActive();
    }
    #updateTitle() {
        if (!this.#active || this.#hasActiveChildren()) {
            return;
        }
        const localTitle = this.getComputedTitle();
        if (!localTitle) {
            return;
        }
        document.title = localTitle + ' | Umbraco';
    }
    #computeTitle() {
        const titles = [];
        if (this.#inherit && this.#parentView) {
            titles.push(this.#parentView.getComputedTitle());
        }
        if (this.#title) {
            titles.push(this.#localize.string(this.#title));
        }
        this.#computedTitle.setValue(titles.length > 0 ? titles.join(' | ') : undefined);
    }
    getComputedTitle() {
        return this.#computedTitle.getValue();
    }
    #children;
    // eslint-disable-next-line @typescript-eslint/naming-convention
    _internal_addChild(child) {
        this.#children.push(child);
        if (this.isActive) {
            child._internal_activate();
        }
    }
    // eslint-disable-next-line @typescript-eslint/naming-convention
    _internal_removeChild(child) {
        const index = this.#children.indexOf(child);
        if (index !== -1) {
            this.#children.splice(index, 1);
        }
        // update title?
        if (!this.#hasActiveChildren()) {
            if (this.#active) {
                this.#updateTitle();
            }
            else if (this.#providerCtrl && this.#attached) {
                // If we're not active but should be (no active children, attached, and provided),
                // reactivate to restore the title
                this._internal_activate();
            }
        }
    }
    #hasActiveChildren() {
        return this.#children.some((child) => child.isActive);
    }
    destroy() {
        this.#inherit = false;
        this.#removeActive();
        this.#autoActivate = false;
        this.provideAt = undefined;
        this.unprovide();
        super.destroy();
        this.#consumeParentCtrl = undefined;
    }
}
_a = UmbViewController;
