/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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 "CamComm1.0-Exif"
#define ATRACE_TAG ATRACE_TAG_CAMERA
// #define LOG_NDEBUG 0

#include <android/log.h>

#include <inttypes.h>
#include <math.h>
#include <stdint.h>
#include <string>
#include <vector>

#include "Exif.h"

extern "C" {
#include <libexif/exif-data.h>
}

namespace std {

template <>
struct default_delete<ExifEntry> {
    inline void operator()(ExifEntry* entry) const { exif_entry_unref(entry); }
};

}  // namespace std

namespace android {
namespace hardware {
namespace camera {
namespace common {
namespace helper {

class ExifUtilsImpl : public ExifUtils {
  public:
    ExifUtilsImpl();

    virtual ~ExifUtilsImpl();

    // Initialize() can be called multiple times. The setting of Exif tags will be
    // cleared.
    virtual bool initialize();

    // set all known fields from a metadata structure
    virtual bool setFromMetadata(const CameraMetadata& metadata, const size_t imageWidth,
                                 const size_t imageHeight);

    // sets the len aperture.
    // Returns false if memory allocation fails.
    virtual bool setAperture(uint32_t numerator, uint32_t denominator);

    // sets the value of brightness.
    // Returns false if memory allocation fails.
    virtual bool setBrightness(int32_t numerator, int32_t denominator);

    // sets the color space.
    // Returns false if memory allocation fails.
    virtual bool setColorSpace(uint16_t color_space);

    // sets the information to compressed data.
    // Returns false if memory allocation fails.
    virtual bool setComponentsConfiguration(const std::string& components_configuration);

    // sets the compression scheme used for the image data.
    // Returns false if memory allocation fails.
    virtual bool setCompression(uint16_t compression);

    // sets image contrast.
    // Returns false if memory allocation fails.
    virtual bool setContrast(uint16_t contrast);

    // sets the date and time of image last modified. It takes local time. The
    // name of the tag is DateTime in IFD0.
    // Returns false if memory allocation fails.
    virtual bool setDateTime(const struct tm& t);

    // sets the image description.
    // Returns false if memory allocation fails.
    virtual bool setDescription(const std::string& description);

    // sets the digital zoom ratio. If the numerator is 0, it means digital zoom
    // was not used.
    // Returns false if memory allocation fails.
    virtual bool setDigitalZoomRatio(uint32_t numerator, uint32_t denominator);

    // sets the exposure bias.
    // Returns false if memory allocation fails.
    virtual bool setExposureBias(int32_t numerator, int32_t denominator);

    // sets the exposure mode set when the image was shot.
    // Returns false if memory allocation fails.
    virtual bool setExposureMode(uint16_t exposure_mode);

    // sets the program used by the camera to set exposure when the picture is
    // taken.
    // Returns false if memory allocation fails.
    virtual bool setExposureProgram(uint16_t exposure_program);

    // sets the exposure time, given in seconds.
    // Returns false if memory allocation fails.
    virtual bool setExposureTime(uint32_t numerator, uint32_t denominator);

    // sets the status of flash.
    // Returns false if memory allocation fails.
    virtual bool setFlash(uint16_t flash);

    // sets the F number.
    // Returns false if memory allocation fails.
    virtual bool setFNumber(uint32_t numerator, uint32_t denominator);

    // sets the focal length of lens used to take the image in millimeters.
    // Returns false if memory allocation fails.
    virtual bool setFocalLength(uint32_t numerator, uint32_t denominator);

    // sets the degree of overall image gain adjustment.
    // Returns false if memory allocation fails.
    virtual bool setGainControl(uint16_t gain_control);

    // sets the altitude in meters.
    // Returns false if memory allocation fails.
    virtual bool setGpsAltitude(double altitude);

    // sets the latitude with degrees minutes seconds format.
    // Returns false if memory allocation fails.
    virtual bool setGpsLatitude(double latitude);

    // sets the longitude with degrees minutes seconds format.
    // Returns false if memory allocation fails.
    virtual bool setGpsLongitude(double longitude);

    // sets GPS processing method.
    // Returns false if memory allocation fails.
    virtual bool setGpsProcessingMethod(const std::string& method);

    // sets GPS date stamp and time stamp (atomic clock). It takes UTC time.
    // Returns false if memory allocation fails.
    virtual bool setGpsTimestamp(const struct tm& t);

