/*
 * Copyright (C) 2016-2021 Intel Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG EXIFMaker

#include "EXIFMaker.h"

#include <limits.h>

#include <fstream>
#include <string>
#include <unordered_map>

#include "ParameterHelper.h"
#include "iutils/CameraLog.h"
#include "iutils/Utils.h"

namespace icamera {

#define DEFAULT_ISO_SPEED 100

// The property file directory
static const char* CAMERA_CACHE_DIR = "/var/cache/camera/";
/*
 * Property file defines product name and manufactory info
 * Used for EXIF header of JPEG. Format: key=value in each line
 */
static const char* CAMERA_PROPERTY_FILE = "camera.prop";

EXIFMaker::EXIFMaker()
        : mExifSize(-1),
          mInitialized(false),
          mProductName("<not_set>"),
          mManufacturerName("<not set>") {
    LOG1("@%s", __func__);

    CLEAR(mExifAttributes);
    mMakernoteSection = new unsigned char[MAKERNOTE_SECTION1_SIZE + MAKERNOTE_SECTION2_SIZE];
    readProperty();
}

EXIFMaker::~EXIFMaker() {
    LOG1("@%s", __func__);
    delete[] mMakernoteSection;
}

void EXIFMaker::readProperty() {
    LOG2("@%s", __func__);
    std::string cameraPropertyPath =
        std::string(CAMERA_CACHE_DIR) + std::string(CAMERA_PROPERTY_FILE);
    std::fstream props(cameraPropertyPath.c_str(), std::ios::in);

    if (!props.is_open()) {
        LOG2("There isn't camera property file.");
        return;
    }

    const std::string kModel = "ro.product.model";
    const std::string kManufacturer = "ro.product.manufacturer";
    const std::string kDelimiter = "=";
    std::unordered_map<std::string, std::string> properties;

    while (!props.eof()) {
        size_t pos;
        std::string line, key, value;

        std::getline(props, line);
        pos = line.find(kDelimiter);
        if (pos != std::string::npos) {
            key = line.substr(0, pos);
            value = line.substr(pos + 1);
            properties[key] = value;
            LOG2("%s, new key,value: %s,%s", __func__, key.c_str(), value.c_str());
        }
    }

    if (properties.find(kManufacturer) != properties.end()) {
        mManufacturerName = properties[kManufacturer];
    }
    if (properties.find(kModel) != properties.end()) {
        mProductName = properties[kModel];
    }
}

/**
 * Fills EXIF data after a picture has been taken to
 * record the active sensor, 3A and ISP state to EXIF metadata.
 *
 * This function is intented to set EXIF tags belonging
 * to the EXIF "Per Picture Camera Setting" group.
 *
 * @arg params active Android HAL parameters
 */
