
const geokit = require('geokit');
const geocoder = require('offline-geocoder')({ database: 'static/db.sqlite'});

function ExifGps(lat, latRef, lon, lonRef) {
    this.lat = null;
    this.lon = null;

    let latSign = 1;
    let lonSign = 1;

    if (!lat || !lon)
        return;
    if ((latRef && latRef !== 'N') || lat < 0) {
        lat[0] = Math.abs(lat[0]);
        latSign = -1;
    }
    if ((lonRef && lonRef !== 'E') || lon[0] < 0) {
        lon[0] = Math.abs(lon[0]);
        lonSign = -1;
    }
    this.lat = (lat[0] + lat[1] / 60 + lat[2]/3600) * latSign;
    this.lon = (lon[0] + lon[1] / 60 + lon[2]/3600) * lonSign;
    if (!this.lat || isNaN(this.lat) || !this.lon || isNaN(this.lon))
        this.lat = this.lon = null;
}

ExifGps.fromLatLng = (lat, lng) => {
    let result = new ExifGps();
    result.lat = lat;
    result.lon = lng;
    return result;
}

ExifGps.prototype.toGps = function() {
    if (this.lat === null)
        return undefined;
    return JSON.stringify([this.lat, this.lon]);
}

ExifGps.prototype.toGeoHash = function() {
    if (this.lat === null)
        return undefined;
    return geokit.hash({lat: this.lat, lng: this.lon});
}

ExifGps.prototype.toAddress = function() {
    return new Promise(async (ok, ko) => {
        if (this.lat === null)
            return ok(undefined);
        const data = await geocoder.reverse(this.lat, this.lon);
        ok({
            city: data?.name,
            admin: data?.admin1?.name,
            country: data?.country?.name
        });
    });
}

class MetaStruct {
    artist = undefined;
    exposureProgram = undefined;
    exposureTime = undefined;
    exposureTimeStr = undefined;
    dateTime = undefined;
    fNumber = undefined;
    focal = undefined;
    lensModel = undefined;
    camera = undefined;
    software = undefined;
    iso = undefined;
    width = undefined;
    height = undefined;
    compression = undefined;
    gpsLocation = undefined;
    geoHash = undefined;
    geoCountry = undefined;
    geoAdmin = undefined;
    geoCity = undefined;
    orientation = undefined;
    tags = undefined;

    // Internal
    imException = undefined;
    exifParsed = false;

    setExposureProgram(progId) {
        if (isNaN(progId) || !progId)
            this.exposureProgram = undefined;
        else
            this.exposureProgram = [ "Manual", "Normal program", "Aperture priority", "Shutter priority",
                "Creative program", "Action program", "Portrait mode", "Landscape mode"][progId];
    }

    async #updateGPS(gpsData) {
        this.gpsLocation = gpsData.toGps();
        this.geoHash = gpsData.toGeoHash();
        const address = await gpsData.toAddress();
        this.geoCountry = address?.country;
        this.geoAdmin = address?.admin;
        this.geoCity = address?.city;
    }

    setGPSInfo(latitude, latitudeRef, longitude, longitudeRef) {
        const gpsData = new ExifGps(latitude, latitudeRef, longitude, longitudeRef);
        return this.#updateGPS(gpsData);
    }

    setGPSInfoFromLatLng(latitude, longitude) {
        const gpsData = ExifGps.fromLatLng(latitude, longitude);
        return this.#updateGPS(gpsData);
    }
}

module.exports.MetaStruct = MetaStruct;