    // sets the length (number of rows) of main image.
    // Returns false if memory allocation fails.
    virtual bool setImageHeight(uint32_t length);

    // sets the width (number of columes) of main image.
    // Returns false if memory allocation fails.
    virtual bool setImageWidth(uint32_t width);

    // sets the ISO speed.
    // Returns false if memory allocation fails.
    virtual bool setIsoSpeedRating(uint16_t iso_speed_ratings);

    // sets the kind of light source.
    // Returns false if memory allocation fails.
    virtual bool setLightSource(uint16_t light_source);

    // sets the smallest F number of the lens.
    // Returns false if memory allocation fails.
    virtual bool setMaxAperture(uint32_t numerator, uint32_t denominator);

    // sets the metering mode.
    // Returns false if memory allocation fails.
    virtual bool setMeteringMode(uint16_t metering_mode);

    // sets image orientation.
    // Returns false if memory allocation fails.
    virtual bool setOrientation(uint16_t orientation);

    // sets the unit for measuring XResolution and YResolution.
    // Returns false if memory allocation fails.
    virtual bool setResolutionUnit(uint16_t resolution_unit);

    // sets image saturation.
    // Returns false if memory allocation fails.
    virtual bool setSaturation(uint16_t saturation);

    // sets the type of scene that was shot.
    // Returns false if memory allocation fails.
    virtual bool setSceneCaptureType(uint16_t type);

    // sets image sharpness.
    // Returns false if memory allocation fails.
    virtual bool setSharpness(uint16_t sharpness);

    // sets the shutter speed.
    // Returns false if memory allocation fails.
    virtual bool setShutterSpeed(int32_t numerator, int32_t denominator);

    // sets the distance to the subject, given in meters.
    // Returns false if memory allocation fails.
    virtual bool setSubjectDistance(uint32_t numerator, uint32_t denominator);

    // sets the fractions of seconds for the <DateTime> tag.
    // Returns false if memory allocation fails.
    virtual bool setSubsecTime(const std::string& subsec_time);

    // sets the white balance mode set when the image was shot.
    // Returns false if memory allocation fails.
    virtual bool setWhiteBalance(uint16_t white_balance);

    // sets the number of pixels per resolution unit in the image width.
    // Returns false if memory allocation fails.
    virtual bool setXResolution(uint32_t numerator, uint32_t denominator);

    // sets the position of chrominance components in relation to the luminance
    // component.
    // Returns false if memory allocation fails.
    virtual bool setYCbCrPositioning(uint16_t ycbcr_positioning);

    // sets the number of pixels per resolution unit in the image length.
    // Returns false if memory allocation fails.
    virtual bool setYResolution(uint32_t numerator, uint32_t denominator);

    // sets the manufacturer of camera.
    // Returns false if memory allocation fails.
    virtual bool setMake(const std::string& make);

    // sets the model number of camera.
    // Returns false if memory allocation fails.
    virtual bool setModel(const std::string& model);

    // Generates APP1 segment.
    // Returns false if generating APP1 segment fails.
    virtual bool generateApp1(const void* thumbnail_buffer, uint32_t size);

    // Gets buffer of APP1 segment. This method must be called only after calling
    // GenerateAPP1().
    virtual const uint8_t* getApp1Buffer();

    // Gets length of APP1 segment. This method must be called only after calling
    // GenerateAPP1().
    virtual unsigned int getApp1Length();

  protected:
    // sets the version of this standard supported.
    // Returns false if memory allocation fails.
    virtual bool setExifVersion(const std::string& exif_version);

    // Resets the pointers and memories.
    virtual void reset();

    // Adds a variable length tag to |exif_data_|. It will remove the original one
    // if the tag exists.
    // Returns the entry of the tag. The reference count of returned ExifEntry is
    // two.
    virtual std::unique_ptr<ExifEntry> addVariableLengthEntry(ExifIfd ifd, ExifTag tag,
                                                              ExifFormat format,
                                                              uint64_t components,
                                                              unsigned int size);

    // Adds a entry of |tag| in |exif_data_|. It won't remove the original one if
    // the tag exists.
    // Returns the entry of the tag. It adds one reference count to returned
    // ExifEntry.
    virtual std::unique_ptr<ExifEntry> addEntry(ExifIfd ifd, ExifTag tag);

    // Helpe functions to add exif data with different types.
    virtual bool setShort(ExifIfd ifd, ExifTag tag, uint16_t value, const std::string& msg);