void EXIFMaker::pictureTaken(ExifMetaData* exifmetadata) {
    LOG2("@%s", __func__);

    mExifAttributes.contrast = 0;
    mExifAttributes.saturation = 0;
    mExifAttributes.sharpness = 0;
    LOG2("EXIF: contrast=%d, saturation=%d, sharpness=%d (0:normal 1:low 2:high)",
         mExifAttributes.contrast, mExifAttributes.saturation, mExifAttributes.sharpness);

    // set the exposure program mode
    icamera::camera_ae_mode_t aeMode = exifmetadata->aeMode;
    switch (aeMode) {
        case AE_MODE_MANUAL:
            mExifAttributes.exposure_program = EXIF_EXPOSURE_PROGRAM_MANUAL;
            mExifAttributes.exposure_mode = EXIF_EXPOSURE_MANUAL;
            LOG2("EXIF: Exposure Program = Manual, Exposure Mode = Manual");
            break;
        case AE_MODE_AUTO:
        default:
            mExifAttributes.exposure_program = EXIF_EXPOSURE_PROGRAM_NORMAL;
            mExifAttributes.exposure_mode = EXIF_EXPOSURE_AUTO;
            LOG2("EXIF: Exposure Program = Normal, Exposure Mode = Auto");
            break;
    }

    mExifAttributes.metering_mode = EXIF_METERING_AVERAGE;

    // white balance mode. 0: auto; 1: manual
    icamera::camera_awb_mode_t awbMode = exifmetadata->awbMode;
    LOG2("EXIF: awbMode = %d", awbMode);
    switch (awbMode) {
        case AWB_MODE_AUTO:
        case AWB_MODE_MAX:
            mExifAttributes.white_balance = EXIF_WB_AUTO;
            break;
        default:
            mExifAttributes.white_balance = EXIF_WB_MANUAL;
            break;
    }

    // light source type. Refer to EXIF V2.2
    // TBD. Now light source is only set to UNKNOWN, when WB is auto mode.
    std::unordered_map<icamera::camera_awb_mode_t, CamExifLightSourceType> awbModeLightMap = {
        {AWB_MODE_AUTO, EXIF_LIGHT_SOURCE_UNKNOWN},
        {AWB_MODE_SUNSET, EXIF_LIGHT_SOURCE_TUNGSTEN},
        {AWB_MODE_DAYLIGHT, EXIF_LIGHT_SOURCE_FINE_WEATHER},
        {AWB_MODE_PARTLY_OVERCAST, EXIF_LIGHT_SOURCE_CLOUDY_WEATHER},
        {AWB_MODE_FLUORESCENT, EXIF_LIGHT_SOURCE_FLUORESCENT},
        {AWB_MODE_INCANDESCENT, EXIF_LIGHT_SOURCE_TUNGSTEN},
        {AWB_MODE_MAX, EXIF_LIGHT_SOURCE_OTHER_LIGHT_SOURCE},
    };
    if (awbModeLightMap.find(awbMode) != awbModeLightMap.end()) {
        mExifAttributes.light_source = awbModeLightMap[awbMode];
    } else {
        mExifAttributes.light_source = EXIF_LIGHT_SOURCE_UNKNOWN;
    }

    mExifAttributes.scene_capture_type = EXIF_SCENE_STANDARD;

    int rotation = exifmetadata->mJpegSetting.orientation;
    mExifAttributes.orientation = EXIF_ORIENTATION_UP;
    if (0 == rotation)
        mExifAttributes.orientation = EXIF_ORIENTATION_UP;
    else if (90 == rotation)
        mExifAttributes.orientation = EXIF_ORIENTATION_90;
    else if (180 == rotation)
        mExifAttributes.orientation = EXIF_ORIENTATION_180;
    else if (270 == rotation)
        mExifAttributes.orientation = EXIF_ORIENTATION_270;

    // Platform has no HW rotation. No swap here
    // if (rotation % 180 == 90)
    //    swap(mExifAttributes.width, mExifAttributes.height);

    mExifAttributes.zoom_ratio.num = exifmetadata->zoomRatio;
    mExifAttributes.zoom_ratio.den = 100;
    // the unit of subjectDistance is meter, focus distance from 3A is mm.
    mExifAttributes.subject_distance.num =
        static_cast<uint32_t>(exifmetadata->currentFocusDistance);
    mExifAttributes.subject_distance.den = 1000;
    mExifAttributes.custom_rendered =
        exifmetadata->hdr ? EXIF_CUSTOM_RENDERED_HDR : EXIF_DEF_CUSTOM_RENDERED;
    LOG2("subject_distance is %d", mExifAttributes.subject_distance.num);
}

/**
 * Called when the the camera static configuration is known.
 *
 * @arg width: width of the main JPEG picture.
 * @arg height: height of the main JPEG picture.
 */
