import * as i0 from '@angular/core';
import { Injectable, NgZone, ApplicationRef, InjectionToken } from '@angular/core';

class AgentNode {
    type;
    selector;
    name;
    parent;
    nextSibling;
    attribute = {};
    style = {};
    property = {};
    classList = new Set();
    value;
    children = [];
    listener = {};
    constructor(type) {
        this.type = type;
    }
    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 {
    map = new Map();
    mapPromise = new Map();
    async get(component) {
        if (this.map.has(component)) {
            return this.map.get(component);
        }
        let fn;
        const promise = new Promise((res) => {
            fn = res;
        });
        this.mapPromise.set(component, fn);
        return promise.then((result) => {
            this.mapPromise.delete(component);
            return result;
        });
    }
    /** @internal */
    set(component, instance) {
        if (this.mapPromise.has(component)) {
            this.mapPromise.get(component)();
        }
        this.map.set(component, instance);
    }
    /** @internal */
    remove(component) {
        return this.map.delete(component);
    }
    static ɵfac = function ComponentFinderService_Factory(t) { return new (t || ComponentFinderService)(); };
    static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: ComponentFinderService, factory: ComponentFinderService.ɵfac });
}
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ComponentFinderService, [{
        type: Injectable
    }], null, null); })();

function _arrayOrObjectItemDiff(count, prefix, fromItem, toItem, changeObject) {
    if (fromItem instanceof Array && toItem instanceof Array) {
        const result = diffDataArray(fromItem, toItem, prefix);
        if (result.allChange || result.object) {
            if (result.allChange) {
                count++;
                changeObject[prefix] = toItem;
            }
            else {
                changeObject = { ...changeObject, ...result.object };
            }
        }
        return { count: count, changeObject };
    }
    else if (typeof fromItem === 'object' &&
        fromItem !== null &&
        typeof toItem === 'object' &&
        toItem !== null) {
        const result = diffDataObject(fromItem, toItem, prefix);
        if (result.allChange || result.object) {
            if (result.allChange) {
                count++;
                changeObject[prefix] = toItem;
            }
            else {
                changeObject = { ...changeObject, ...result.object };
            }
        }
        return { count: count, changeObject };
    }
    else if (fromItem !== toItem) {
        changeObject[prefix] = toItem;
        count++;
        return { count: count, changeObject };
    }
    return { count: count, changeObject };
}
function diffDataObject(from, to, prefix) {
    const toKeyList = Object.keys(to);
    let changeObject = {};
    const point = prefix ? '.' : '';
    let count = 0;
    if (Object.keys(from).length !== toKeyList.length) {
        return { allChange: true };
    }
    for (let index = 0; index < toKeyList.length; index++) {
        const key = toKeyList[index];
        const fromItem = from[key];
        const toItem = to[key];
        const currentPrefix = `${prefix}${point}${key}`;
        const result = _arrayOrObjectItemDiff(count, currentPrefix, fromItem, toItem, changeObject);
        count = result.count;
        changeObject = result.changeObject;
    }
    if (count === toKeyList.length && toKeyList.length !== 0) {
        return { allChange: true };
    }
    return { allChange: false, object: changeObject };
}
function diffDataArray(from, to, prefix) {
    let changeObject = {};
    if (from.length !== to.length) {
        return { allChange: true };
    }
    let count = 0;
    for (let i = 0; i < to.length; i++) {
        const fromItem = from[i];
        const toItem = to[i];
        const currentPrefix = `${prefix}[${i}]`;
        const result = _arrayOrObjectItemDiff(count, currentPrefix, fromItem, toItem, changeObject);
        count = result.count;
        changeObject = result.changeObject;
    }
    if (count === to.length && to.length !== 0) {
        return {
            allChange: true,
        };
    }
    return { allChange: false, object: changeObject };
}
function diffNodeData(from, to) {
    const result = diffDataObject(from, to, '');
    if (result.allChange) {
        return to;
    }
    return result.object;
}