    virtual bool setLong(ExifIfd ifd, ExifTag tag, uint32_t value, const std::string& msg);

    virtual bool setRational(ExifIfd ifd, ExifTag tag, uint32_t numerator, uint32_t denominator,
                             const std::string& msg);

    virtual bool setSRational(ExifIfd ifd, ExifTag tag, int32_t numerator, int32_t denominator,
                              const std::string& msg);

    virtual bool setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::string& buffer,
                           const std::string& msg);

    // Destroys the buffer of APP1 segment if exists.
    virtual void destroyApp1();

    // The Exif data (APP1). Owned by this class.
    ExifData* exif_data_;
    // The raw data of APP1 segment. It's allocated by ExifMem in |exif_data_| but
    // owned by this class.
    uint8_t* app1_buffer_;
    // The length of |app1_buffer_|.
    unsigned int app1_length_;
};

#define SET_SHORT(ifd, tag, value)                                  \
    do {                                                            \
        if (setShort(ifd, tag, value, #tag) == false) return false; \
    } while (0);

#define SET_LONG(ifd, tag, value)                                  \
    do {                                                           \
        if (setLong(ifd, tag, value, #tag) == false) return false; \
    } while (0);

#define SET_RATIONAL(ifd, tag, numerator, denominator)                                  \
    do {                                                                                \
        if (setRational(ifd, tag, numerator, denominator, #tag) == false) return false; \
    } while (0);

#define SET_SRATIONAL(ifd, tag, numerator, denominator)                                  \
    do {                                                                                 \
        if (setSRational(ifd, tag, numerator, denominator, #tag) == false) return false; \
    } while (0);

#define SET_STRING(ifd, tag, format, buffer)                                  \
    do {                                                                      \
        if (setString(ifd, tag, format, buffer, #tag) == false) return false; \
    } while (0);

// This comes from the Exif Version 2.2 standard table 6.
const char gExifAsciiPrefix[] = {0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0};

static void setLatitudeOrLongitudeData(unsigned char* data, double num) {
    // Take the integer part of |num|.
    ExifLong degrees = static_cast<ExifLong>(num);
    ExifLong minutes = static_cast<ExifLong>(60 * (num - degrees));
    ExifLong microseconds = static_cast<ExifLong>(3600000000u * (num - degrees - minutes / 60.0));
    exif_set_rational(data, EXIF_BYTE_ORDER_INTEL, {degrees, 1});
    exif_set_rational(data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL, {minutes, 1});
    exif_set_rational(data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
                      {microseconds, 1000000});
}

ExifUtils* ExifUtils::create() {
    return new ExifUtilsImpl();
}

ExifUtils::~ExifUtils() {}

ExifUtilsImpl::ExifUtilsImpl() : exif_data_(nullptr), app1_buffer_(nullptr), app1_length_(0) {}

ExifUtilsImpl::~ExifUtilsImpl() {
    reset();
}

bool ExifUtilsImpl::initialize() {
    reset();
    exif_data_ = exif_data_new();
    if (exif_data_ == nullptr) {
        ALOGE("%s: allocate memory for exif_data_ failed", __FUNCTION__);
        return false;
    }
    // set the image options.
    exif_data_set_option(exif_data_, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
    exif_data_set_data_type(exif_data_, EXIF_DATA_TYPE_COMPRESSED);
    exif_data_set_byte_order(exif_data_, EXIF_BYTE_ORDER_INTEL);

    // set exif version to 2.2.
    if (!setExifVersion("0220")) {
        return false;
    }

    return true;
}

bool ExifUtilsImpl::setAperture(uint32_t numerator, uint32_t denominator) {
    SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_APERTURE_VALUE, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::setBrightness(int32_t numerator, int32_t denominator) {
    SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_BRIGHTNESS_VALUE, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::setColorSpace(uint16_t color_space) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_COLOR_SPACE, color_space);
    return true;
}

bool ExifUtilsImpl::setComponentsConfiguration(const std::string& components_configuration) {
    SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_COMPONENTS_CONFIGURATION, EXIF_FORMAT_UNDEFINED,
               components_configuration);
    return true;
}

bool ExifUtilsImpl::setCompression(uint16_t compression) {
    SET_SHORT(EXIF_IFD_0, EXIF_TAG_COMPRESSION, compression);
    return true;
}

bool ExifUtilsImpl::setContrast(uint16_t contrast) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_CONTRAST, contrast);
    return true;
}

bool ExifUtilsImpl::setDateTime(const struct tm& t) {
    // The length is 20 bytes including NULL for termination in Exif standard.
    char str[20];
    int result = snprintf(str, sizeof(str), "%04i:%02i:%02i %02i:%02i:%02i", t.tm_year + 1900,
                          t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
    if (result != sizeof(str) - 1) {
        ALOGW("%s: Input time is invalid", __FUNCTION__);
        return false;
    }
    std::string buffer(str);
    SET_STRING(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII, buffer);
    SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, EXIF_FORMAT_ASCII, buffer);
    SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, EXIF_FORMAT_ASCII, buffer);
    return true;
}

bool ExifUtilsImpl::setDescription(const std::string& description) {
    SET_STRING(EXIF_IFD_0, EXIF_TAG_IMAGE_DESCRIPTION, EXIF_FORMAT_ASCII, description);
    return true;
}

bool ExifUtilsImpl::setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) {
    SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_DIGITAL_ZOOM_RATIO, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::setExposureBias(int32_t numerator, int32_t denominator) {
    SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_BIAS_VALUE, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::setExposureMode(uint16_t exposure_mode) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_MODE, exposure_mode);
    return true;
}

bool ExifUtilsImpl::setExposureProgram(uint16_t exposure_program) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_PROGRAM, exposure_program);
    return true;
}

bool ExifUtilsImpl::setExposureTime(uint32_t numerator, uint32_t denominator) {
    SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::setFlash(uint16_t flash) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_FLASH, flash);
    return true;
}

bool ExifUtilsImpl::setFNumber(uint32_t numerator, uint32_t denominator) {
    SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FNUMBER, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::setFocalLength(uint32_t numerator, uint32_t denominator) {
    SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::setGainControl(uint16_t gain_control) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_GAIN_CONTROL, gain_control);
    return true;
}

bool ExifUtilsImpl::setGpsAltitude(double altitude) {
    ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE_REF);
    std::unique_ptr<ExifEntry> refEntry =
            addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_BYTE, 1, 1);
    if (!refEntry) {
        ALOGE("%s: Adding GPSAltitudeRef exif entry failed", __FUNCTION__);
        return false;
    }
    if (altitude >= 0) {
        *refEntry->data = 0;
    } else {
        *refEntry->data = 1;
        altitude *= -1;
    }

    ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE);
    std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
            EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 1, sizeof(ExifRational));
    if (!entry) {
        exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
        ALOGE("%s: Adding GPSAltitude exif entry failed", __FUNCTION__);
        return false;
    }
    exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
                      {static_cast<ExifLong>(altitude * 1000), 1000});