void EXIFMaker::initialize(int width, int height) {
    /* We clear the exif attributes, so we won't be using some old values
     * from a previous EXIF generation.
     */
    clear();

    // Initialize the mExifAttributes with specific values
    // time information
    time_t rawtime;
    struct tm* timeinfo;
    time(&rawtime);
    timeinfo = localtime(&rawtime);
    if (timeinfo) {
        strftime(reinterpret_cast<char*>(mExifAttributes.date_time),
                 sizeof(mExifAttributes.date_time), "%Y:%m:%d %H:%M:%S", timeinfo);
        // fields: tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst,
        // tm_gmtoff, tm_zone
    } else {
        LOGW("nullptr timeinfo from localtime(), using defaults...");
        struct tm tmpTime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "UTC"};
        strftime(reinterpret_cast<char*>(mExifAttributes.date_time),
                 sizeof(mExifAttributes.date_time), "%Y:%m:%d %H:%M:%S", &tmpTime);
    }

    // set default subsec time to 1000
    const char subsecTime[] = "1000";
    MEMCPY_S(reinterpret_cast<char*>(mExifAttributes.subsec_time),
             sizeof(mExifAttributes.subsec_time), subsecTime, sizeof(subsecTime));

    // conponents configuration.
    // Default = 4 5 6 0(if RGB uncompressed), 1 2 3 0(other cases)
    // 0 = does not exist; 1 = Y; 2 = Cb; 3 = Cr; 4 = R; 5 = G; 6 = B; other = reserved
    mExifAttributes.components_configuration[0] = 1;
    mExifAttributes.components_configuration[1] = 2;
    mExifAttributes.components_configuration[2] = 3;
    mExifAttributes.components_configuration[3] = 0;

    // set default values for fnumber and focal length
    // (see EXIFMaker::setDriverData() how to override these)
    mExifAttributes.fnumber.num = EXIF_DEF_FNUMBER_NUM;
    mExifAttributes.fnumber.den = EXIF_DEF_FNUMBER_DEN;
    mExifAttributes.focal_length.num = EXIF_DEF_FOCAL_LEN_NUM;
    mExifAttributes.focal_length.den = EXIF_DEF_FOCAL_LEN_DEN;

    mExifAttributes.iso_speed_rating = DEFAULT_ISO_SPEED;

    mExifAttributes.aperture.den = EXIF_DEF_APEX_DEN;
    mExifAttributes.aperture.num = EXIF_DEF_APEX_NUM;
    // max aperture. the smallest F number of the lens. unit is APEX value.
    mExifAttributes.max_aperture.num = mExifAttributes.aperture.num;
    mExifAttributes.max_aperture.den = mExifAttributes.aperture.den;

    // subject distance,    0 means distance unknown; (~0) means infinity.
    mExifAttributes.subject_distance.num = EXIF_DEF_SUBJECT_DISTANCE_UNKNOWN;
    mExifAttributes.subject_distance.den = 1;

    // light source, 0 means light source unknown
    mExifAttributes.light_source = 0;

    // gain control, 0 = none;
    // 1 = low gain up; 2 = high gain up; 3 = low gain down; 4 = high gain down
    mExifAttributes.gain_control = 0;

    // contrast, 0 = normal; 1 = soft; 2 = hard; other = reserved
    mExifAttributes.contrast = EXIF_CONTRAST_NORMAL;

    // saturation, 0 = normal; 1 = Low saturation; 2 = High saturation; other = reserved
    mExifAttributes.saturation = EXIF_SATURATION_NORMAL;

    // sharpness, 0 = normal; 1 = soft; 2 = hard; other = reserved
    mExifAttributes.sharpness = EXIF_SHARPNESS_NORMAL;

    // the picture's width and height
    mExifAttributes.width = width;
    mExifAttributes.height = height;

    mExifAttributes.orientation = 1;

    mExifAttributes.custom_rendered = EXIF_DEF_CUSTOM_RENDERED;

    // metering mode, 0 = normal; 1 = soft; 2 = hard; other = reserved
    mExifAttributes.metering_mode = EXIF_METERING_UNKNOWN;
    mInitialized = true;
}

