import * as i0 from '@angular/core';
import { Injectable, NgZone, ApplicationRef, InjectionToken } from '@angular/core';
import { Subject, merge, from } from 'rxjs';
import { filter, map, take } from 'rxjs/operators';
import 'miniprogram-api-typings';

class AgentNode {
    constructor(type) {
        this.type = type;
        this.attribute = {};
        this.style = {};
        this.property = {};
        this.classList = new Set();
        this.children = [];
        this.listener = {};
    }
    appendChild(child) {
        const lastChildIndex = this.children.length - 1;
        this.children.push(child);
        child.parent = this;
        if (lastChildIndex > -1) {
            this.children[lastChildIndex].nextSibling = child;
        }
    }
    setParent(parent) {
        const oldParent = this.parent;
        if (oldParent) {
            const index = oldParent.children.findIndex((item) => item === this);
            if (index === -1) {
                throw new Error('没有在之前的父级上找到该节点' + this);
            }
            oldParent.children.splice(index, 1);
        }
        parent.appendChild(this);
    }
    insertBefore(newChild, refChild) {
        const refIndex = this.children.findIndex((item) => item === refChild);
        if (refIndex === -1) {
            throw new Error('未找到引用子节点' + refChild);
        }
        if (refIndex === 0) {
            newChild.nextSibling = refChild;
        }
        else {
            this.children[refIndex - 1].nextSibling = newChild;
            newChild.nextSibling = refChild;
        }
        this.children.splice(refIndex, 0, newChild);
    }
    removeChild(child) {
        const index = this.children.findIndex((item) => item === child);
        if (index === 0) {
            this.children.shift();
        }
        else if (index + 1 === this.children.length) {
            this.children[index - 1].nextSibling = undefined;
            this.children.pop();
        }
        else {
            this.children[index - 1].nextSibling = this.children[index + 1];
            this.children.splice(index, 1);
        }
        child.nextSibling = undefined;
        child.parent = undefined;
    }
    toView() {
        if (this.type === 'text') {
            return { value: this.value };
        }
        else {
            return {
                class: Array.from(this.classList).join(' ') +
                    (this.attribute.class ? ' ' + this.attribute.class : ''),
                style: Object.entries(this.style)
                    .map(([style, value]) => `${style}:${value}`)
                    .join(';') +
                    (this.attribute.style ? ';' + this.attribute.style : ''),
                property: this.property,
            };
        }
    }
}

class ComponentFinderService {
    constructor() {
        this.map = new Map();
        this.subject = new Subject();
    }
    get(component) {
        return merge(from(this.map), this.subject).pipe(filter((item) => item[0] === component), map((item) => item[1]), take(1));
    }
    set(component, instance) {
        this.subject.next([component, instance]);
        return this.map.set(component, instance);
    }
    remove(component) {
        return this.map.delete(component);
    }
}
ComponentFinderService.ɵfac = function ComponentFinderService_Factory(t) { return new (t || ComponentFinderService)(); };
ComponentFinderService.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: ComponentFinderService, factory: ComponentFinderService.ɵfac });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ComponentFinderService, [{
        type: Injectable
    }], null, null); })();