    return true;
}

bool ExifUtilsImpl::setGpsLatitude(double latitude) {
    const ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE_REF);
    std::unique_ptr<ExifEntry> refEntry =
            addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
    if (!refEntry) {
        ALOGE("%s: Adding GPSLatitudeRef exif entry failed", __FUNCTION__);
        return false;
    }
    if (latitude >= 0) {
        memcpy(refEntry->data, "N", sizeof("N"));
    } else {
        memcpy(refEntry->data, "S", sizeof("S"));
        latitude *= -1;
    }

    const ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE);
    std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
            EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
    if (!entry) {
        exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
        ALOGE("%s: Adding GPSLatitude exif entry failed", __FUNCTION__);
        return false;
    }
    setLatitudeOrLongitudeData(entry->data, latitude);

    return true;
}

bool ExifUtilsImpl::setGpsLongitude(double longitude) {
    ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE_REF);
    std::unique_ptr<ExifEntry> refEntry =
            addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
    if (!refEntry) {
        ALOGE("%s: Adding GPSLongitudeRef exif entry failed", __FUNCTION__);
        return false;
    }
    if (longitude >= 0) {
        memcpy(refEntry->data, "E", sizeof("E"));
    } else {
        memcpy(refEntry->data, "W", sizeof("W"));
        longitude *= -1;
    }

    ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE);
    std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
            EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
    if (!entry) {
        exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
        ALOGE("%s: Adding GPSLongitude exif entry failed", __FUNCTION__);
        return false;
    }
    setLatitudeOrLongitudeData(entry->data, longitude);

    return true;
}