void EXIFMaker::initializeLocation(ExifMetaData* metadata) {
    // GIS information
    bool gpsEnabled = false;
    double latitude = metadata->mGpsSetting.latitude;
    double longitude = metadata->mGpsSetting.longitude;
    double altitude = metadata->mGpsSetting.altitude;
    long timestamp = metadata->mGpsSetting.gpsTimeStamp;
    char* pprocmethod = metadata->mGpsSetting.gpsProcessingMethod;

    // check whether the GIS Information is valid
    if (!(latitude >= -EPSILON && latitude <= EPSILON) ||
        !(longitude >= -EPSILON && longitude <= EPSILON) ||
        !(altitude >= -EPSILON && altitude <= EPSILON) || (timestamp != 0) ||
        (strlen(pprocmethod) != 0))
        gpsEnabled = true;

    mExifAttributes.enableGps = 0;
    LOG1("@%s,EXIF: gpsEnabled: %d", __func__, gpsEnabled);

    // the version is given as 2.2.0.0, it is mandatory when GPSInfo tag is present
    if (gpsEnabled) {
        const unsigned char gpsversion[4] = {0x02, 0x02, 0x00, 0x00};
        MEMCPY_S(mExifAttributes.gps_version_id, sizeof(mExifAttributes.gps_version_id), gpsversion,
                 sizeof(gpsversion));
    } else {
        return;
    }

    // latitude, for example, 39.904214 degrees, N
    if (latitude > 0)
        MEMCPY_S(mExifAttributes.gps_latitude_ref, sizeof(mExifAttributes.gps_latitude_ref), "N",
                 sizeof(mExifAttributes.gps_latitude_ref));
    else
        MEMCPY_S(mExifAttributes.gps_latitude_ref, sizeof(mExifAttributes.gps_latitude_ref), "S",
                 sizeof(mExifAttributes.gps_latitude_ref));

    latitude = fabs(latitude);
    mExifAttributes.gps_latitude[0].num = (uint32_t)latitude;
    mExifAttributes.gps_latitude[0].den = 1;
    mExifAttributes.gps_latitude[1].num =
        (uint32_t)((latitude - mExifAttributes.gps_latitude[0].num) * 60);
    mExifAttributes.gps_latitude[1].den = 1;
    mExifAttributes.gps_latitude[2].num =
        (uint32_t)(((latitude - mExifAttributes.gps_latitude[0].num) * 60 -
                    mExifAttributes.gps_latitude[1].num) *
                   60 * 100);
    mExifAttributes.gps_latitude[2].den = 100;
    mExifAttributes.enableGps |= EXIF_GPS_LATITUDE;
    LOG1("EXIF: latitude, ref:%s, dd:%d, mm:%d, ss:%d", mExifAttributes.gps_latitude_ref,
         mExifAttributes.gps_latitude[0].num, mExifAttributes.gps_latitude[1].num,
         mExifAttributes.gps_latitude[2].num);

    // longitude, for example, 116.407413 degrees, E
    if (longitude > 0)
        MEMCPY_S(mExifAttributes.gps_longitude_ref, sizeof(mExifAttributes.gps_longitude_ref), "E",
                 sizeof(mExifAttributes.gps_longitude_ref));
    else
        MEMCPY_S(mExifAttributes.gps_longitude_ref, sizeof(mExifAttributes.gps_longitude_ref), "W",
                 sizeof(mExifAttributes.gps_longitude_ref));
    longitude = fabs(longitude);
    mExifAttributes.gps_longitude[0].num = (uint32_t)longitude;
    mExifAttributes.gps_longitude[0].den = 1;
    mExifAttributes.gps_longitude[1].num =
        (uint32_t)((longitude - mExifAttributes.gps_longitude[0].num) * 60);
    mExifAttributes.gps_longitude[1].den = 1;
    mExifAttributes.gps_longitude[2].num =
        (uint32_t)(((longitude - mExifAttributes.gps_longitude[0].num) * 60 -
                    mExifAttributes.gps_longitude[1].num) *
                   60 * 100);
    mExifAttributes.gps_longitude[2].den = 100;
    mExifAttributes.enableGps |= EXIF_GPS_LONGITUDE;
    LOG1("EXIF: longitude, ref:%s, dd:%d, mm:%d, ss:%d", mExifAttributes.gps_longitude_ref,
         mExifAttributes.gps_longitude[0].num, mExifAttributes.gps_longitude[1].num,
         mExifAttributes.gps_longitude[2].num);

    // altitude
    // altitude, sea level or above sea level, set it to 0; below sea level, set it to 1
    mExifAttributes.gps_altitude_ref = ((altitude > 0) ? 0 : 1);
    altitude = fabs(altitude);
    mExifAttributes.gps_altitude.num = (uint32_t)altitude;
    mExifAttributes.gps_altitude.den = 1;
    mExifAttributes.enableGps |= EXIF_GPS_ALTITUDE;
    LOG1("EXIF: altitude, ref:%d, height:%d", mExifAttributes.gps_altitude_ref,
         mExifAttributes.gps_altitude.num);

    // timestamp
    if (timestamp >= LONG_MAX || timestamp <= LONG_MIN) {
        timestamp = 0;
        LOGW("invalid timestamp was provided, defaulting to 0 (i.e. 1970)");
    }
    struct tm time;
    gmtime_r(&timestamp, &time);
    time.tm_year += 1900;
    time.tm_mon += 1;
    mExifAttributes.gps_timestamp[0].num = time.tm_hour;
    mExifAttributes.gps_timestamp[0].den = 1;
    mExifAttributes.gps_timestamp[1].num = time.tm_min;
    mExifAttributes.gps_timestamp[1].den = 1;
    mExifAttributes.gps_timestamp[2].num = time.tm_sec;
    mExifAttributes.gps_timestamp[2].den = 1;
    mExifAttributes.enableGps |= EXIF_GPS_TIMESTAMP;

    snprintf(reinterpret_cast<char*>(mExifAttributes.gps_datestamp),
             sizeof(mExifAttributes.gps_datestamp), "%04d:%02d:%02d", time.tm_year, time.tm_mon,
             time.tm_mday);

    LOG1("EXIF: timestamp, year:%d,mon:%d,day:%d,hour:%d,min:%d,sec:%d", time.tm_year, time.tm_mon,
         time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec);

    // processing method
    MEMCPY_S(mExifAttributes.gps_processing_method, sizeof(mExifAttributes.gps_processing_method),
             metadata->mGpsSetting.gpsProcessingMethod,
             sizeof(metadata->mGpsSetting.gpsProcessingMethod));
    mExifAttributes.gps_processing_method[sizeof(mExifAttributes.gps_processing_method) - 1] = 0;

    mExifAttributes.enableGps |= EXIF_GPS_PROCMETHOD;
    LOG1("EXIF: GPS processing method:%s", mExifAttributes.gps_processing_method);
}

