
const fs = require('fs');
const tmp = require('tmp');
const imLib = require('imagemagick');
const ThreadPool = require('../threadPool.js');
const MetaStruct = require('./metaStruct.js').MetaStruct;

class ImagemagickWrapper {
    #threadPool = new ThreadPool(5);

    readMeta(path) {
        return this.#threadPool.pushTask(() => new Promise((ok, ko) => {
            imLib.identify(['-format', '%[EXIF:*]Compression=%[compression]\nWidth=%w\nHeight=%h\n', path], (err, stdout) => {
                if (err)
                    return ko(err);
                ok(stdout);
            });
        }));
    }

    convert(args) {
        return this.#threadPool.pushTask(() => new Promise((ok, ko) => {
            imLib.convert(args, (err, stdout) => {
                if (err)
                    return ko(err);
                ok(stdout);
            });
        }));
    }
}

const im = new ImagemagickWrapper();

function readMeta(path) {
    return new Promise(async (ok, ko) => {
        try {
            let stdout = await im.readMeta(path);
            var meta = {};
            for (const line of stdout.split(/\n/))
            {
                var eq_p = line.indexOf('=');
                if (eq_p === -1)
                    continue;
                var key = line.substr(0, eq_p).replace('/','-'),
                    value = line.substr(eq_p+1).trim();
                var p = key.indexOf(':');
                if (p !== -1)
                    key = key.substr(p+1);
                key = key.charAt(0).toLowerCase() + key.slice(1);
                meta[key] = value;
            }
            ok(meta);
        } catch (err) {
            console.error("readMeta from Imagemagick: ", err);
            ko(err);
        }
    });
}

function exifDate(value) {
    if (!value)
        return undefined;
    value = value.split(/ /);
    return new Date(value[0].replace(/:/g, '-')+' '+value[1]+' +0000');
}

function exifSlash(value) {
    if (!value)
        return undefined;
    if (!(/[0-9]+\/[0-9]+/).test(value))
        return value;
    return eval(value);
}

function readTags(data) {
    let result = [];
    if (data['winXP-Keywords']) {
        result = result.concat(Array.from(data["winXP-Keywords"]).filter((a, b) => !(b%2)).slice(0, -1).join("").split(";"));
    }
    return result;
}

module.exports.parse = async (fileObj, data) => {
    if (!fileObj.mimeType.startsWith('image/') || data["exifParsed"])
        return {};
    let result = new MetaStruct();
    try {
        let imdata = await readMeta(fileObj.path);
        if (!imdata)
            return {};
        result.artist = imdata.artist || undefined;
        result.setExposureProgram(Number.parseInt(imdata.exposureProgram));
        result.exposureTime = exifSlash(imdata.exposureTime);
        result.exposureTimeStr = imdata.exposureTime || undefined;
        result.dateTime = exifDate(imdata.dateTimeDigitized || imdata.dateTimeOriginal);
        result.fNumber = exifSlash(imdata.fNumber);
        result.focal = exifSlash(imdata.focalLength);
        result.lensModel = imdata.lensModel || undefined;
        result.camera = ((imdata.model || "") + (imdata.model && imdata.make ? " " : "") + (imdata.make || "")) || "";
        result.software = imdata.software || undefined;
        result.iso = Number.parseInt(imdata.photographicSensitivity) || undefined;
        result.width = imdata.width || undefined;
        result.height = imdata.height || undefined;
        result.compression = imdata.compression || undefined;
        result.orientation = imdata.orientation || undefined;
        try {
            result.setGPSInfo(imdata.gPSLatitude.split(',').map(eval), data.gPSLatitudeRef,
                imdata.gPSLongitude.split(',').map(eval), data.gPSLongitudeRef);
        }
        catch (err) {}
        result.tags = readTags(imdata);
    }
    catch (err) {
        result.imException = err;
    }
    for (let i of Object.keys(result))
        if (result[i] === undefined || result[i].length === 0)
            delete result[i];
    return { value: result };
}

module.exports.createThumbnail = async (fileObj, width, height, quality, keepMeta) => {
    if (!fileObj?.meta?.width || !fileObj?.meta?.height)
        return null;
    if (!width && !height)
        width = height = 420;
    let ratio = Math.min((width / fileObj.meta.width) || 1, (height / fileObj.meta.height) || 1, 1);
    const output = tmp.fileSync({ discardDescriptor: true });
    try {
        let args = [ fileObj.path ];
        if (keepMeta !== true)
            args.push('-strip');
        await im.convert([...args,
            '-interlace', 'Plane',
            '-auto-orient',
            '-resize', (Math.floor(fileObj.meta.width * ratio)),
            '-quality', '' + (Math.floor(Math.max(0, Math.min(100, (quality *10))))) + '%',
            '-sampling-factor', '4:2:0',
            `JPG:${output.name}`,
        ]);
    }
    catch (err) {
        console.error("Imagemagick createThumbnail error: ", err);
        throw err;
    }
    return output;
};