bool ExifUtilsImpl::setGpsProcessingMethod(const std::string& method) {
    std::string buffer = std::string(gExifAsciiPrefix, sizeof(gExifAsciiPrefix)) + method;
    SET_STRING(EXIF_IFD_GPS, static_cast<ExifTag>(EXIF_TAG_GPS_PROCESSING_METHOD),
               EXIF_FORMAT_UNDEFINED, buffer);
    return true;
}

bool ExifUtilsImpl::setGpsTimestamp(const struct tm& t) {
    const ExifTag dateTag = static_cast<ExifTag>(EXIF_TAG_GPS_DATE_STAMP);
    const size_t kGpsDateStampSize = 11;
    std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
            EXIF_IFD_GPS, dateTag, EXIF_FORMAT_ASCII, kGpsDateStampSize, kGpsDateStampSize);
    if (!entry) {
        ALOGE("%s: Adding GPSDateStamp exif entry failed", __FUNCTION__);
        return false;
    }
    int result = snprintf(reinterpret_cast<char*>(entry->data), kGpsDateStampSize, "%04i:%02i:%02i",
                          t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
    if (result != kGpsDateStampSize - 1) {
        ALOGW("%s: Input time is invalid", __FUNCTION__);
        return false;
    }

    const ExifTag timeTag = static_cast<ExifTag>(EXIF_TAG_GPS_TIME_STAMP);
    entry = addVariableLengthEntry(EXIF_IFD_GPS, timeTag, EXIF_FORMAT_RATIONAL, 3,
                                   3 * sizeof(ExifRational));
    if (!entry) {
        ALOGE("%s: Adding GPSTimeStamp exif entry failed", __FUNCTION__);
        return false;
    }
    exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL, {static_cast<ExifLong>(t.tm_hour), 1});
    exif_set_rational(entry->data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
                      {static_cast<ExifLong>(t.tm_min), 1});
    exif_set_rational(entry->data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
                      {static_cast<ExifLong>(t.tm_sec), 1});

    return true;
}

bool ExifUtilsImpl::setImageHeight(uint32_t length) {
    SET_SHORT(EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH, length);
    SET_LONG(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION, length);
    return true;
}

bool ExifUtilsImpl::setImageWidth(uint32_t width) {
    SET_SHORT(EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH, width);
    SET_LONG(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION, width);
    return true;
}

bool ExifUtilsImpl::setIsoSpeedRating(uint16_t iso_speed_ratings) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_ISO_SPEED_RATINGS, iso_speed_ratings);
    return true;
}

bool ExifUtilsImpl::setLightSource(uint16_t light_source) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_LIGHT_SOURCE, light_source);
    return true;
}

bool ExifUtilsImpl::setMaxAperture(uint32_t numerator, uint32_t denominator) {
    SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_MAX_APERTURE_VALUE, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::setMeteringMode(uint16_t metering_mode) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_METERING_MODE, metering_mode);
    return true;
}

bool ExifUtilsImpl::setOrientation(uint16_t orientation) {
    /*
     * Orientation value:
     *  1      2      3      4      5          6          7          8
     *
     *  888888 888888     88 88     8888888888 88                 88 8888888888
     *  88         88     88 88     88  88     88  88         88  88     88  88
     *  8888     8888   8888 8888   88         8888888888 8888888888         88
     *  88         88     88 88
     *  88         88 888888 888888
     */
    int value = 1;
    switch (orientation) {
        case 90:
            value = 6;
            break;
        case 180:
            value = 3;
            break;
        case 270:
            value = 8;
            break;
        default:
            break;
    }
    SET_SHORT(EXIF_IFD_0, EXIF_TAG_ORIENTATION, value);
    return true;
}

bool ExifUtilsImpl::setResolutionUnit(uint16_t resolution_unit) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_RESOLUTION_UNIT, resolution_unit);
    return true;
}

bool ExifUtilsImpl::setSaturation(uint16_t saturation) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SATURATION, saturation);
    return true;
}

bool ExifUtilsImpl::setSceneCaptureType(uint16_t type) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SCENE_CAPTURE_TYPE, type);
    return true;
}

bool ExifUtilsImpl::setSharpness(uint16_t sharpness) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SHARPNESS, sharpness);
    return true;
}