void EXIFMaker::setSensorAeConfig(const Parameters& params) {
    LOG1("@%s", __func__);

    int64_t expTime = 0;
    params.getExposureTime(expTime);

    if (expTime > 0) {
        // EXIF exposure rational value is in seconds and the unit of exposure time in 3A is usecs
        mExifAttributes.exposure_time.num = expTime;
        mExifAttributes.exposure_time.den = 1000000;
        uint32_t tv = APEX_EXPOSURE_TO_SHUTTER(static_cast<double>(expTime) /
                                               mExifAttributes.exposure_time.den);
        mExifAttributes.shutter_speed.num = tv * 65536;
        mExifAttributes.shutter_speed.den = 65536;
    } else {
        mExifAttributes.exposure_time.num = 0;
        mExifAttributes.exposure_time.den = 1;
        mExifAttributes.shutter_speed.num = 0;
        mExifAttributes.shutter_speed.den = 1;
    }

    float stepEv = 1 / 3.0f;
    icamera::camera_rational_t aeCompensationStep;
    if (params.getAeCompensationStep(aeCompensationStep) == 0) {
        stepEv = static_cast<float>(aeCompensationStep.numerator) / aeCompensationStep.denominator;
    }

    int32_t ev = 0;
    float evBias = 0;
    if (params.getAeCompensation(ev) == 0) {
        evBias = static_cast<float>(ev) * stepEv;
    }

    // exposure bias. unit is APEX value. -99.99 to 99.99
    const int evLowerBound = -100, evUpperBound = 100;
    if (evBias > evLowerBound && evBias < evUpperBound) {
        mExifAttributes.exposure_bias.num = static_cast<int>(evBias) * 100;
        mExifAttributes.exposure_bias.den = 100;
        LOG2("EXIF: Ev = %.2f", evBias);
    } else {
        mExifAttributes.exposure_bias.num = 0;
        mExifAttributes.exposure_bias.den = 100;
        LOG2("EXIF: Invalid Ev!");
    }

    int32_t iso;
    mExifAttributes.iso_speed_rating = DEFAULT_ISO_SPEED;
    if (params.getSensitivityIso(iso) == 0) {
        mExifAttributes.iso_speed_rating = iso;
    }

    LOG2("EXIF: ISO=%d", mExifAttributes.iso_speed_rating);
    LOG2("EXIF: shutter speed=%u/%u", mExifAttributes.shutter_speed.num,
         mExifAttributes.shutter_speed.den);
    LOG2("EXIF: exposure time=%u/%u", mExifAttributes.exposure_time.num,
         mExifAttributes.exposure_time.den);

    if (mExifAttributes.fnumber.den > 0 && expTime > 0 && mExifAttributes.iso_speed_rating > 0) {
        // 'dAv + dTv = dSv + dBv' based on the equation of APEX system
        double dAv = APEX_FNUM_TO_APERTURE(static_cast<double>(mExifAttributes.fnumber.num) /
                                           mExifAttributes.fnumber.den);
        double dTv = APEX_EXPOSURE_TO_SHUTTER(static_cast<double>(expTime) / 1000000);
        double dSv = APEX_ISO_TO_FILMSENSITIVITY(mExifAttributes.iso_speed_rating);
        mExifAttributes.brightness.num = (dAv + dTv - dSv) * EXIF_DEF_BRIGHTNESSVALUE_DEN;
        mExifAttributes.brightness.den = EXIF_DEF_BRIGHTNESSVALUE_DEN;
        LOG2("EXIF: brightness = %d / %d", mExifAttributes.brightness.num,
             mExifAttributes.brightness.den);
    }
}

