
const tmp = require('tmp');
const Sharp = require('sharp');
const ThreadPool = require('../threadPool.js');
const MetaStruct = require('./metaStruct.js').MetaStruct;
const exifReader = require('exif-reader');

class SharpWrapper {
    #threadPool = new ThreadPool(15);

    parse(path) {
        return this.#threadPool.pushTask(() =>
            new Sharp(path)
                .metadata()
        );
    }

    async convert(fileObj, width, height, quality, keepMeta) {
        const output = tmp.fileSync({ discardDescriptor: true });

        let result = null;
        try {
            result = await this.#threadPool.pushTask(async () => {
                let sharpInst = new Sharp(fileObj.path);
                if (keepMeta)
                    sharpInst = await sharpInst.keepMetadata();
                return sharpInst
                    .rotate()
                    .resize(width, height, { fit: "inside", withoutEnlargement: true })
                    .jpeg({ quality: quality *10 })
                    .toFile(output.name)
            });
        } catch (err) {
            console.error("Sharp::createThumbnail: ", err);
            output.removeCallback();
            result = null;
        }
        return result ? output : null;
    }
}

const sh = new SharpWrapper();

async function sharpParse(path) {
    let rawData = await sh.parse(path);
    let result = new MetaStruct();

    result.compression = rawData.format;
    result.width = rawData.width;
    result.height = rawData.height;
    result.exifParsed = 0;

    if (!rawData.exif)
        return;
    let exifData = exifReader(rawData.exif);
    result.setExposureProgram(exifData?.Photo?.ExposureProgram);
    result.exposureTime = exifData?.Photo?.ExposureTime || undefined;
    result.exposureTimeStr = undefined;
    result.dateTime = exifData?.Photo?.DateTimeOriginal || exifData?.Image?.DateTime || undefined;
    result.fNumber = exifData?.Photo?.FNumber || undefined;
    result.focal = exifData?.Photo?.FocalLength || undefined;
    result.lensModel = exifData?.Photo?.LensModel || undefined;
    result.camera = ((exifData?.Image?.Model || "") + (exifData?.Image?.Model && exifData?.Image.Make ? " " : "") + (exifData?.Image?.Make || "")) || undefined;
    result.artist = exifData?.Image?.Artist || undefined;
    result.software = exifData?.Image?.Software || undefined;
    result.iso = exifData?.Photo?.ISOSpeedRatings || undefined;
    result.orientation = exifData?.Image?.Orientation || undefined;
    result.tags = [];
    try {
        if (exifData?.Image?.XPKeywords)
            result.tags = Buffer.from(exifData.Image.XPKeywords.filter(x => !!x)).toString().split(";");
    } catch (err) {}

    if (exifData?.GPSInfo?.GPSLatitudeRef && exifData?.GPSInfo?.GPSLatitude &&
        exifData?.GPSInfo?.GPSLongitudeRef && exifData?.GPSInfo?.GPSLongitude) {
        await result.setGPSInfo(exifData?.GPSInfo?.GPSLatitude, exifData?.GPSInfo?.GPSLatitudeRef, exifData?.GPSInfo?.GPSLongitude, exifData?.GPSInfo?.GPSLongitudeRef);
    }
    result.exifParsed = 1;
    return result;
}

module.exports.parse = async (fileObj) => {
    try {
        return { value: await sharpParse(fileObj.path) };
    } catch (err) {
        return { reason: err };
    }
}

module.exports.createThumbnail = (fileObj, width, height, quality, keepMeta) => {
    return sh.convert(fileObj, width, height, quality, keepMeta);
};

