import { s } from '../../../json-crdt-patch';
import { SliceStacking } from '../slice/constants';
import { CommonSliceType } from '../slice';
import { SliceBehavior } from './SliceBehavior';
import { printTree } from 'tree-dump/lib/printTree';
/**
 * Slice registry contains a record of possible inline an block formatting
 * annotations. Each entry in the registry is a {@link SliceBehavior} that
 * specifies the behavior, tag, and other properties of the slice.
 *
 * @todo Consider moving the registry under the `/transfer` directory. Or maybe
 * `/slices` directory.
 */
export class SliceRegistry {
    /**
     * Creates a new slice registry with common tag registered.
     */
    static withCommon = () => {
        const undefSchema = s.con(undefined);
        const registry = new SliceRegistry();
        //------------------------------ Inline elements with "One" stacking behavior
        const i0 = (tag, name, fromHtml) => {
            registry.add(new SliceBehavior(SliceStacking.One, tag, name, void 0, false, void 0, fromHtml));
        };
        const i1 = (tag, name, htmlTags) => {
            const fromHtml = {};
            for (const htmlTag of htmlTags)
                fromHtml[htmlTag] = () => [tag, null];
            i0(tag, name, fromHtml);
        };
        i1(-4 /* TAG.i */, 'Italic', ['i', 'em']);
        i1(-3 /* TAG.b */, 'Bold', ['b', 'strong']);
        i1(-6 /* TAG.s */, 'Strikethrough', ['s', 'strike']);
        i0(-5 /* TAG.u */, 'Underline');
        i0(-7 /* TAG.code */, 'Code');
        i0(-8 /* TAG.mark */, 'Highlight');
        i0(-19 /* TAG.kbd */, 'Keyboard');
        i0(-11 /* TAG.del */, 'Delete');
        i0(-12 /* TAG.ins */, 'Insert');
        i0(-13 /* TAG.sup */, 'Superscript');
        i0(-14 /* TAG.sub */, 'Subscript');
        i0(-15 /* TAG.math */, 'Math');
        // --------------------------- Inline elements with "Many" stacking behavior
        const aSchema = s.obj({}, {
            href: s.str(''),
            title: s.str(''),
        });
        registry.add(new SliceBehavior(SliceStacking.Many, -9 /* TAG.a */, 'Link', aSchema, false, void 0, {
            a: (jsonml) => {
                const attr = jsonml[1] || {};
                const data = {
                    href: attr.href ?? '',
                    title: attr.title ?? '',
                };
                return [-9 /* TAG.a */, { data, inline: true }];
            },
        }));
        const colSchema = s.obj({}, {
            color: s.str(''),
        });
        registry.add(new SliceBehavior(SliceStacking.Many, -17 /* TAG.col */, 'Color', colSchema, false, void 0, {
            col: (jsonml) => {
                const attr = jsonml[1] || {};
                const data = {
                    color: attr.color ?? '',
                };
                return [-17 /* TAG.col */, { data, inline: true }];
            },
        }));
        // TODO: add more default annotations with "Many" stacking behavior
        // comment = SliceTypeCon.comment,
        // font = SliceTypeCon.font,
        // col = SliceTypeCon.col,
        // bg = SliceTypeCon.bg,
        // hidden = SliceTypeCon.hidden,
        // footnote = SliceTypeCon.footnote,
        // ref = SliceTypeCon.ref,
        // iaside = SliceTypeCon.iaside,
        // iembed = SliceTypeCon.iembed,
        // bookmark = SliceTypeCon.bookmark,
        // -------------------------- Block elements with "Marker" stacking behavior
        const commonBlockSchema = s.obj({}, {
            indent: s.con(0),
            align: s.str('left'),
        });
        const b0 = (tag, name, container) => {
            registry.add(new SliceBehavior(SliceStacking.Marker, tag, name, commonBlockSchema, container));
        };
        b0(0 /* TAG.p */, 'Paragraph', false);
        b0(1 /* TAG.blockquote */, 'Blockquote', true);
        b0(2 /* TAG.codeblock */, 'Code block', false);
        b0(3 /* TAG.pre */, 'Pre-formatted', false);
        b0(4 /* TAG.ul */, 'Unordered list', true);
        b0(5 /* TAG.ol */, 'Ordered list', true);
        b0(6 /* TAG.tl */, 'Task list', true);
        b0(7 /* TAG.li */, 'List item', true);
        b0(8 /* TAG.h1 */, 'Heading 1', false);
        b0(9 /* TAG.h2 */, 'Heading 2', false);
        b0(10 /* TAG.h3 */, 'Heading 3', false);
        b0(11 /* TAG.h4 */, 'Heading 4', false);
        b0(12 /* TAG.h5 */, 'Heading 5', false);
        b0(13 /* TAG.h6 */, 'Heading 6', false);
        b0(14 /* TAG.title */, 'Title', false);
        b0(15 /* TAG.subtitle */, 'Subtitle', false);
        // b0(TAG.br, false);
        // b0(TAG.nl, false);
        // b0(TAG.hr, false);
        // b0(TAG.page, false);
        // b0(TAG.aside, true);
        // b0(TAG.embed, false);
        // b0(TAG.column, true);
        // b0(TAG.contents, true);
        // b0(TAG.table, true);
        // b0(TAG.row, true);
        // b0(TAG.cell, true);
        // b0(TAG.collapselist, true);
        // b0(TAG.collapse, true);
        // b0(TAG.note, true);
        // b0(TAG.mathblock, false);
        return registry;
    };
    map = new Map();
    _fromHtml = new Map();
    clear() {
        this.map.clear();
        this._fromHtml.clear();
    }
    add(entry) {
        const { tag, fromHtml } = entry;
        this.map.set(tag, entry);
        const _fromHtml = this._fromHtml;
        if (fromHtml) {
            for (const htmlTag in fromHtml) {
                const converter = fromHtml[htmlTag];
                const converters = _fromHtml.get(htmlTag) ?? [];
                converters.push([entry, converter]);
                _fromHtml.set(htmlTag, converters);
            }
        }
        const tagStr = CommonSliceType[tag];
        if (tagStr && typeof tagStr === 'string' && (!fromHtml || !(tagStr in fromHtml)))
            _fromHtml.set(tagStr, [[entry, () => [tag, null]]]);
    }
    get(tag) {
        return this.map.get(tag);
    }
    isContainer(tag) {
        const entry = this.map.get(tag);
        return entry?.container ?? false;
    }
    toHtml(el) {
        const entry = this.map.get(el[0]);
        return entry?.toHtml ? entry?.toHtml(el) : void 0;
    }
    fromHtml(el) {
        const tag = el[0] + '';
        const converters = this._fromHtml.get(tag);
        if (converters) {
            for (const [entry, converter] of converters) {
                const result = converter(el);
                if (result) {
                    if (entry.isInline()) {
                        const attr = result[1] ?? (result[1] = {});
                        attr.inline = entry.isInline();
                        attr.stacking = !attr.inline ? SliceStacking.Marker : (entry.stacking ?? SliceStacking.Many);
                    }
                    return result;
                }
            }
        }
        return;
    }
    /** ----------------------------------------------------- {@link Printable} */
    toString(tab = '') {
        return ('SliceRegistry' +
            printTree(tab, [...this.map.values()].map((entry) => (tab) => entry.toString(tab))));
    }
}
//# sourceMappingURL=SliceRegistry.js.map