var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var _PlayAssets_instances, _PlayAssets_localFileHandles, _PlayAssets_mountAssets, _PlayAssets_remount, _PlayAssets_unmount, _PlayAssets_mountLocalArchive, _PlayAssets_mountLocalDirectory, _PlayAssets_mountRoot, _PlayAssets_unmountRoot, _PlayAssets_mountVirtualFS, _PlayAssets_tryMountCachedLocalFS, _PlayAssets_addVirtualFile, _PlayAssets_renameVirtualFile, _PlayAssets_removeVirtualFile, _PlayAssets_clearVirtualFiles, _PlayAssets_cacheLoadHandles, _PlayAssets_cacheUpdate, _PlayAssets_cacheClear, _PlayAssets_canReadHandle, _PlayAssets_updateMap, _PlayAssets_clearAssetMap, _PlayAssets_updateState;
import { ReactiveElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { WebAccess, WebStorage } from '@zenfs/dom';
import * as ZenFS from '@zenfs/core';
import { fs, InMemory } from '@zenfs/core';
import * as IDB from 'idb-keyval';
import { hasFileAccessAPI, tryGetFile, tryQueryPermission } from '../../utils/file-access-api.js';
import { Zip } from '@zenfs/zip';
import { Bubble } from '../../utils/bubble.js';
const CACHE_LAST_DIR = 'lastMountedDirectory';
const CACHE_LAST_ZIP = 'lastMountedArchive';
const MIME = {
    html: 'text/html',
    htm: 'text/html',
    jpg: 'image/jpeg',
    jpeg: 'image/jpeg',
    png: 'image/png',
    gif: 'image/gif'
};
const DEFAULT_MIME = 'application/octet-stream';
//endregion
export function emptyAssetsState() {
    return {
        hasFileAccessAPI,
        filesystemType: 'virtual',
        map: {},
        count: 0
    };
}
let PlayAssets = class PlayAssets extends ReactiveElement {
    constructor() {
        super(...arguments);
        _PlayAssets_instances.add(this);
        //region Properties
        this.allowStorage = false;
        this.assetsState = emptyAssetsState();
        this.filesystemType = 'virtual';
        //endregion
        //region Private fields
        _PlayAssets_localFileHandles.set(this, {}
        //endregion
        //region ReactiveElement overrides
        );
        //endregion
    }
    //endregion
    //region ReactiveElement overrides
    willUpdate(changedProperties) {
        if (changedProperties.has('filesystemType')) {
            void __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountAssets).call(this);
        }
    }
    //endregion
    //region Event handlers
    async onFilesystemChange(change) {
        switch (change.kind) {
            case 'filesystem-type':
                this.filesystemType = change.filesystemType;
                await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountAssets).call(this);
                break;
            case 'remount-archive':
                await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_remount).call(this);
                break;
            case 'mount-archive':
                await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountLocalArchive).call(this, change.archiveHandle);
                break;
            case 'mount-directory':
                await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountLocalDirectory).call(this, change.directoryHandle);
                break;
            case 'unmount':
                await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_unmount).call(this);
                break;
        }
    }
    async onVirtualFileChange(change) {
        if (this.filesystemType !== 'virtual') {
            return;
        }
        switch (change.kind) {
            case 'add':
                await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_addVirtualFile).call(this, change.fileHandle);
                break;
            case 'rename':
                await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_renameVirtualFile).call(this, change.oldName, change.newName);
                break;
            case 'remove':
                await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_removeVirtualFile).call(this, change.name);
                break;
            case 'clear':
                await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_clearVirtualFiles).call(this);
                break;
            default:
                return;
        }
        await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_updateMap).call(this);
    }
};
_PlayAssets_localFileHandles = new WeakMap();
_PlayAssets_instances = new WeakSet();
_PlayAssets_mountAssets = 
//endregion
//region Mount management
async function _PlayAssets_mountAssets() {
    __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_unmountRoot).call(this);
    if (this.filesystemType === 'virtual') {
        await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountVirtualFS).call(this);
    }
    else {
        await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_tryMountCachedLocalFS).call(this);
    }
};
_PlayAssets_remount = async function _PlayAssets_remount() {
    if (__classPrivateFieldGet(this, _PlayAssets_localFileHandles, "f").archive) {
        await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountLocalArchive).call(this, __classPrivateFieldGet(this, _PlayAssets_localFileHandles, "f").archive);
    }
};
_PlayAssets_unmount = async function _PlayAssets_unmount() {
    __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_unmountRoot).call(this);
    await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_updateMap).call(this);
    await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_cacheUpdate).call(this, {});
};
_PlayAssets_mountLocalArchive = async function _PlayAssets_mountLocalArchive(fileHandle) {
    let file;
    if ('getFile' in fileHandle) {
        file = await fileHandle.getFile();
        await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_cacheUpdate).call(this, { archive: fileHandle });
    }
    else {
        file = fileHandle;
        await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_cacheClear).call(this);
    }
    await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountRoot).call(this, Zip.create({ zipData: await file.arrayBuffer() }));
    __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_updateState).call(this, { archiveFilename: fileHandle.name });
};
_PlayAssets_mountLocalDirectory = async function _PlayAssets_mountLocalDirectory(directoryHandle) {
    await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountRoot).call(this, WebAccess.create({ handle: directoryHandle }));
    await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_cacheUpdate).call(this, { directory: directoryHandle });
    __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_updateState).call(this, { directoryName: directoryHandle.name });
};
_PlayAssets_mountRoot = async function _PlayAssets_mountRoot(fs) {
    __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_unmountRoot).call(this);
    ZenFS.mount('/', fs);
    await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_updateMap).call(this);
};
_PlayAssets_unmountRoot = function _PlayAssets_unmountRoot() {
    if (ZenFS.mounts.has('/')) {
        ZenFS.umount('/');
        __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_updateState).call(this, {
            archiveFilename: undefined,
            directoryName: undefined,
            map: {},
            count: 0
        });
    }
};
_PlayAssets_mountVirtualFS = async function _PlayAssets_mountVirtualFS() {
    if (this.allowStorage) {
        await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountRoot).call(this, WebStorage.create({}));
    }
    else {
        await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountRoot).call(this, InMemory.create({}));
    }
};
_PlayAssets_tryMountCachedLocalFS = async function _PlayAssets_tryMountCachedLocalFS() {
    const [directory, archive] = await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_cacheLoadHandles).call(this);
    if (directory && (await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_canReadHandle).call(this, directory))) {
        await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountLocalDirectory).call(this, directory);
    }
    else if (archive && (await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_canReadHandle).call(this, archive))) {
        await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_mountLocalArchive).call(this, archive);
    }
    else {
        await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_cacheClear).call(this);
    }
};
_PlayAssets_addVirtualFile = 
//endregion
//region Virtual filesystem management
async function _PlayAssets_addVirtualFile(handle) {
    const file = await tryGetFile(handle);
    await fs.promises.writeFile(file.name, new Uint8Array(await file.arrayBuffer()), { flush: true });
    await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_updateMap).call(this);
};
_PlayAssets_renameVirtualFile = async function _PlayAssets_renameVirtualFile(oldName, newName) {
    await fs.promises.rename(oldName, newName);
    await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_updateMap).call(this);
};
_PlayAssets_removeVirtualFile = async function _PlayAssets_removeVirtualFile(name) {
    await fs.promises.unlink(name);
    await __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_updateMap).call(this);
};
_PlayAssets_clearVirtualFiles = async function _PlayAssets_clearVirtualFiles() {
    await Promise.all(Object.keys(this.assetsState.map ?? {}).map(name => fs.promises.unlink(name)));
};
_PlayAssets_cacheLoadHandles = 
//endregion
//region IndexedDB cache
async function _PlayAssets_cacheLoadHandles() {
    const [dir, zip] = await IDB.getMany([CACHE_LAST_DIR, CACHE_LAST_ZIP]);
    return [dir, zip];
};
_PlayAssets_cacheUpdate = async function _PlayAssets_cacheUpdate(handles) {
    __classPrivateFieldSet(this, _PlayAssets_localFileHandles, handles, "f");
    await IDB.setMany([
        [CACHE_LAST_DIR, handles.directory],
        [CACHE_LAST_ZIP, handles.archive]
    ]);
};
_PlayAssets_cacheClear = async function _PlayAssets_cacheClear() {
    await IDB.delMany([CACHE_LAST_DIR, CACHE_LAST_ZIP]);
};
_PlayAssets_canReadHandle = async function _PlayAssets_canReadHandle(handle) {
    return (await tryQueryPermission(handle, { mode: 'read' })) === 'granted';
};
_PlayAssets_updateMap = 
//endregion
//region State
async function _PlayAssets_updateMap() {
    __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_clearAssetMap).call(this);
    const assetMap = {};
    let assetCount = 0;
    if (ZenFS.mounts.has('/')) {
        const entries = [];
        entries.push(...(await fs.promises.readdir('/')));
        while (entries.length > 0) {
            const entry = entries.shift();
            // remove the leading / present in some backends
            const name = entry.replace(/^\//, '');
            const stat = await fs.promises.stat(entry);
            if (stat.isDirectory()) {
                entries.unshift(...(await fs.promises.readdir(entry)).map(child => {
                    if (child.startsWith('/'))
                        return child;
                    return `${entry}/${child}`;
                }));
            }
            else {
                const file = await fs.promises.open(entry);
                const buffer = await file.readFile();
                await file.close();
                const basename = entry.split('/').at(-1);
                const extension = basename.split('.').at(-1);
                const mimetype = MIME[extension] ?? DEFAULT_MIME;
                assetMap[name] = window.URL.createObjectURL(new Blob([buffer], { type: mimetype }));
                assetCount++;
            }
        }
    }
    __classPrivateFieldGet(this, _PlayAssets_instances, "m", _PlayAssets_updateState).call(this, { map: assetMap, count: assetCount });
};
_PlayAssets_clearAssetMap = function _PlayAssets_clearAssetMap() {
    const assetMap = this.assetsState.map ?? {};
    for (const entry of Object.keys(assetMap)) {
        const url = assetMap[entry] ?? undefined;
        if (url) {
            URL.revokeObjectURL(url);
        }
    }
};
_PlayAssets_updateState = function _PlayAssets_updateState(updates) {
    const newState = {
        ...this.assetsState,
        ...updates,
        filesystemType: this.filesystemType
    };
    this.dispatchEvent(Bubble('assets-updated', newState));
};
__decorate([
    property({ attribute: 'allow-storage', type: Boolean })
], PlayAssets.prototype, "allowStorage", void 0);
__decorate([
    property({ attribute: false })
], PlayAssets.prototype, "assetsState", void 0);
__decorate([
    property({ attribute: 'filesystem-type', type: String, reflect: true })
], PlayAssets.prototype, "filesystemType", void 0);
PlayAssets = __decorate([
    customElement('play-assets')
], PlayAssets);
export { PlayAssets };
//# sourceMappingURL=play-assets.js.map