/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

#ifndef ULTRAHDR_JPEGRUTILS_H
#define ULTRAHDR_JPEGRUTILS_H

#include "ultrahdr/jpegr.h"

#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
     __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) ||                  \
    defined(_WIN32) || defined(_WIN64)
#define UHDR_HOST_BIG_ENDIAN false
#elif (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
       __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) ||                  \
    defined(__BIG_ENDIAN__)
#define UHDR_HOST_BIG_ENDIAN true
#else
#error "Unable to detect endianness."
#endif

// TODO (dichenzhang): This is old version metadata, new version can be found in
// https://drive.google.com/file/d/1yUGmjGytRuBa2vpr9eM5Uu8CVhyyddjp/view?resourcekey=0-HGzFrzPQzu5FNYLRAJXQBA
// and in gainmapmetadata.h/.cpp
// This file is kept in order to keep the backward compatibility.
namespace ultrahdr {

static constexpr uint32_t EndianSwap32(uint32_t value) {
  return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) |
         (value >> 24);
}
static inline uint16_t EndianSwap16(uint16_t value) {
  return static_cast<uint16_t>((value >> 8) | ((value & 0xFF) << 8));
}

/*
 * Mutable data structure. Holds information for metadata.
 */
class DataStruct {
 private:
  void* data;
  size_t writePos;
  size_t length;

 public:
  DataStruct(size_t s);
  ~DataStruct();

  void* getData();
  size_t getLength();
  size_t getBytesWritten();
  bool write8(uint8_t value);
  bool write16(uint16_t value);
  bool write32(uint32_t value);
  bool write(const void* src, size_t size);
};

/*
 * Helper function used for writing data to destination.
 *
 * @param destination destination of the data to be written.
 * @param source source of data being written.
 * @param length length of the data to be written.
 * @param position cursor in desitination where the data is to be written.
 * @return success or error code.
 */
uhdr_error_info_t Write(uhdr_compressed_image_t* destination, const void* source, size_t length,
                        size_t& position);

/*
 * Parses XMP packet and fills metadata with data from XMP
 *
 * @param xmp_data pointer to XMP packet
 * @param xmp_size size of XMP packet
 * @param exif_data pointer to EXIF packet
 * @param exif_size size of EXIF packet
 * @param metadata place to store HDR metadata values
 * @return success or error code.
 */
uhdr_error_info_t getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, uint8_t* exif_data,
                                     int exif_size, uhdr_gainmap_metadata_ext_t* metadata);

/*
 * This method generates XMP metadata for the primary image.
 *
 * below is an example of the XMP metadata that this function generates where
 * secondary_image_length = 1000
 *
 * <x:xmpmeta
 *   xmlns:x="adobe:ns:meta/"
 *   x:xmptk="Adobe XMP Core 5.1.2">
 *   <rdf:RDF
 *     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
 *     <rdf:Description
 *       xmlns:Container="http://ns.google.com/photos/1.0/container/"
 *       xmlns:Item="http://ns.google.com/photos/1.0/container/item/"
 *       xmlns:hdrgm="http://ns.adobe.com/hdr-gain-map/1.0/"
 *       hdrgm:Version="1">
 *       <Container:Directory>
 *         <rdf:Seq>
 *           <rdf:li
 *             rdf:parseType="Resource">
 *             <Container:Item
 *               Item:Semantic="Primary"
 *               Item:Mime="image/jpeg"/>
 *           </rdf:li>
 *           <rdf:li
 *             rdf:parseType="Resource">
 *             <Container:Item
 *               Item:Semantic="GainMap"
 *               Item:Mime="image/jpeg"
 *               Item:Length="1000"/>
 *           </rdf:li>
 *         </rdf:Seq>
 *       </Container:Directory>
 *     </rdf:Description>
 *   </rdf:RDF>
 * </x:xmpmeta>
 *
 * @param secondary_image_length length of secondary image
 * @return XMP metadata in type of string
 */
std::string generateXmpForPrimaryImage(size_t secondary_image_length,
                                       uhdr_gainmap_metadata_ext_t& metadata);

/*
 * This method generates XMP metadata for the recovery map image.
 * Link: https://developer.android.com/media/platform/hdr-image-format#XMP-attributes
 *
 * below is an example of the XMP metadata that this function generates where
 * max_content_boost = 8.0
 * min_content_boost = 0.5
 *
 * <x:xmpmeta
 *   xmlns:x="adobe:ns:meta/"
 *   x:xmptk="Adobe XMP Core 5.1.2">
 *   <rdf:RDF
 *     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
 *     <rdf:Description
 *       xmlns:hdrgm="http://ns.adobe.com/hdr-gain-map/1.0/"
 *       hdrgm:Version="1"
 *       hdrgm:GainMapMin="-1"
 *       hdrgm:GainMapMax="3"
 *       hdrgm:Gamma="1"
 *       hdrgm:OffsetSDR="0"
 *       hdrgm:OffsetHDR="0"
 *       hdrgm:HDRCapacityMin="0"
 *       hdrgm:HDRCapacityMax="3"
 *       hdrgm:BaseRenditionIsHDR="False"/>
 *   </rdf:RDF>
 * </x:xmpmeta>
 *
 * @param metadata JPEG/R metadata to encode as XMP
 * @return XMP metadata in type of string
 */
std::string generateXmpForSecondaryImage(uhdr_gainmap_metadata_ext_t& metadata);

}  // namespace ultrahdr

#endif  // ULTRAHDR_JPEGRUTILS_H