/*
 * more secure attribute copy routine.
 * \param dst pointer to dst buffer
 * \param dstSize dst buffer size
 * \param src pointer to src character buffer
 * \param srcLength src buffer length in characters, not including null byte
 */
void EXIFMaker::copyAttribute(uint8_t* dst, size_t dstSize, const char* src, size_t srcLength) {
    size_t dstMaxLength = dstSize - 1;            // leave space for null
    MEMCPY_S(dst, dstMaxLength, src, srcLength);  // copy chars (not null)
    // add null termination
    size_t len = std::min(dstMaxLength, srcLength);
    dst[len] = '\0';
}

void EXIFMaker::clear() {
    LOG1("@%s", __func__);
    // Reset all the attributes
    CLEAR(mExifAttributes);
    // Initialize the common values
    mExifAttributes.enableThumb = false;
    copyAttribute(mExifAttributes.image_description, sizeof(mExifAttributes.image_description),
                  EXIF_DEF_IMAGE_DESCRIPTION, strlen(EXIF_DEF_IMAGE_DESCRIPTION));

    copyAttribute(mExifAttributes.maker, sizeof(mExifAttributes.maker), mManufacturerName.c_str(),
                  strlen(mManufacturerName.c_str()));

    copyAttribute(mExifAttributes.model, sizeof(mExifAttributes.model), mProductName.c_str(),
                  strlen(mProductName.c_str()));

    copyAttribute(mExifAttributes.software, sizeof(mExifAttributes.software), EXIF_DEF_SOFTWARE,
                  strlen(EXIF_DEF_SOFTWARE));

    copyAttribute(mExifAttributes.exif_version, sizeof(mExifAttributes.exif_version),
                  EXIF_DEF_EXIF_VERSION, strlen(EXIF_DEF_EXIF_VERSION));

    copyAttribute(mExifAttributes.flashpix_version, sizeof(mExifAttributes.flashpix_version),
                  EXIF_DEF_FLASHPIXVERSION, strlen(EXIF_DEF_FLASHPIXVERSION));

    // initially, set default flash
    mExifAttributes.flash = EXIF_DEF_FLASH;

    // normally it is sRGB, 1 means sRGB. FFFF.H means uncalibrated
    mExifAttributes.color_space = EXIF_DEF_COLOR_SPACE;

    // the number of pixels per ResolutionUnit in the w or h direction
    // 72 means the image resolution is unknown
    mExifAttributes.x_resolution.num = EXIF_DEF_RESOLUTION_NUM;
    mExifAttributes.x_resolution.den = EXIF_DEF_RESOLUTION_DEN;
    mExifAttributes.y_resolution.num = mExifAttributes.x_resolution.num;
    mExifAttributes.y_resolution.den = mExifAttributes.x_resolution.den;
    // resolution unit, 2 means inch
    mExifAttributes.resolution_unit = EXIF_DEF_RESOLUTION_UNIT;
    // when thumbnail uses JPEG compression, this tag 103H's value is set to 6
    mExifAttributes.compression_scheme = EXIF_DEF_COMPRESSION;

    // the TIFF default is 1 (centered)
    mExifAttributes.ycbcr_positioning = EXIF_DEF_YCBCR_POSITIONING;

    // Clear the Intel 3A Makernote information
    mExifAttributes.makerNoteData = mMakernoteSection;
    mExifAttributes.makerNoteDataSize = 0;
    mExifAttributes.makernoteToApp2 = ENABLE_APP2_MARKER;

    mInitialized = false;
}