/* eslint-disable @typescript-eslint/no-explicit-any */
const CLEANUP = 7;
const LVIEW_CONTEXT = 8;
const INJECTOR = 9;
const NEXT = 4;
const CHILD_HEAD = 13;
const VIEW_REFS = 8;
const start = 20;
const initValueMap = new Map();
const linkMap = new Map();
const nodePathMap = new Map();
let index = 0;
const lViewRegistryMap = new WeakMap();
const pageRegistryMap = new Map();
function propertyChange(context) {
    const lView = findCurrentComponentLView(context);
    const lviewPath = getLViewPath(lView);
    const nodeList = lViewToWXView(lView, lviewPath);
    const ctx = {
        nodeList: nodeList,
        nodePath: lviewPath || [],
        hasLoad: true,
    };
    if (linkMap.has(lView)) {
        const ngZone = lView[INJECTOR].get(NgZone);
        ngZone.runOutsideAngular(() => {
            linkMap.get(lView).setData(ctx);
        });
    }
    else {
        initValueMap.set(lView, ctx);
    }
}
function findCurrentComponentLView(context) {
    let lView = context['__ngContext__'];
    if (lView[LVIEW_CONTEXT] === context) {
        return lView;
    }
    lView = lView[CHILD_HEAD];
    while (lView[LVIEW_CONTEXT] !== context) {
        lView = lView[NEXT];
    }
    if (!lView) {
        throw new Error('没有找到LView');
    }
    return lView;
}
function lViewToWXView(lView, parentNodePath = []) {
    const tView = lView[1];
    const end = tView.bindingStartIndex;
    const nodeList = [];
    for (let index = start; index < end; index++) {
        const item = lView[index];
        if (item instanceof AgentNode) {
            nodeList[index - start] = item.toView();
        }
        else if (item && item[1] === true) {
            const lContainerList = [];
            const viewRefList = item[VIEW_REFS] || [];
            viewRefList.forEach((item, itemIndex) => {
                const nodePath = [
                    ...parentNodePath,
                    'directive',
                    index - start,
                    itemIndex,
                ];
                lContainerList.push({
                    __templateName: item._lView[LVIEW_CONTEXT]
                        ? item._lView[LVIEW_CONTEXT].__templateName
                        : undefined,
                    nodeList: lViewToWXView(item._lView, nodePath),
                    nodePath: nodePath,
                    index: lContainerList.length,
                });
            });
            nodeList[index - start] = lContainerList;
        }
        else {
            // todo
            nodeList[index - start] = {};
        }
    }
    return nodeList;
}
function setLViewPath(lView, nodePath) {
    nodePath = nodePath.slice();
    nodePathMap.set(lView, nodePath);
}
function getLViewPath(lView) {
    return nodePathMap.get(lView);
}
function updatePath(context, nodePath) {
    nodePath = nodePath.slice();
    context.nodePath = nodePath;
    const list = [
        ...context.nodeList,
    ];
    while (list.length) {
        const item = list.pop();
        if (item instanceof Array) {
            list.push(...item);
        }
        if (item.nodeList && item.nodeList.length) {
            list.push(...item.nodeList);
        }
        if (item.nodePath) {
            item.nodePath.unshift(...nodePath);
        }
    }
    return context;
}
function getInitValue(lView) {
    const result = initValueMap.get(lView);
    if (result) {
        initValueMap.delete(lView);
    }
    return result;
}
function resolveNodePath(list) {
    list = list.slice();
    let lView = pageRegistryMap.get(list.shift());
    while (list.length) {
        const item = list.shift();
        if (item === 'directive') {
            const index = list.shift();
            const lContainer = lView[index + start];
            const child = list.shift();
            const viewRef = lContainer[VIEW_REFS][child];
            lView = viewRef['_lView'];
        }
        else {
            lView = lView[start + item];
        }
    }
    return lView;
}
function findCurrentElement(lView, list = []) {
    list = [...list];
    while (list.length) {
        const item = list.shift();
        if (item === 'directive') {
            const index = list.shift();
            const lContainer = lView[index + start];
            const child = list.shift();
            const viewRef = lContainer[VIEW_REFS][child];
            lView = viewRef['_lView'];
        }
        else {
            lView = lView[item + start];
        }
    }
    return lView;
}
function lViewLinkToMPComponentRef(ref, lView) {
    linkMap.set(lView, ref);
}
function cleanWhenDestroy(lView, fn) {
    const list = (lView[CLEANUP] = lView[CLEANUP] || []);
    list.push(() => cleanAll(lView));
    list.push(fn);
}
function cleanAll(lview) {
    linkMap.delete(lview);
    nodePathMap.delete(lview);
}
function pageBind(context) {
    const lView = findCurrentComponentLView(context);
    const wxComponentInstance = lView[LVIEW_CONTEXT];
    if (!wxComponentInstance) {
        return;
    }
    const ngZone = lView[INJECTOR].get(NgZone);
    if (!ngZone) {
        throw new Error('没有查询到NgZone');
    }
    ngZone.runOutsideAngular(() => {
        lViewRegistryMap.set(lView, index++);
    });
}
function findPageLView(componentRef) {
    const lView = findCurrentComponentLView(componentRef.instance);
    const id = lViewRegistryMap.get(lView);
    lViewRegistryMap.delete(lView);
    pageRegistryMap.set(id, lView);
    return { lView: lView, id: id };
}
function removePageLViewLink(id) {
    pageRegistryMap.delete(id);
}