/* eslint-disable @typescript-eslint/no-explicit-any */
// packages\core\src\render3\interfaces\view.ts
const CLEANUP = 7;
const LVIEW_CONTEXT = 8;
const INJECTOR = 9;
// packages\core\src\render3\interfaces\container.ts
const VIEW_REFS = 8;
// HEADER_OFFSET
const start = 25;
const linkMap = new Map();
const nodePathMap = new Map();
let index = 0;
const pageRegistryMap = new Map();
const lViewLastDataMap = new Map();
let waitingRefreshLViewList = [];
/** @internal */
function propertyChange(lView) {
    if (linkMap.has(lView)) {
        const ngZone = lView[INJECTOR].get(NgZone);
        waitingRefreshLViewList.push(() => {
            ngZone.runOutsideAngular(() => {
                const instance = linkMap.get(lView);
                if (!instance) {
                    return;
                }
                const currentData = getPageRefreshContext(lView);
                const diffData = getDiffData(lView, currentData);
                if (Object.keys(diffData).length) {
                    instance.setData(diffData);
                }
            });
        });
    }
}
function endRender() {
    for (const fn of waitingRefreshLViewList) {
        fn();
    }
    waitingRefreshLViewList = [];
}
function getPageRefreshContext(lView) {
    const lviewPath = getLViewPath(lView);
    const nodeList = lViewToWXView(lView, lviewPath);
    const ctx = {
        nodeList: nodeList,
        nodePath: lviewPath || [],
        hasLoad: true,
    };
    return ctx;
}
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 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);
    lViewLastDataMap.delete(lView);
}
function findPageLView(componentRef) {
    const lView = componentRef._rootLView[start];
    index++;
    pageRegistryMap.set(index, lView);
    return { lView: lView, id: index };
}
function removePageLViewLink(id) {
    const lView = pageRegistryMap.get(id);
    lViewLastDataMap.delete(lView);
    pageRegistryMap.delete(id);
}
function getDiffData(lView, currentData) {
    const lastData = lViewLastDataMap.get(lView);
    if (!lastData) {
        lViewLastDataMap.set(lView, currentData);
        return currentData;
    }
    const diff = diffNodeData(lastData, currentData);
    lViewLastDataMap.set(lView, currentData);
    return diff;
}