bool ExifUtilsImpl::setShutterSpeed(int32_t numerator, int32_t denominator) {
    SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SHUTTER_SPEED_VALUE, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::setSubjectDistance(uint32_t numerator, uint32_t denominator) {
    SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_DISTANCE, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::setSubsecTime(const std::string& subsec_time) {
    SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME, EXIF_FORMAT_ASCII, subsec_time);
    SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL, EXIF_FORMAT_ASCII, subsec_time);
    SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_DIGITIZED, EXIF_FORMAT_ASCII, subsec_time);
    return true;
}

bool ExifUtilsImpl::setWhiteBalance(uint16_t white_balance) {
    SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_WHITE_BALANCE, white_balance);
    return true;
}

bool ExifUtilsImpl::setXResolution(uint32_t numerator, uint32_t denominator) {
    SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_X_RESOLUTION, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::setYCbCrPositioning(uint16_t ycbcr_positioning) {
    SET_SHORT(EXIF_IFD_0, EXIF_TAG_YCBCR_POSITIONING, ycbcr_positioning);
    return true;
}

bool ExifUtilsImpl::setYResolution(uint32_t numerator, uint32_t denominator) {
    SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_Y_RESOLUTION, numerator, denominator);
    return true;
}

bool ExifUtilsImpl::generateApp1(const void* thumbnail_buffer, uint32_t size) {
    destroyApp1();
    exif_data_->data = const_cast<uint8_t*>(static_cast<const uint8_t*>(thumbnail_buffer));
    exif_data_->size = size;
    // Save the result into |app1_buffer_|.
    exif_data_save_data(exif_data_, &app1_buffer_, &app1_length_);
    if (!app1_length_) {
        ALOGE("%s: Allocate memory for app1_buffer_ failed", __FUNCTION__);
        return false;
    }
    /*
     * The JPEG segment size is 16 bits in spec. The size of APP1 segment should
     * be smaller than 65533 because there are two bytes for segment size field.
     */
    if (app1_length_ > 65533) {
        destroyApp1();
        ALOGE("%s: The size of APP1 segment is too large", __FUNCTION__);
        return false;
    }
    return true;
}

const uint8_t* ExifUtilsImpl::getApp1Buffer() {
    return app1_buffer_;
}

unsigned int ExifUtilsImpl::getApp1Length() {
    return app1_length_;
}

bool ExifUtilsImpl::setExifVersion(const std::string& exif_version) {
    SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_EXIF_VERSION, EXIF_FORMAT_UNDEFINED, exif_version);
    return true;
}

bool ExifUtilsImpl::setMake(const std::string& make) {
    SET_STRING(EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, make);
    return true;
}

bool ExifUtilsImpl::setModel(const std::string& model) {
    SET_STRING(EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, model);
    return true;
}

void ExifUtilsImpl::reset() {
    destroyApp1();
    if (exif_data_) {
        /*
         * Since we decided to ignore the original APP1, we are sure that there is
         * no thumbnail allocated by libexif. |exif_data_->data| is actually
         * allocated by JpegCompressor. sets |exif_data_->data| to nullptr to
         * prevent exif_data_unref() destroy it incorrectly.
         */
        exif_data_->data = nullptr;
        exif_data_->size = 0;
        exif_data_unref(exif_data_);
        exif_data_ = nullptr;
    }
}

std::unique_ptr<ExifEntry> ExifUtilsImpl::addVariableLengthEntry(ExifIfd ifd, ExifTag tag,
                                                                 ExifFormat format,
                                                                 uint64_t components,
                                                                 unsigned int size) {
    // Remove old entry if exists.
    exif_content_remove_entry(exif_data_->ifd[ifd],
                              exif_content_get_entry(exif_data_->ifd[ifd], tag));
    ExifMem* mem = exif_mem_new_default();
    if (!mem) {
        ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
        return nullptr;
    }
    std::unique_ptr<ExifEntry> entry(exif_entry_new_mem(mem));
    if (!entry) {
        ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
        exif_mem_unref(mem);
        return nullptr;
    }
    void* tmpBuffer = exif_mem_alloc(mem, size);
    if (!tmpBuffer) {
        ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
        exif_mem_unref(mem);
        return nullptr;
    }

    entry->data = static_cast<unsigned char*>(tmpBuffer);
    entry->tag = tag;
    entry->format = format;
    entry->components = components;
    entry->size = size;

    exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
    exif_mem_unref(mem);

    return entry;
}

