/**
 * @module ol/source/IIIF
 */
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
import TileGrid from '../tilegrid/TileGrid.js';
import TileImage from './TileImage.js';
import { CustomTile } from './Zoomify.js';
import { DEFAULT_TILE_SIZE } from '../tilegrid/common.js';
import { Versions } from '../format/IIIFInfo.js';
import { assert } from '../asserts.js';
import { getTopLeft } from '../extent.js';
import { toSize } from '../size.js';
/**
 * @typedef {Object} Options
 * @property {import("./Source.js").AttributionLike} [attributions] Attributions.
 * @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
 * @property {number} [cacheSize]
 * @property {null|string} [crossOrigin]
 * @property {import("../extent.js").Extent} [extent=[0, -height, width, 0]]
 * @property {string} [format='jpg'] Requested image format.
 * @property {boolean} [imageSmoothing=true] Enable image smoothing.
 * @property {import("../proj.js").ProjectionLike} [projection]
 * @property {string} [quality] Requested IIIF image quality. Default is 'native'
 * for version 1, 'default' for versions 2 and 3.
 * @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels).
 * Higher values can increase reprojection performance, but decrease precision.
 * @property {Array<number>} [resolutions] Supported resolutions as given in IIIF 'scaleFactors'
 * @property {import("../size.js").Size} size Size of the image [width, height].
 * @property {Array<import("../size.js").Size>} [sizes] Supported scaled image sizes.
 * Content of the IIIF info.json 'sizes' property, but as array of Size objects.
 * @property {import("./State.js").default} [state] Source state.
 * @property {Array<string>} [supports=[]] Supported IIIF region and size calculation
 * features.
 * @property {number} [tilePixelRatio]
 * @property {number|import("../size.js").Size} [tileSize] Tile size.
 * Same tile size is used for all zoom levels. If tile size is a number,
 * a square tile is assumed. If the IIIF image service supports arbitrary
 * tiling (sizeByH, sizeByW, sizeByWh or sizeByPct as well as regionByPx or regionByPct
 * are supported), the default tilesize is 256.
 * @property {number} [transition]
 * @property {string} [url] Base URL of the IIIF Image service.
 * This should be the same as the IIIF Image ID.
 * @property {import("../format/IIIFInfo.js").Versions} [version=Versions.VERSION2] Service's IIIF Image API version.
 * @property {number} [zDirection=0] Indicate which resolution should be used
 * by a renderer if the view resolution does not match any resolution of the tile source.
 * If 0, the nearest resolution will be used. If 1, the nearest lower resolution
 * will be used. If -1, the nearest higher resolution will be used.
 */
function formatPercentage(percentage) {
    return percentage.toLocaleString('en', { maximumFractionDigits: 10 });
}
/**
 * @classdesc
 * Layer source for IIIF Image API services.
 * @api
 */
