"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
            t[p[i]] = s[p[i]];
    return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const image_utils_1 = require("@expo/image-utils");
const fs_extra_1 = __importDefault(require("fs-extra"));
const mime_1 = __importDefault(require("mime"));
const node_fetch_1 = __importDefault(require("node-fetch"));
const path_1 = __importDefault(require("path"));
const stream_1 = __importDefault(require("stream"));
const tempy_1 = __importDefault(require("tempy"));
const util_1 = __importDefault(require("util"));
const Errors_1 = require("./Errors");
const utils_1 = require("./utils");
const Apple_1 = require("./validators/Apple");
const supportedMimeTypes = ['image/png', 'image/jpeg', 'image/webp'];
function sanitizeIcon(iconSnippet) {
    if (!iconSnippet.src) {
        throw new Errors_1.IconError('Unknown icon source.');
    }
    const sizes = utils_1.toArray(iconSnippet.size || iconSnippet.sizes);
    if (!sizes) {
        throw new Errors_1.IconError('Unknown icon sizes.');
    }
    return {
        src: iconSnippet.src,
        resizeMode: iconSnippet.resizeMode,
        sizes,
        media: iconSnippet.media,
        destination: iconSnippet.destination,
        ios: iconSnippet.ios,
        color: iconSnippet.color,
    };
}
function getBufferWithMimeAsync({ src, resizeMode, color }, mimeType, { width, height }) {
    return __awaiter(this, void 0, void 0, function* () {
        let imagePath;
        if (!supportedMimeTypes.includes(mimeType)) {
            imagePath = src;
        }
        else {
            let localSrc = src;
            // In case the icon is a remote URL we need to download it first
            if (src.startsWith('http')) {
                localSrc = yield downloadImage(src);
            }
            imagePath = yield resize(localSrc, mimeType, width, height, resizeMode, color);
        }
        try {
            return yield fs_extra_1.default.readFile(imagePath);
        }
        catch (err) {
            throw new Errors_1.IconError(`It was not possible to read '${src}'.`);
        }
    });
}
function downloadImage(url) {
    return __awaiter(this, void 0, void 0, function* () {
        const outputPath = tempy_1.default.directory();
        const localPath = path_1.default.join(outputPath, path_1.default.basename(url));
        const response = yield node_fetch_1.default(url);
        if (!response.ok) {
            throw new Errors_1.IconError(`It was not possible to download splash screen from '${url}'`);
        }
        // Download to local file
        const streamPipeline = util_1.default.promisify(stream_1.default.pipeline);
        yield streamPipeline(response.body, fs_extra_1.default.createWriteStream(localPath));
        return localPath;
    });
}
function processImageAsync(size, icon, shouldFingerprint, publicPath) {
    return __awaiter(this, void 0, void 0, function* () {
        const { width, height } = utils_1.toSize(size);
        if (width <= 0 || height <= 0) {
            throw Error(`Failed to process image with invalid size: { width: ${width}, height: ${height}}`);
        }
        const mimeType = mime_1.default.getType(icon.src);
        if (!mimeType) {
            throw new Error(`Invalid mimeType for image with source: ${icon.src}`);
        }
        const imageBuffer = yield getBufferWithMimeAsync(icon, mimeType, { width, height });
        const dimensions = `${width}x${height}`;
        const fileName = shouldFingerprint
            ? `icon_${dimensions}.${utils_1.generateFingerprint(imageBuffer)}.${mime_1.default.getExtension(mimeType)}`
            : `icon_${dimensions}.${mime_1.default.getExtension(mimeType)}`;
        const iconOutputDir = icon.destination ? utils_1.joinURI(icon.destination, fileName) : fileName;
        const iconPublicUrl = utils_1.joinURI(publicPath, iconOutputDir);
        return {
            manifestIcon: {
                src: iconPublicUrl,
                sizes: dimensions,
                type: mimeType,
            },
            webpackAsset: {
                output: iconOutputDir,
                url: iconPublicUrl,
                source: imageBuffer,
                size: imageBuffer.length,
                ios: icon.ios
                    ? { valid: icon.ios, media: icon.media, size: dimensions, href: iconPublicUrl }
                    : false,
                resizeMode: icon.resizeMode,
                color: icon.color,
            },
        };
    });
}
function ensureValidMimeType(mimeType) {
    if (['input', 'jpeg', 'jpg', 'png', 'raw', 'tiff', 'webp'].includes(mimeType)) {
        return mimeType;
    }
    return 'png';
}
function resize(inputPath, mimeType, width, height, fit = 'contain', background) {
    return __awaiter(this, void 0, void 0, function* () {
        const format = ensureValidMimeType(mimeType.split('/')[1]);
        try {
            const outputPath = tempy_1.default.directory();
            yield image_utils_1.sharpAsync({
                input: inputPath,
                output: outputPath,
                format,
            }, [
                {
                    operation: 'flatten',
                    background,
                },
                {
                    operation: 'resize',
                    width,
                    height,
                    fit,
                    background,
                },
            ]);
            return path_1.default.join(outputPath, path_1.default.basename(inputPath));
        }
        catch ({ message }) {
            throw new Errors_1.IconError(`It was not possible to generate splash screen '${inputPath}'. ${message}`);
        }
    });
}
function retrieveIcons(manifest) {
    // Remove these items so they aren't written to disk.
    const { startupImages, icons } = manifest, config = __rest(manifest, ["startupImages", "icons"]);
    const parsedStartupImages = utils_1.toArray(startupImages);
    let parsedIcons = utils_1.toArray(icons);
    if (parsedStartupImages.length) {
        // TODO: Bacon: use all of the startup images
        const startupImage = parsedStartupImages[0];
        parsedIcons = [...parsedIcons, ...Apple_1.fromStartupImage(startupImage)];
    }
    const response = parsedIcons.map(icon => sanitizeIcon(icon));
    return [response, config];
}
exports.retrieveIcons = retrieveIcons;
function parseIconsAsync(inputIcons, fingerprint, publicPath) {
    return __awaiter(this, void 0, void 0, function* () {
        if (!inputIcons.length) {
            return {};
        }
        const icons = [];
        const assets = [];
        let promises = [];
        for (const icon of inputIcons) {
            const { sizes } = icon;
            promises = [
                ...promises,
                ...sizes.map((size) => __awaiter(this, void 0, void 0, function* () {
                    const { manifestIcon, webpackAsset } = yield processImageAsync(size, icon, fingerprint, publicPath);
                    icons.push(manifestIcon);
                    assets.push(webpackAsset);
                })),
            ];
        }
        yield Promise.all(promises);
        return {
            icons: sortByAttribute(icons, 'sizes'),
            // startupImages: icons.filter(({ isStartupImage }) => isStartupImage),
            assets: sortByAttribute(assets, 'output'),
        };
    });
}
exports.parseIconsAsync = parseIconsAsync;
function sortByAttribute(arr, key) {
    return arr
        .filter(Boolean)
        .sort((valueA, valueB) => {
        if (valueA[key] < valueB[key])
            return -1;
        else if (valueA[key] > valueB[key])
            return 1;
        return 0;
    });
}
//# sourceMappingURL=icons.js.map