std::unique_ptr<ExifEntry> ExifUtilsImpl::addEntry(ExifIfd ifd, ExifTag tag) {
    std::unique_ptr<ExifEntry> entry(exif_content_get_entry(exif_data_->ifd[ifd], tag));
    if (entry) {
        // exif_content_get_entry() won't ref the entry, so we ref here.
        exif_entry_ref(entry.get());
        return entry;
    }
    entry.reset(exif_entry_new());
    if (!entry) {
        ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
        return nullptr;
    }
    entry->tag = tag;
    exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
    exif_entry_initialize(entry.get(), tag);
    return entry;
}

bool ExifUtilsImpl::setShort(ExifIfd ifd, ExifTag tag, uint16_t value, const std::string& msg) {
    std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
    if (!entry) {
        ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
        return false;
    }
    exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, value);
    return true;
}

bool ExifUtilsImpl::setLong(ExifIfd ifd, ExifTag tag, uint32_t value, const std::string& msg) {
    std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
    if (!entry) {
        ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
        return false;
    }
    exif_set_long(entry->data, EXIF_BYTE_ORDER_INTEL, value);
    return true;
}

bool ExifUtilsImpl::setRational(ExifIfd ifd, ExifTag tag, uint32_t numerator, uint32_t denominator,
                                const std::string& msg) {
    std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
    if (!entry) {
        ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
        return false;
    }
    exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL, {numerator, denominator});
    return true;
}

bool ExifUtilsImpl::setSRational(ExifIfd ifd, ExifTag tag, int32_t numerator, int32_t denominator,
                                 const std::string& msg) {
    std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
    if (!entry) {
        ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
        return false;
    }
    exif_set_srational(entry->data, EXIF_BYTE_ORDER_INTEL, {numerator, denominator});
    return true;
}

bool ExifUtilsImpl::setString(ExifIfd ifd, ExifTag tag, ExifFormat format,
                              const std::string& buffer, const std::string& msg) {
    size_t entry_size = buffer.length();
    // Since the exif format is undefined, NULL termination is not necessary.
    if (format == EXIF_FORMAT_ASCII) {
        entry_size++;
    }
    std::unique_ptr<ExifEntry> entry =
            addVariableLengthEntry(ifd, tag, format, entry_size, entry_size);
    if (!entry) {
        ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
        return false;
    }
    memcpy(entry->data, buffer.c_str(), entry_size);
    return true;
}

void ExifUtilsImpl::destroyApp1() {
    /*
     * Since there is no API to access ExifMem in ExifData->priv, we use free
     * here, which is the default free function in libexif. See
     * exif_data_save_data() for detail.
     */
    free(app1_buffer_);
    app1_buffer_ = nullptr;
    app1_length_ = 0;
}