void EXIFMaker::enableFlash(bool enable, int8_t aeMode, int8_t flashMode) {
    mExifAttributes.flash = EXIF_DEF_FLASH;
}

void EXIFMaker::setThumbnail(unsigned char* data, size_t size, int width, int height) {
    LOG1("@%s: data = %p, size = %zu", __func__, data, size);
    mExifAttributes.enableThumb = true;
    mExifAttributes.widthThumb = width;
    mExifAttributes.heightThumb = height;
    if (mEncoder.setThumbData(data, size) != EXIF_SUCCESS) {
        LOGE("Error in setting EXIF thumbnail");
    }
}

bool EXIFMaker::isThumbnailSet() const {
    return mEncoder.isThumbDataSet();
}

size_t EXIFMaker::makeExif(unsigned char* data) {
    LOG2("@%s", __func__);
    CheckAndLogError(!data, 0, "nullptr passed for EXIF. Cannot generate EXIF!");

    if (mEncoder.makeExif(data, &mExifAttributes, &mExifSize) == EXIF_SUCCESS) {
        LOG1("Generated EXIF (@%p) of size: %zu", data, mExifSize);
        return mExifSize;
    }
    return 0;
}

void EXIFMaker::setMaker(const char* data) {
    LOG2("@%s: data = %s", __func__, data);

    snprintf((char*)mExifAttributes.maker, sizeof(mExifAttributes.maker), "%s", data);
}

void EXIFMaker::setModel(const char* data) {
    LOG2("@%s: data = %s", __func__, data);

    snprintf((char*)mExifAttributes.model, sizeof(mExifAttributes.model), "%s", data);
}

void EXIFMaker::setSoftware(const char* data) {
    LOG2("@%s: data = %s", __func__, data);

    snprintf((char*)mExifAttributes.software, sizeof(mExifAttributes.software), "%s", data);
}

void EXIFMaker::saveMakernote(const Parameters& params) {
    unsigned int size = sizeof(unsigned char) * (MAKERNOTE_SECTION1_SIZE + MAKERNOTE_SECTION2_SIZE);
    if (params.getMakernoteData(mMakernoteSection, &size) == OK) {
        mExifAttributes.makerNoteDataSize = size;
    }
}

void EXIFMaker::updateSensorInfo(const Parameters& params) {
    float focal = 0.0;
    params.getFocalLength(focal);

    if (focal < EPSILON) {
        // Focal length is not supported, set to default value
        const icamera::CameraMetadata& meta = icamera::ParameterHelper::getMetadata(params);

        icamera_metadata_ro_entry entry = meta.find(CAMERA_LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
        if (entry.count >= 1) {
            focal = entry.data.f[0];
        }
    }

    LOG2("focal length is %f", focal);
    mExifAttributes.focal_length.num = focal * mExifAttributes.focal_length.den;
    float aperture = 0.0;
    params.getAperture(aperture);
    mExifAttributes.aperture.num = aperture * mExifAttributes.aperture.den;

    mExifAttributes.fnumber.num = aperture * mExifAttributes.aperture.den;
    mExifAttributes.fnumber.den = mExifAttributes.aperture.den;
}

}  // namespace icamera