class MiniProgramCoreFactory {
    constructor() {
        this.MINIPROGRAM_GLOBAL = wx;
        this.loadApp = (app) => {
            App(app || {});
            const appInstance = getApp();
            return appInstance;
        };
        this.eventPrefixList = [
            { listener: 'bind', prefix: 'bind' },
            { listener: 'catch', prefix: 'catch' },
            { listener: 'mutBind', prefix: 'mut-bind' },
            { listener: 'captureBind', prefix: 'capture-bind' },
            { listener: 'captureCatch', prefix: 'capture-catch' },
        ];
        this.pageStatus = {
            destroy: function () {
                if (this.__ngDestroy) {
                    this.__ngDestroy();
                }
            },
            attachView: function () {
                if (this.__ngComponentInjector && this.__isDetachView) {
                    const applicationRef = this.__ngComponentInjector.get(ApplicationRef);
                    applicationRef.attachView(this.__ngComponentHostView);
                    this.__isDetachView = false;
                }
            },
            detachView: function () {
                if (this.__ngComponentInjector) {
                    this.__isDetachView = true;
                    const applicationRef = this.__ngComponentInjector.get(ApplicationRef);
                    applicationRef.detachView(this.__ngComponentHostView);
                }
            },
        };
        this.pageStartup = (module, component, pageOptions) => {
            const _this = this;
            if (pageOptions?.useComponent) {
                const options = this.getComponentOptions(component) || {};
                const config = {
                    ...options,
                    data: { hasLoad: false },
                    options: { ...options?.options, multipleSlots: true },
                    methods: {
                        ...this.listenerEvent(component),
                        onHide: async function () {
                            if (options.methods?.onHide) {
                                await options.methods.onHide.bind(this)();
                            }
                            _this.pageStatus.detachView.bind(this)();
                        },
                        onUnload: async function () {
                            if (options.methods?.onUnload) {
                                await options.methods.onUnload.bind(this)();
                            }
                            _this.pageStatus.destroy.bind(this)();
                        },
                        onShow: async function () {
                            if (options.methods?.onShow) {
                                await options.methods.onShow.bind(this)();
                            }
                            _this.pageStatus.attachView.bind(this)();
                        },
                    },
                };
                config.lifetimes = config.lifetimes || {};
                const oldCreated = config.lifetimes?.created;
                let componentRef, ngModuleRef;
                let resolveFunction;
                config.lifetimes.created = function () {
                    this.__waitLinkPromise = new Promise((resolve) => {
                        resolveFunction = resolve;
                    });
                    this.__waitLinkResolve = resolveFunction;
                    const app = getApp();
                    const result = app.__ngStartPage(module, component, this);
                    componentRef = result.componentRef;
                    ngModuleRef = result.ngModuleRef;
                    if (oldCreated) {
                        oldCreated.bind(this)();
                    }
                };
                const oldAttached = config.lifetimes.attached;
                config.lifetimes.attached = function () {
                    _this.linkNgComponentWithPage(this, componentRef, ngModuleRef);
                    resolveFunction();
                    if (oldAttached) {
                        oldAttached.bind(this)();
                    }
                };
                return Component(config);
            }
            const options = this.getPageOptions(component) || {};
            return Page({
                ...options,
                ...this.listenerEvent(component),
                data: { hasLoad: false },
                onLoad: function (query) {
                    const app = getApp();
                    const { componentRef, ngModuleRef } = app.__ngStartPage(module, component, this);
                    _this.linkNgComponentWithPage(this, componentRef, ngModuleRef);
                    if (options.onLoad) {
                        return options.onLoad.bind(this)(query);
                    }
                },
                onHide: async function () {
                    if (options.onHide) {
                        await options.onHide.bind(this)();
                    }
                    _this.pageStatus.detachView.bind(this)();
                },
                onUnload: async function () {
                    if (options.onUnload) {
                        await options.onUnload.bind(this)();
                    }
                    _this.pageStatus.destroy.bind(this)();
                },
                onShow: async function () {
                    if (options.onShow) {
                        await options.onShow.bind(this)();
                    }
                    _this.pageStatus.attachView.bind(this)();
                },
            });
        };
        this.componentRegistry = (component) => {
            const options = this.getComponentOptions(component) || {};
            let config = {
                ...options,
                data: { hasLoad: false },
                options: { ...options?.options, multipleSlots: true },
                methods: {
                    ...this.listenerEvent(component),
                },
            };
            config = this.addNgComponentLinkLogic(config);
            return Component(config);
        };
    }
    getListenerEventMapping(prefix, name) {
        return [name, prefix + name];
    }
    linkNgComponentWithPath(mpComponentInstance, list) {
        mpComponentInstance.__isLink = true;
        const currentLView = resolveNodePath(list);
        const injector = currentLView[INJECTOR];
        mpComponentInstance.__lView = currentLView;
        mpComponentInstance.__ngComponentInstance = currentLView[LVIEW_CONTEXT];
        mpComponentInstance.__ngComponentInjector = injector;
        mpComponentInstance.__ngZone = injector.get(NgZone);
        const componentFinderService = injector.get(ComponentFinderService);
        componentFinderService.set(mpComponentInstance.__ngComponentInstance, mpComponentInstance);
        cleanWhenDestroy(currentLView, () => {
            componentFinderService.remove(mpComponentInstance.__ngComponentInstance);
        });
        setLViewPath(currentLView, list);
        lViewLinkToMPComponentRef(mpComponentInstance, currentLView);
        mpComponentInstance.__waitLinkResolve();
        const initValue = getInitValue(currentLView);
        if (initValue) {
            mpComponentInstance.setData(updatePath(initValue, list));
        }
    }
    listenerEvent(component) {
        const _this = this;
        return this.eventPrefixList.reduce((pre, cur) => {
            pre[cur.listener + 'Event'] = function (event) {
                if (this.__lView) {
                    const dataset = event.currentTarget?.dataset || event.target.dataset;
                    const currentPath = [
                        ...(dataset.nodePath || []),
                        dataset.nodeIndex,
                    ];
                    const nodePath = this.__completePath || [];
                    const relativePath = currentPath.slice(nodePath.length);
                    let el = findCurrentElement(this.__lView, relativePath);
                    if (!(el instanceof AgentNode)) {
                        el = el[0];
                        if (!(el instanceof AgentNode)) {
                            throw new Error('查询代理节点失败');
                        }
                    }
                    const eventName = event.type;
                    _this
                        .getListenerEventMapping(cur.prefix, eventName)
                        .forEach((name) => {
                        this.__ngZone.run(() => {
                            if (el.listener[name]) {
                                el.listener[name](event);
                            }
                        });
                    });
                }
                else {
                    throw new Error('未绑定lView');
                }
            };
            return pre;
        }, {});
    }
    linkNgComponentWithPage(mpComponentInstance, componentRef, ngModuleRef) {
        mpComponentInstance.__isLink = true;
        mpComponentInstance.__ngComponentHostView = componentRef.hostView;
        mpComponentInstance.__ngComponentInstance = componentRef.instance;
        mpComponentInstance.__ngComponentInjector = componentRef.injector;
        mpComponentInstance.__ngZone = componentRef.injector.get(NgZone);
        const { lView, id } = findPageLView(componentRef);
        setLViewPath(lView, [id]);
        mpComponentInstance.__completePath = [id];
        const initValue = getInitValue(lView);
        mpComponentInstance.setData(updatePath(initValue, [id]));
        lViewLinkToMPComponentRef(mpComponentInstance, lView);
        mpComponentInstance.__lView = lView;
        mpComponentInstance.__ngDestroy = () => {
            ngModuleRef.destroy();
            componentRef.destroy();
            removePageLViewLink(id);
            cleanAll(lView);
        };
    }
    addNgComponentLinkLogic(config) {
        const oldCreate = config.lifetimes?.created;
        config.lifetimes = config.lifetimes || {};
        config.lifetimes.created = function () {
            let resolveFunction;
            this.__waitLinkPromise = new Promise((resolve) => {
                resolveFunction = resolve;
            });
            this.__waitLinkResolve = resolveFunction;
            if (oldCreate) {
                oldCreate.bind(this)();
            }
        };
        const _this = this;
        config.properties = {
            nodePath: {
                type: null,
                observer: function (list) {
                    if (this.__isLink) {
                        return;
                    }
                    if (typeof list === 'string') {
                        list = JSON.parse(list);
                    }
                    this.__nodePath = list || [];
                    if (typeof this.__nodeIndex !== 'undefined') {
                        this.__completePath = [...this.__nodePath, this.__nodeIndex];
                        _this.linkNgComponentWithPath(this, this.__completePath);
                    }
                },
            },
            nodeIndex: {
                type: null,
                observer: function (index) {
                    if (this.__isLink) {
                        return;
                    }
                    if (typeof index === 'string') {
                        index = parseInt(index, 10);
                    }
                    this.__nodeIndex = index;
                    if (typeof this.__nodePath !== 'undefined') {
                        this.__completePath = [...this.__nodePath, this.__nodeIndex];
                        _this.linkNgComponentWithPath(this, this.__completePath);
                    }
                },
            },
        };
        return config;
    }
    getPageOptions(component) {
        const options = component.mpPageOptions;
        if (options) {
            for (const key in options) {
                if (Object.prototype.hasOwnProperty.call(options, key)) {
                    const element = options[key];
                    if (element instanceof Function) {
                        options[key] = function (...args) {
                            return element.bind(this.__ngComponentInstance)(...args);
                        };
                    }
                }
            }
        }
        return options;
    }
    getComponentOptions(component) {
        const options = component.mpComponentOptions;
        if (options) {
            const pageLifetimes = options.pageLifetimes;
            for (const key in pageLifetimes) {
                if (Object.prototype.hasOwnProperty.call(pageLifetimes, key)) {
                    const element = pageLifetimes[key];
                    if (element instanceof Function) {
                        pageLifetimes[key] = function (...args) {
                            if (this.__isLink) {
                                return element.bind(this.__ngComponentInstance)(...args);
                            }
                            else {
                                return this.__waitLinkPromise.then(() => {
                                    element.bind(this.__ngComponentInstance)(...args);
                                });
                            }
                        };
                    }
                }
            }
            const lifetimes = options.lifetimes;
            for (const key in lifetimes) {
                if (Object.prototype.hasOwnProperty.call(lifetimes, key)) {
                    const element = lifetimes[key];
                    if (element instanceof Function) {
                        lifetimes[key] = function (...args) {
                            if (this.__isLink) {
                                return element.bind(this.__ngComponentInstance)(...args);
                            }
                            else {
                                return this.__waitLinkPromise.then(() => {
                                    element.bind(this.__ngComponentInstance)(...args);
                                });
                            }
                        };
                    }
                }
            }
        }
        return options;
    }
}
const MiniProgramCore = new MiniProgramCoreFactory();