bool ExifUtilsImpl::setFromMetadata(const CameraMetadata& metadata, const size_t imageWidth,
                                    const size_t imageHeight) {
    // How precise the float-to-rational conversion for EXIF tags would be.
    constexpr int kRationalPrecision = 10000;
    if (!setImageWidth(imageWidth) || !setImageHeight(imageHeight)) {
        ALOGE("%s: setting image resolution failed.", __FUNCTION__);
        return false;
    }

    struct timespec tp;
    struct tm time_info;
    bool time_available = clock_gettime(CLOCK_REALTIME, &tp) != -1;
    localtime_r(&tp.tv_sec, &time_info);
    if (!setDateTime(time_info)) {
        ALOGE("%s: setting data time failed.", __FUNCTION__);
        return false;
    }

    float focal_length;
    camera_metadata_ro_entry entry = metadata.find(ANDROID_LENS_FOCAL_LENGTH);
    if (entry.count) {
        focal_length = entry.data.f[0];

        if (!setFocalLength(static_cast<uint32_t>(focal_length * kRationalPrecision),
                            kRationalPrecision)) {
            ALOGE("%s: setting focal length failed.", __FUNCTION__);
            return false;
        }
    } else {
        ALOGV("%s: Cannot find focal length in metadata.", __FUNCTION__);
    }

    if (metadata.exists(ANDROID_JPEG_GPS_COORDINATES)) {
        entry = metadata.find(ANDROID_JPEG_GPS_COORDINATES);
        if (entry.count < 3) {
            ALOGE("%s: Gps coordinates in metadata is not complete.", __FUNCTION__);
            return false;
        }
        if (!setGpsLatitude(entry.data.d[0])) {
            ALOGE("%s: setting gps latitude failed.", __FUNCTION__);
            return false;
        }
        if (!setGpsLongitude(entry.data.d[1])) {
            ALOGE("%s: setting gps longitude failed.", __FUNCTION__);
            return false;
        }
        if (!setGpsAltitude(entry.data.d[2])) {
            ALOGE("%s: setting gps altitude failed.", __FUNCTION__);
            return false;
        }
    }

    if (metadata.exists(ANDROID_JPEG_GPS_PROCESSING_METHOD)) {
        entry = metadata.find(ANDROID_JPEG_GPS_PROCESSING_METHOD);
        std::string method_str(reinterpret_cast<const char*>(entry.data.u8));
        if (!setGpsProcessingMethod(method_str)) {
            ALOGE("%s: setting gps processing method failed.", __FUNCTION__);
            return false;
        }
    }

    if (time_available && metadata.exists(ANDROID_JPEG_GPS_TIMESTAMP)) {
        entry = metadata.find(ANDROID_JPEG_GPS_TIMESTAMP);
        time_t timestamp = static_cast<time_t>(entry.data.i64[0]);
        if (gmtime_r(&timestamp, &time_info)) {
            if (!setGpsTimestamp(time_info)) {
                ALOGE("%s: setting gps timestamp failed.", __FUNCTION__);
                return false;
            }
        } else {
            ALOGE("%s: Time tranformation failed.", __FUNCTION__);
            return false;
        }
    }

    if (metadata.exists(ANDROID_JPEG_ORIENTATION)) {
        entry = metadata.find(ANDROID_JPEG_ORIENTATION);
        if (!setOrientation(entry.data.i32[0])) {
            ALOGE("%s: setting orientation failed.", __FUNCTION__);
            return false;
        }
    }

    if (metadata.exists(ANDROID_SENSOR_EXPOSURE_TIME)) {
        entry = metadata.find(ANDROID_SENSOR_EXPOSURE_TIME);
        // int64_t of nanoseconds
        if (!setExposureTime(entry.data.i64[0], 1000000000u)) {
            ALOGE("%s: setting exposure time failed.", __FUNCTION__);
            return false;
        }
    }

    if (metadata.exists(ANDROID_LENS_APERTURE)) {
        const int kAperturePrecision = 10000;
        entry = metadata.find(ANDROID_LENS_APERTURE);
        if (!setFNumber(entry.data.f[0] * kAperturePrecision, kAperturePrecision)) {
            ALOGE("%s: setting F number failed.", __FUNCTION__);
            return false;
        }
    }

    if (metadata.exists(ANDROID_FLASH_INFO_AVAILABLE)) {
        entry = metadata.find(ANDROID_FLASH_INFO_AVAILABLE);
        if (entry.data.u8[0] == ANDROID_FLASH_INFO_AVAILABLE_FALSE) {
            const uint32_t kNoFlashFunction = 0x20;
            if (!setFlash(kNoFlashFunction)) {
                ALOGE("%s: setting flash failed.", __FUNCTION__);
                return false;
            }
        } else {
            ALOGE("%s: Unsupported flash info: %d", __FUNCTION__, entry.data.u8[0]);
            return false;
        }
    }

    if (metadata.exists(ANDROID_CONTROL_AWB_MODE)) {
        entry = metadata.find(ANDROID_CONTROL_AWB_MODE);
        if (entry.data.u8[0] == ANDROID_CONTROL_AWB_MODE_AUTO) {
            const uint16_t kAutoWhiteBalance = 0;
            if (!setWhiteBalance(kAutoWhiteBalance)) {
                ALOGE("%s: setting white balance failed.", __FUNCTION__);
                return false;
            }
        } else {
            ALOGE("%s: Unsupported awb mode: %d", __FUNCTION__, entry.data.u8[0]);
            return false;
        }
    }

    if (time_available) {
        char str[4];
        if (snprintf(str, sizeof(str), "%03ld", tp.tv_nsec / 1000000) < 0) {
            ALOGE("%s: Subsec is invalid: %ld", __FUNCTION__, tp.tv_nsec);
            return false;
        }
        if (!setSubsecTime(std::string(str))) {
            ALOGE("%s: setting subsec time failed.", __FUNCTION__);
            return false;
        }
    }

    return true;
}

}  // namespace helper
}  // namespace common
}  // namespace camera
}  // namespace hardware
}  // namespace android
