import PubBase from './PubBase';
import PubListen from './PubListen';
import PubUpdate from './PubUpdate';
import PubCommon from './PubCommon';
import pipe from '../utils/pipe';
import optMasonry from '../options/optMasonry';
import prefix from '../global/prefix';
import classes from '../utils/classes';
import toPixel from '../utils/toPixel';
import Observe from './Observe';
import getDataType from '../utils/getDataType';
class Masonry extends pipe(PubListen, PubUpdate, PubCommon)(PubBase) {
    constructor(elem, options, inital = true) {
        super();
        this.options = {};
        this.ready({
            options,
            maps: optMasonry,
            host: elem,
            component: false,
        });
        this.itemsTmp = [];
        this.itemsObs = new Observe(this.itemsTmp, {
            onSet: (data) => {
                if (getDataType(data.value).includes('HTML')) {
                    this.targetEl.appendChild(data.value);
                    this.itemSizeObs.observe(data.value);
                }
            },
            onDelete: (data) => { },
            onComplete: () => {
            }
        });
        this.items = this.itemsObs.proxy;
        this.itemSizeObs = new ResizeObserver((data) => {
            for (let k of data) {
                this.updateItemStyle(k.target);
            }
        });
        super.listen({ name: 'ready' });
        inital ? this.init() : null;
    }
    init(cb) {
        super.listen({ name: 'start' });
        this.gap = Math.ceil(toPixel(this.options.gap));
        this.setAttrs();
        if (Array.isArray(this.options.content) && this.options.content.length > 0) {
            this.items.push(...this.options.content);
        }
        else if (this.targetEl.innerHTML.trim()) {
            this.items.push(...this.targetEl.children);
        }
        else {
            return this;
        }
        super.listen({ name: 'init', cb });
        return this;
    }
    setAttrs() {
        this.targetEl.classList.add(`${prefix}masonry`);
        classes.add(this.targetEl, this.options.classes);
        this.targetEl.style.columnGap = `${this.gap}px`;
        this.options.cols && (this.targetEl.style.gridTemplateColumns = `repeat(${this.options.cols}, 1fr)`);
    }
    updateItemStyle(el) {
        let height = el.offsetHeight;
        el.style.gridRow = `span ${Math.ceil(height + this.gap)}`;
    }
    setItem(el) {
        this.itemSizeObs.observe(el);
    }
    async add(data, cb) {
        if (this.destroyed)
            return this;
        let tmp = Array.isArray(data) ? data : [data];
        this.options.b4Add && await this.options.b4Add.call(this, tmp);
        this.items.push(...tmp);
        super.listen({ name: 'add', cb, params: [tmp] });
        return this;
    }
    async remove(data, cb) {
        if (this.destroyed)
            return this;
        let tmp = Array.isArray(data) ? data : [data];
        this.options.b4Remove && await this.options.b4Remove.call(this, tmp);
        for (let k = 0; k < tmp.length; k++) {
            let item = tmp[k], idx = this.items.findIndex((k) => k === item);
            if (idx > -1) {
                this.items.splice(idx, 1);
                this.items[idx].remove();
            }
        }
        super.listen({ name: 'remove', cb, params: [tmp] });
        return this;
    }
    async clear(cb) {
        if (this.destroyed)
            return this;
        this.options.b4Clear && await this.options.b4Clear.call(this);
        this.items.splice(0);
        this.targetEl.innerHTML = '';
        super.listen({ name: 'clear', cb });
        return this;
    }
    /**
     * @description Destroy instance.Prohibit use.
     * @param {fn_cb_destroy} cb - Call back after destroy.
     * @returns {*} Return the instance itself
     */
    destroy(cb) {
        if (this.destroyed)
            return this;
        this.button.onclick = null;
        super.clearCache();
        this.destroyed = true;
        super.listen({ name: 'destroy', cb });
        return this;
    }
}
Masonry.hostType = 'el';
export default Masonry;
