/*!
 * Built by Revolist OU ❤️
 */
import { getScrollDimension } from "./scroll.dimension.helpers";
const initialParams = {
    contentSize: 0,
    clientSize: 0,
    virtualSize: 0,
    maxSize: 0,
};
const NO_COORDINATE = -1;
/**
 * Based on content size, client size and virtual size
 * return full size
 */
export function getContentSize(contentSize, clientSize, virtualSize = 0) {
    return getScrollDimension({
        contentSize,
        clientSize,
        virtualSize,
    }).physicalContentSize;
}
export default class LocalScrollService {
    constructor(cfg) {
        this.cfg = cfg;
        this.preventArtificialScroll = {
            rgRow: null,
            rgCol: null,
        };
        // to check if scroll changed
        this.previousScroll = {
            rgRow: NO_COORDINATE,
            rgCol: NO_COORDINATE,
        };
        this.previousLogicalScroll = {
            rgRow: 0,
            rgCol: 0,
        };
        this.params = {
            rgRow: Object.assign({}, initialParams),
            rgCol: Object.assign({}, initialParams),
        };
    }
    setParams(params, dimension) {
        const scrollDimension = getScrollDimension(params);
        const virtualContentSize = scrollDimension.physicalContentSize;
        this.params[dimension] = Object.assign(Object.assign({}, params), { maxSize: virtualContentSize - params.clientSize, virtualContentSize,
            scrollDimension });
    }
    // apply scroll values after scroll done
    async setScroll(e) {
        this.cancelScroll(e.dimension);
        // start frame animation
        const frameAnimation = new Promise((resolve, reject) => {
            // for example safari desktop has issues with animation frame
            if (this.cfg.skipAnimationFrame) {
                return resolve();
            }
            const animationId = window.requestAnimationFrame(() => {
                resolve();
            });
            this.preventArtificialScroll[e.dimension] = reject.bind(null, animationId);
        });
        try {
            await frameAnimation;
            const params = this.getParams(e.dimension);
            e.coordinate = Math.ceil(e.coordinate);
            this.previousLogicalScroll[e.dimension] = this.wrapLogicalCoordinate(e.coordinate, params);
            const physicalCoordinate = this.toPhysicalCoordinate(e.coordinate, params);
            this.previousScroll[e.dimension] = this.wrapPhysicalCoordinate(physicalCoordinate, params);
            this.preventArtificialScroll[e.dimension] = null;
            this.cfg.applyScroll(Object.assign(Object.assign({}, e), { coordinate: physicalCoordinate }));
        }
        catch (id) {
            window.cancelAnimationFrame(id);
        }
    }
    async setScrollByDelta(e, currentPhysicalCoordinate) {
        var _a;
        const params = this.getParams(e.dimension);
        const baseCoordinate = this.previousScroll[e.dimension] === NO_COORDINATE
            ? this.toLogicalCoordinate(currentPhysicalCoordinate, params)
            : this.previousLogicalScroll[e.dimension];
        const coordinate = this.wrapLogicalCoordinate(baseCoordinate + ((_a = e.delta) !== null && _a !== void 0 ? _a : 0), params);
        const nextEvent = Object.assign(Object.assign({}, e), { coordinate });
        await this.setScroll(nextEvent);
        return nextEvent;
    }
    /**
     * On scroll event started
     */
    scroll(coordinate, dimension, force = false, delta, outside = false) {
        // cancel all previous scrolls for same dimension
        this.cancelScroll(dimension);
        // drop if no change
        if (!force && this.previousScroll[dimension] === coordinate) {
            this.previousScroll[dimension] = NO_COORDINATE;
            return;
        }
        const param = this.getParams(dimension);
        const logicalCoordinate = this.toLogicalScrollCoordinate(coordinate, dimension, param, delta);
        // let component know about scroll event started
        this.cfg.runScroll({
            dimension: dimension,
            coordinate: logicalCoordinate,
            delta,
            outside,
        });
        this.previousLogicalScroll[dimension] = logicalCoordinate;
    }
    getParams(dimension) {
        return this.params[dimension];
    }
    // check if scroll outside of region to avoid looping
    wrapPhysicalCoordinate(c, param) {
        if (c < 0) {
            return NO_COORDINATE;
        }
        if (typeof param.maxSize === 'number' && c > param.maxSize) {
            return param.maxSize;
        }
        return c;
    }
    wrapLogicalCoordinate(c, param) {
        var _a, _b;
        if (c < 0) {
            return 0;
        }
        return Math.min(c, (_b = (_a = param.scrollDimension) === null || _a === void 0 ? void 0 : _a.logicalScrollSize) !== null && _b !== void 0 ? _b : c);
    }
    // prevent already started scroll, performance optimization
    cancelScroll(dimension) {
        var _a, _b;
        (_b = (_a = this.preventArtificialScroll)[dimension]) === null || _b === void 0 ? void 0 : _b.call(_a);
        this.preventArtificialScroll[dimension] = null;
    }
    toLogicalScrollCoordinate(coordinate, dimension, param, delta) {
        const scrollDimension = param.scrollDimension;
        if (!scrollDimension) {
            return coordinate;
        }
        if (typeof delta === 'number' && scrollDimension.isCompressed) {
            const base = this.previousScroll[dimension] === NO_COORDINATE
                ? scrollDimension.toLogicalCoordinate(coordinate - delta)
                : this.previousLogicalScroll[dimension];
            return scrollDimension.toLogicalCoordinate(scrollDimension.toPhysicalCoordinate(base + delta));
        }
        return scrollDimension.toLogicalCoordinate(coordinate);
    }
    toPhysicalCoordinate(coordinate, param) {
        var _a, _b;
        return (_b = (_a = param.scrollDimension) === null || _a === void 0 ? void 0 : _a.toPhysicalCoordinate(coordinate)) !== null && _b !== void 0 ? _b : coordinate;
    }
    toLogicalCoordinate(coordinate, param) {
        var _a, _b;
        return (_b = (_a = param.scrollDimension) === null || _a === void 0 ? void 0 : _a.toLogicalCoordinate(coordinate)) !== null && _b !== void 0 ? _b : coordinate;
    }
}