/// <reference types="miniprogram-api-typings"/>
class MiniProgramCoreFactory {
    MINIPROGRAM_GLOBAL = wx;
    loadApp = (app) => {
        App(app || {});
        const appInstance = getApp();
        appInstance.__ngStartPagePromise = new Promise((resolve) => {
            appInstance.__ngStartPageResolve = resolve;
        });
        return appInstance;
    };
    eventPrefixList = [
        { listener: 'bind', prefix: 'bind' },
        { listener: 'catch', prefix: 'catch' },
        { listener: 'mutBind', prefix: 'mut-bind' },
        { listener: 'captureBind', prefix: 'capture-bind' },
        { listener: 'captureCatch', prefix: 'capture-catch' },
    ];
    getListenerEventMapping(prefix, name) {
        return [name, prefix + name];
    }
    linkNgComponentWithPath(mpComponentInstance, list) {
        mpComponentInstance.__isLink = true;
        const lView = resolveNodePath(list);
        const injector = lView[INJECTOR];
        mpComponentInstance.__lView = lView;
        mpComponentInstance.__ngComponentInstance = lView[LVIEW_CONTEXT];
        mpComponentInstance.__ngComponentInjector = injector;
        const ngZone = injector.get(NgZone);
        mpComponentInstance.__ngZone = ngZone;
        const componentFinderService = injector.get(ComponentFinderService);
        componentFinderService.set(mpComponentInstance.__ngComponentInstance, mpComponentInstance);
        cleanWhenDestroy(lView, () => {
            componentFinderService.remove(mpComponentInstance.__ngComponentInstance);
        });
        setLViewPath(lView, list);
        lViewLinkToMPComponentRef(mpComponentInstance, lView);
        mpComponentInstance.__waitLinkResolve();
        ngZone.runOutsideAngular(() => {
            const initValue = getPageRefreshContext(lView);
            const diffData = getDiffData(lView, initValue);
            if (Object.keys(diffData).length) {
                mpComponentInstance.setData(diffData);
            }
        });
    }
    /** 监听事件 */
    listenerEvent() {
        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;
        }, {});
    }
    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);
            }
        },
    };
    linkNgComponentWithPage(mpComponentInstance, componentRef, ngModuleRef) {
        mpComponentInstance.__isLink = true;
        mpComponentInstance.__ngComponentHostView = componentRef.hostView;
        mpComponentInstance.__ngComponentInstance = componentRef.instance;
        mpComponentInstance.__ngComponentInjector = componentRef.injector;
        const ngZone = componentRef.injector.get(NgZone);
        mpComponentInstance.__ngZone = ngZone;
        const { lView, id } = findPageLView(componentRef);
        setLViewPath(lView, [id]);
        mpComponentInstance.__completePath = [id];
        ngZone.runOutsideAngular(() => {
            const initValue = getPageRefreshContext(lView);
            const diffData = getDiffData(lView, initValue);
            if (Object.keys(diffData).length) {
                mpComponentInstance.setData(diffData);
            }
        });
        lViewLinkToMPComponentRef(mpComponentInstance, lView);
        mpComponentInstance.__lView = lView;
        mpComponentInstance.__ngDestroy = () => {
            ngModuleRef.destroy();
            componentRef.destroy();
            removePageLViewLink(id);
            cleanAll(lView);
        };
    }
    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: {
                    ...options.methods,
                    ...this.listenerEvent(),
                    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)();
                        }
                        return _this.pageStatus.attachView.bind(this)();
                    },
                },
            };
            config.lifetimes = config.lifetimes || {};
            const oldCreated = config.lifetimes.created;
            let componentRef, ngModuleRef;
            config.lifetimes.created = function () {
                const app = getApp();
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                this.__lifeTimePromiseObject = {};
                return (this.__lifeTimePromiseObject['created'] =
                    app.__ngStartPagePromise.then(() => {
                        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 () {
                return this.__lifeTimePromiseObject['created'].then(() => {
                    _this.linkNgComponentWithPage(this, componentRef, ngModuleRef);
                    if (oldAttached) {
                        oldAttached.bind(this)();
                    }
                });
            };
            return Component(config);
        }
        const options = this.getPageOptions(component) || {};
        return Page({
            ...options,
            ...this.listenerEvent(),
            data: { hasLoad: false },
            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)();
            },
            onLoad: function (query) {
                const app = getApp();
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                this.__lifeTimePromiseObject = {};
                return (this.__lifeTimePromiseObject['onLoad'] =
                    app.__ngStartPagePromise.then(() => {
                        const { componentRef, ngModuleRef } = app.__ngStartPage(module, component, this);
                        _this.linkNgComponentWithPage(this, componentRef, ngModuleRef);
                        if (options.onLoad) {
                            return options.onLoad.bind(this)(query);
                        }
                    }));
            },
            onShow: function () {
                return (this.__lifeTimePromiseObject['onShow'] =
                    this.__lifeTimePromiseObject['onLoad'].then(async () => {
                        if (options.onShow) {
                            await options.onShow.bind(this)();
                        }
                        return _this.pageStatus.attachView.bind(this)();
                    }));
            },
            onReady: function () {
                return this.__lifeTimePromiseObject.onShow.then(() => {
                    if (options.onReady) {
                        return options.onReady.bind(this)();
                    }
                });
            },
        });
    };
    addNgComponentLinkLogic(config) {
        config.lifetimes = config.lifetimes || {};
        const oldCreate = config.lifetimes.created;
        config.lifetimes.created = function () {
            this.__waitLinkPromise = new Promise((resolve) => {
                this.__waitLinkResolve = resolve;
            });
            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;
    }
    componentRegistry = (component) => {
        const options = this.getComponentOptions(component) || {};
        let config = {
            ...options,
            data: { hasLoad: false },
            options: { ...options?.options, multipleSlots: true },
            methods: {
                ...this.listenerEvent(),
            },
        };
        config = this.addNgComponentLinkLogic(config);
        return Component(config);
    };
    getPageOptions(component) {
        return component.mpPageOptions;
    }
    getComponentOptions(component) {
        return component.mpComponentOptions;
    }
}
const MiniProgramCore = new MiniProgramCoreFactory();

const PAGE_TOKEN = new InjectionToken('PAGE_TOKEN');

class MiniProgramRenderer {
    root;
    constructor() { }
    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.appendChild(newChild);
    }
    insertBefore(parent, newChild, refChild, isMove) {
        if (isMove) {
            // todo 应该没用
        }
        if (parent) {
            parent.insertBefore(newChild, refChild);
        }
    }
    removeChild(parent, oldChild, isHostElement) {
        if (isHostElement) {
            // todo 应该没用
        }
        if (parent) {
            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 {
    defaultRenderer;
    constructor() {
        this.defaultRenderer = new MiniProgramRenderer();
    }
    createRenderer(element, type) {
        return this.defaultRenderer;
    }
    begin() { }
    end() {
        endRender();
    }
    static ɵfac = function MiniProgramRendererFactory_Factory(t) { return new (t || MiniProgramRendererFactory)(); };
    static ɵ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, propertyChange };
//# sourceMappingURL=angular-miniprogram-platform-default.mjs.map