var IIIF = /** @class */ (function (_super) {
    __extends(IIIF, _super);
    /**
     * @param {Options=} opt_options Tile source options. Use {@link import("../format/IIIFInfo.js").IIIFInfo}
     * to parse Image API service information responses into constructor options.
     * @api
     */
    function IIIF(opt_options) {
        var _this = this;
        /**
         * @type {Partial<Options>}
         */
        var options = opt_options || {};
        var baseUrl = options.url || '';
        baseUrl =
            baseUrl +
                (baseUrl.lastIndexOf('/') === baseUrl.length - 1 || baseUrl === ''
                    ? ''
                    : '/');
        var version = options.version || Versions.VERSION2;
        var sizes = options.sizes || [];
        var size = options.size;
        assert(size != undefined &&
            Array.isArray(size) &&
            size.length == 2 &&
            !isNaN(size[0]) &&
            size[0] > 0 &&
            !isNaN(size[1]) &&
            size[1] > 0, 60);
        var width = size[0];
        var height = size[1];
        var tileSize = options.tileSize;
        var tilePixelRatio = options.tilePixelRatio || 1;
        var format = options.format || 'jpg';
        var quality = options.quality ||
            (options.version == Versions.VERSION1 ? 'native' : 'default');
        var resolutions = options.resolutions || [];
        var supports = options.supports || [];
        var extent = options.extent || [0, -height, width, 0];
        var supportsListedSizes = sizes != undefined && Array.isArray(sizes) && sizes.length > 0;
        var supportsListedTiles = tileSize !== undefined &&
            ((typeof tileSize === 'number' &&
                Number.isInteger(tileSize) &&
                tileSize > 0) ||
                (Array.isArray(tileSize) && tileSize.length > 0));
        var supportsArbitraryTiling = supports != undefined &&
            Array.isArray(supports) &&
            (supports.includes('regionByPx') || supports.includes('regionByPct')) &&
            (supports.includes('sizeByWh') ||
                supports.includes('sizeByH') ||
                supports.includes('sizeByW') ||
                supports.includes('sizeByPct'));
        var tileWidth, tileHeight, maxZoom;
        resolutions.sort(function (a, b) {
            return b - a;
        });
        if (supportsListedTiles || supportsArbitraryTiling) {
            if (tileSize != undefined) {
                if (typeof tileSize === 'number' &&
                    Number.isInteger(tileSize) &&
                    tileSize > 0) {
                    tileWidth = tileSize;
                    tileHeight = tileSize;
                }
                else if (Array.isArray(tileSize) && tileSize.length > 0) {
                    if (tileSize.length == 1 ||
                        (tileSize[1] == undefined && Number.isInteger(tileSize[0]))) {
                        tileWidth = tileSize[0];
                        tileHeight = tileSize[0];
                    }
                    if (tileSize.length == 2) {
                        if (Number.isInteger(tileSize[0]) &&
                            Number.isInteger(tileSize[1])) {
                            tileWidth = tileSize[0];
                            tileHeight = tileSize[1];
                        }
                        else if (tileSize[0] == undefined &&
                            Number.isInteger(tileSize[1])) {
                            tileWidth = tileSize[1];
                            tileHeight = tileSize[1];
                        }
                    }
                }
            }
            if (tileWidth === undefined || tileHeight === undefined) {
                tileWidth = DEFAULT_TILE_SIZE;
                tileHeight = DEFAULT_TILE_SIZE;
            }
            if (resolutions.length == 0) {
                maxZoom = Math.max(Math.ceil(Math.log(width / tileWidth) / Math.LN2), Math.ceil(Math.log(height / tileHeight) / Math.LN2));
                for (var i = maxZoom; i >= 0; i--) {
                    resolutions.push(Math.pow(2, i));
                }
            }
            else {
                var maxScaleFactor = Math.max.apply(Math, resolutions);
                // TODO maxScaleFactor might not be a power to 2
                maxZoom = Math.round(Math.log(maxScaleFactor) / Math.LN2);
            }
        }
        else {
            // No tile support.
            tileWidth = width;
            tileHeight = height;
            resolutions = [];
            if (supportsListedSizes) {
                /*
                 * 'sizes' provided. Use full region in different resolutions. Every
                 * resolution has only one tile.
                 */
                sizes.sort(function (a, b) {
                    return a[0] - b[0];
                });
                maxZoom = -1;
                var ignoredSizesIndex = [];
                for (var i = 0; i < sizes.length; i++) {
                    var resolution = width / sizes[i][0];
                    if (resolutions.length > 0 &&
                        resolutions[resolutions.length - 1] == resolution) {
                        ignoredSizesIndex.push(i);
                        continue;
                    }
                    resolutions.push(resolution);
                    maxZoom++;
                }
                if (ignoredSizesIndex.length > 0) {
                    for (var i = 0; i < ignoredSizesIndex.length; i++) {
                        sizes.splice(ignoredSizesIndex[i] - i, 1);
                    }
                }
            }
            else {
                // No useful image information at all. Try pseudo tile with full image.
                resolutions.push(1);
                sizes.push([width, height]);
                maxZoom = 0;
            }
        }
        var tileGrid = new TileGrid({
            tileSize: [tileWidth, tileHeight],
            extent: extent,
            origin: getTopLeft(extent),
            resolutions: resolutions,
        });
        var tileUrlFunction = function (tileCoord, pixelRatio, projection) {
            var regionParam, sizeParam;
            var zoom = tileCoord[0];
            if (zoom > maxZoom) {
                return;
            }
            var tileX = tileCoord[1], tileY = tileCoord[2], scale = resolutions[zoom];
            if (tileX === undefined ||
                tileY === undefined ||
                scale === undefined ||
                tileX < 0 ||
                Math.ceil(width / scale / tileWidth) <= tileX ||
                tileY < 0 ||
                Math.ceil(height / scale / tileHeight) <= tileY) {
                return;
            }
            if (supportsArbitraryTiling || supportsListedTiles) {
                var regionX = tileX * tileWidth * scale, regionY = tileY * tileHeight * scale;
                var regionW = tileWidth * scale, regionH = tileHeight * scale, sizeW = tileWidth, sizeH = tileHeight;
                if (regionX + regionW > width) {
                    regionW = width - regionX;
                }
                if (regionY + regionH > height) {
                    regionH = height - regionY;
                }
                if (regionX + tileWidth * scale > width) {
                    sizeW = Math.floor((width - regionX + scale - 1) / scale);
                }
                if (regionY + tileHeight * scale > height) {
                    sizeH = Math.floor((height - regionY + scale - 1) / scale);
                }
                if (regionX == 0 &&
                    regionW == width &&
                    regionY == 0 &&
                    regionH == height) {
                    // canonical full image region parameter is 'full', not 'x,y,w,h'
                    regionParam = 'full';
                }
                else if (!supportsArbitraryTiling ||
                    supports.includes('regionByPx')) {
                    regionParam = regionX + ',' + regionY + ',' + regionW + ',' + regionH;
                }
                else if (supports.includes('regionByPct')) {
                    var pctX = formatPercentage((regionX / width) * 100), pctY = formatPercentage((regionY / height) * 100), pctW = formatPercentage((regionW / width) * 100), pctH = formatPercentage((regionH / height) * 100);
                    regionParam = 'pct:' + pctX + ',' + pctY + ',' + pctW + ',' + pctH;
                }
                if (version == Versions.VERSION3 &&
                    (!supportsArbitraryTiling || supports.includes('sizeByWh'))) {
                    sizeParam = sizeW + ',' + sizeH;
                }
                else if (!supportsArbitraryTiling || supports.includes('sizeByW')) {
                    sizeParam = sizeW + ',';
                }
                else if (supports.includes('sizeByH')) {
                    sizeParam = ',' + sizeH;
                }
                else if (supports.includes('sizeByWh')) {
                    sizeParam = sizeW + ',' + sizeH;
                }
                else if (supports.includes('sizeByPct')) {
                    sizeParam = 'pct:' + formatPercentage(100 / scale);
                }
            }
            else {
                regionParam = 'full';
                if (supportsListedSizes) {
                    var regionWidth = sizes[zoom][0], regionHeight = sizes[zoom][1];
                    if (version == Versions.VERSION3) {
                        if (regionWidth == width && regionHeight == height) {
                            sizeParam = 'max';
                        }
                        else {
                            sizeParam = regionWidth + ',' + regionHeight;
                        }
                    }
                    else {
                        if (regionWidth == width) {
                            sizeParam = 'full';
                        }
                        else {
                            sizeParam = regionWidth + ',';
                        }
                    }
                }
                else {
                    sizeParam = version == Versions.VERSION3 ? 'max' : 'full';
                }
            }
            return (baseUrl + regionParam + '/' + sizeParam + '/0/' + quality + '.' + format);
        };
        var IiifTileClass = CustomTile.bind(null, toSize(tileSize || 256).map(function (size) {
            return size * tilePixelRatio;
        }));
        _this = _super.call(this, {
            attributions: options.attributions,
            attributionsCollapsible: options.attributionsCollapsible,
            cacheSize: options.cacheSize,
            crossOrigin: options.crossOrigin,
            imageSmoothing: options.imageSmoothing,
            projection: options.projection,
            reprojectionErrorThreshold: options.reprojectionErrorThreshold,
            state: options.state,
            tileClass: IiifTileClass,
            tileGrid: tileGrid,
            tilePixelRatio: options.tilePixelRatio,
            tileUrlFunction: tileUrlFunction,
            transition: options.transition,
        }) || this;
        /**
         * @type {number}
         */
        _this.zDirection = options.zDirection;
        return _this;
    }
    return IIIF;
}(TileImage));
export default IIIF;
//# sourceMappingURL=IIIF.js.map