import { Cache } from "../../../core/cache/Cache.js";
import { KeyValuePair } from "../../../core/collection/KeyValuePair.js";
import { noop } from "../../../core/function/noop.js";
import { invokeObjectEquals } from "../../../core/model/object/invokeObjectEquals.js";
import { invokeObjectHash } from "../../../core/model/object/invokeObjectHash.js";
import { ObjectPoolFactory } from "../../../core/model/object/ObjectPoolFactory.js";
import { HTMLElementCacheKey } from "../../common/HTMLElementCacheKey.js";
import View from "../../View.js";

/**
 *
 * @type {Cache<HTMLElementCacheKey, HTMLElement[]>}
 */
const cache = new Cache({
    maxWeight: 100,
    keyHashFunction: invokeObjectHash,
    keyEqualityFunction: invokeObjectEquals
});

/**
 *
 * @type {ObjectPoolFactory<HTMLElementCacheKey>}
 */
const pool = new ObjectPoolFactory(
    () => new HTMLElementCacheKey(),
    noop,
    key => {
        key.reset();
    }
);


/**
 *
 * @param {HTMLElementCacheKey} key
 * @return {HTMLElement}
 */
function obtainElement(key) {
    const elements = cache.get(key);

    if (elements !== null) {

        // console.log('Re-using image from cache', key.toJSON());

        let element;
        if (elements.length === 1) {
            element = elements[0];
            cache.remove(key);
        } else {
            element = elements.pop();
        }

        return element;
    } else {
        return key.createElement();
    }
}

export class VideoView extends View {
    /**
     *
     * @param {String|ObservedString} url
     * @param {String[]} classList
     * @param {*} [attributes]
     */
    constructor(url, { classList = [], attributes = {} } = {}) {
        super();


        /**
         * @type {string}
         */
        let src;

        if (typeof url === "string") {
            src = url;
        } else if (typeof url === "object" && typeof url.getValue === "function") {
            src = url.getValue();

            if (url.onChanged !== undefined) {
                this.bindSignal(url.onChanged, this.__setSource, this);
            }
        }

        const cacheKey = pool.create();
        cacheKey.tag = "video";

        cacheKey.attributes = [
            new KeyValuePair('src', src),
            new KeyValuePair('class', classList.join(' '))
        ];

        for (const attributesKey in attributes) {
            const attributeValue = String(attributes[attributesKey]);

            const attribute = new KeyValuePair(attributesKey, attributeValue);

            cacheKey.attributes.push(attribute);
        }

        cacheKey.update();


        this.el = obtainElement(cacheKey);

        pool.release(cacheKey);

        const classListSize = classList.length;

        for (let i = 0; i < classListSize; i++) {
            const c = classList[i];

            this.addClass(c);
        }

        this.size.onChanged.add(this.__setSize, this);

    }

    destroy() {
        super.destroy();

        const key = pool.create();
        key.initializeFromElement(this.el);

        let elements = cache.get(key);

        if (elements === null) {
            elements = [this.el];

            cache.put(key, elements);
        } else if (elements.length < 100) {
            elements.push(this.el);

            pool.release(key);
        }

    }

    /**
     *
     * @param {number} x
     * @param {number} y
     * @private
     */
    __setSize(x, y) {
        this.el.setAttribute('width', x);
        this.el.setAttribute('height', y);
    }

    /**
     *
     * @param {string} url
     * @private
     */
    __setSource(url) {
        this.el.setAttribute('src', url);
    }
}

/**
 * @readonly
 * @type {boolean}
 */
VideoView.prototype.isVideoView = true;
