/**
 * @fileoverview
 * The BlocksRuntimeCache caches data about the top block of scripts so that
 * Runtime can iterate a targeted opcode and iterate the returned set faster.
 * Many top blocks need to match fields as well as opcode, since that matching
 * compares strings in uppercase we can go ahead and uppercase the cached value
 * so we don't need to in the future.
 */

/**
 * A set of cached data about the top block of a script.
 * @param {Blocks} container - Container holding the block and related data
 * @param {string} blockId - Id for whose block data is cached in this instance
 */
class RuntimeScriptCache {
    constructor (container, blockId) {
        /**
         * Container with block data for blockId.
         * @type {Blocks}
         */
        this.container = container;

        /**
         * ID for block this instance caches.
         * @type {string}
         */
        this.blockId = blockId;

        const block = container.getBlock(blockId);
        const fields = container.getFields(block);

        /**
         * Formatted fields or fields of input blocks ready for comparison in
         * runtime.
         *
         * This is a clone of parts of the targeted blocks. Changes to these
         * clones are limited to copies under RuntimeScriptCache and will not
         * appear in the original blocks in their container. This copy is
         * modified changing the case of strings to uppercase. These uppercase
         * values will be compared later by the VM.
         * @type {object}
         */
        this.fieldsOfInputs = Object.assign({}, fields);
        if (Object.keys(fields).length === 0) {
            const inputs = container.getInputs(block);
            for (const input in inputs) {
                if (!inputs.hasOwnProperty(input)) continue;
                const id = inputs[input].block;
                const inputBlock = container.getBlock(id);
                const inputFields = container.getFields(inputBlock);
                Object.assign(this.fieldsOfInputs, inputFields);
            }
        }
        for (const key in this.fieldsOfInputs) {
            const field = this.fieldsOfInputs[key] = Object.assign({}, this.fieldsOfInputs[key]);
            if (field.value.toUpperCase) {
                field.value = field.value.toUpperCase();
            }
        }
    }
}

/**
 * Get an array of scripts from a block container prefiltered to match opcode.
 * @param {Blocks} container - Container of blocks
 * @param {string} opcode - Opcode to filter top blocks by
 */
exports.getScripts = function () {
    throw new Error('blocks.js has not initialized BlocksRuntimeCache');
};

/**
 * Exposed RuntimeScriptCache class used by integration in blocks.js.
 * @private
 */
exports._RuntimeScriptCache = RuntimeScriptCache;

require('./blocks');