const PAGE_TOKEN = new InjectionToken('PAGE_TOKEN');

class MiniProgramRenderer {
    constructor(element, def) {
        this.element = element;
        this.def = def;
        this.inputRoot = this.element;
        this.data = Object.create(null);
    }
    destroy() { }
    createElement(name, namespace) {
        const element = new AgentNode('element');
        element.name = name;
        element.classList.add(`tag-name-${name}`);
        return element;
    }
    createComment(value) {
        const comment = new AgentNode('comment');
        comment.value = value;
        return comment;
    }
    createText(value) {
        const text = new AgentNode('text');
        text.value = value;
        return text;
    }
    destroyNode() { }
    appendChild(parent, newChild) {
        parent.children.push(newChild);
        newChild.parent = parent;
    }
    insertBefore(parent, newChild, refChild, isMove) {
        if (isMove) {
            // todo 应该没用
        }
        parent.insertBefore(newChild, refChild);
    }
    removeChild(parent, oldChild, isHostElement) {
        if (isHostElement) {
            // todo 应该没用
        }
        parent.removeChild(oldChild);
    }
    selectRootElement(selectorOrNode, preserveContent) {
        const root = new AgentNode('element');
        root.selector = selectorOrNode;
        this.root = root;
        return root;
    }
    parentNode(node) {
        return node.parent;
    }
    nextSibling(node) {
        return node.nextSibling;
    }
    setAttribute(el, name, value, namespace) {
        el.attribute[name] = value;
    }
    removeAttribute(el, name, namespace) {
        delete el.attribute[name];
    }
    addClass(el, name) {
        el.classList.add(name);
    }
    removeClass(el, name) {
        el.classList.delete(name);
    }
    setStyle(el, style, value, flags) {
        el.style[style] = value;
    }
    removeStyle(el, style, flags) {
        delete el.style[style];
    }
    setProperty(el, name, value) {
        el.property[name] = value;
    }
    setValue(node, value) {
        node.value = value;
    }
    listen(target, eventName, callback) {
        if (!(target instanceof AgentNode)) {
            throw new Error('不支持其他类型监听');
        }
        target.listener[eventName] = callback;
        return () => { };
    }
}

class MiniProgramRendererFactory {
    constructor() { }
    createRenderer(element, type) {
        return new MiniProgramRenderer(element, type);
    }
    begin() { }
    end() { }
}
MiniProgramRendererFactory.ɵfac = function MiniProgramRendererFactory_Factory(t) { return new (t || MiniProgramRendererFactory)(); };
MiniProgramRendererFactory.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: MiniProgramRendererFactory, factory: MiniProgramRendererFactory.ɵfac });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MiniProgramRendererFactory, [{
        type: Injectable
    }], function () { return []; }, null); })();

/**
 * Generated bundle index. Do not edit.
 */

;

export { ComponentFinderService, MiniProgramCore, MiniProgramCoreFactory, MiniProgramRenderer, MiniProgramRendererFactory, PAGE_TOKEN, pageBind, propertyChange };
//# sourceMappingURL=angular-miniprogram-platform-default.mjs.map
