import { imageLoader, Enums, eventTarget, imageLoadPoolManager, cache, getConfiguration as getCoreConfiguration, } from '@cornerstonejs/core';
import { addToolState, getToolState } from './state';
import { getStackData, requestType, priority, getPromiseRemovedHandler, nearestIndex, range, } from './stackPrefetchUtils';
let configuration = {
    maxImagesToPrefetch: Infinity,
    preserveExistingPool: true,
};
let resetPrefetchTimeout;
const resetPrefetchDelay = 10;
function prefetch(element) {
    const stackPrefetchData = getToolState(element);
    if (!stackPrefetchData) {
        return;
    }
    const stackPrefetch = (stackPrefetchData || {});
    const stack = getStackData(element);
    if (!stack?.imageIds?.length) {
        console.warn('CornerstoneTools.stackPrefetch: No images in stack.');
        return;
    }
    const { currentImageIdIndex } = stack;
    stackPrefetch.enabled =
        stackPrefetch.enabled && (stackPrefetch.indicesToRequest?.length ?? 0) > 0;
    if (stackPrefetch.enabled === false) {
        return;
    }
    function removeFromList(imageIdIndex) {
        const index = stackPrefetch.indicesToRequest.indexOf(imageIdIndex);
        if (index > -1) {
            stackPrefetch.indicesToRequest.splice(index, 1);
        }
    }
    stackPrefetchData.indicesToRequest.sort((a, b) => a - b);
    const indicesToRequestCopy = stackPrefetch.indicesToRequest.slice();
    indicesToRequestCopy.forEach(function (imageIdIndex) {
        const imageId = stack.imageIds[imageIdIndex];
        if (!imageId) {
            return;
        }
        const distance = Math.abs(currentImageIdIndex - imageIdIndex);
        const imageCached = distance < 6
            ? cache.getImageLoadObject(imageId)
            : cache.isLoaded(imageId);
        if (imageCached) {
            removeFromList(imageIdIndex);
        }
    });
    if (!stackPrefetch.indicesToRequest.length) {
        return;
    }
    if (!configuration.preserveExistingPool) {
        imageLoadPoolManager.clearRequestStack(requestType);
    }
    const nearest = nearestIndex(stackPrefetch.indicesToRequest, stack.currentImageIdIndex);
    let imageId;
    let nextImageIdIndex;
    const preventCache = false;
    function doneCallback(image) {
        console.log('prefetch done: %s', image.imageId);
        const imageIdIndex = stack.imageIds.indexOf(image.imageId);
        removeFromList(imageIdIndex);
    }
    let lowerIndex = nearest.low;
    let higherIndex = nearest.high;
    const imageIdsToPrefetch = [];
    while (lowerIndex >= 0 ||
        higherIndex < stackPrefetch.indicesToRequest.length) {
        const currentIndex = stack.currentImageIdIndex;
        const shouldSkipLower = currentIndex - stackPrefetch.indicesToRequest[lowerIndex] >
            configuration.maxImagesToPrefetch;
        const shouldSkipHigher = stackPrefetch.indicesToRequest[higherIndex] - currentIndex >
            configuration.maxImagesToPrefetch;
        const shouldLoadLower = !shouldSkipLower && lowerIndex >= 0;
        const shouldLoadHigher = !shouldSkipHigher && higherIndex < stackPrefetch.indicesToRequest.length;
        if (!shouldLoadHigher && !shouldLoadLower) {
            break;
        }
        if (shouldLoadLower) {
            nextImageIdIndex = stackPrefetch.indicesToRequest[lowerIndex--];
            imageId = stack.imageIds[nextImageIdIndex];
            imageIdsToPrefetch.push(imageId);
        }
        if (shouldLoadHigher) {
            nextImageIdIndex = stackPrefetch.indicesToRequest[higherIndex++];
            imageId = stack.imageIds[nextImageIdIndex];
            imageIdsToPrefetch.push(imageId);
        }
    }
    const requestFn = (imageId, options) => imageLoader.loadAndCacheImage(imageId, options);
    imageIdsToPrefetch.forEach((imageId) => {
        const options = {
            requestType,
        };
        imageLoadPoolManager.addRequest(requestFn.bind(null, imageId, options), requestType, {
            imageId,
        }, priority);
    });
}
function onImageUpdated(e) {
    clearTimeout(resetPrefetchTimeout);
    resetPrefetchTimeout = setTimeout(function () {
        const element = e.target;
        try {
            prefetch(element);
        }
        catch (error) {
            return;
        }
    }, resetPrefetchDelay);
}
function enable(element) {
    const stack = getStackData(element);
    if (!stack || !stack.imageIds || stack.imageIds.length === 0) {
        console.warn('CornerstoneTools.stackPrefetch: No images in stack.');
        return;
    }
    const stackPrefetchData = {
        indicesToRequest: range(0, stack.imageIds.length - 1),
        enabled: true,
        direction: 1,
    };
    const indexOfCurrentImage = stackPrefetchData.indicesToRequest.indexOf(stack.currentImageIdIndex);
    stackPrefetchData.indicesToRequest.splice(indexOfCurrentImage, 1);
    addToolState(element, stackPrefetchData);
    prefetch(element);
    element.removeEventListener(Enums.Events.STACK_NEW_IMAGE, onImageUpdated);
    element.addEventListener(Enums.Events.STACK_NEW_IMAGE, onImageUpdated);
    const promiseRemovedHandler = getPromiseRemovedHandler(element);
    eventTarget.removeEventListener(Enums.Events.IMAGE_CACHE_IMAGE_REMOVED, promiseRemovedHandler);
    eventTarget.addEventListener(Enums.Events.IMAGE_CACHE_IMAGE_REMOVED, promiseRemovedHandler);
}
function disable(element) {
    clearTimeout(resetPrefetchTimeout);
    element.removeEventListener(Enums.Events.STACK_NEW_IMAGE, onImageUpdated);
    const promiseRemovedHandler = getPromiseRemovedHandler(element);
    eventTarget.removeEventListener(Enums.Events.IMAGE_CACHE_IMAGE_REMOVED, promiseRemovedHandler);
    const stackPrefetchData = getToolState(element);
    if (stackPrefetchData && stackPrefetchData.indicesToRequest.length) {
        stackPrefetchData.enabled = false;
        imageLoadPoolManager.clearRequestStack(requestType);
    }
}
function getConfiguration() {
    return configuration;
}
function setConfiguration(config) {
    configuration = config;
}
const stackPrefetch = { enable, disable, getConfiguration, setConfiguration };
export default stackPrefetch;
