/*
 * 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_JPEGENCODERHELPER_H
#define ULTRAHDR_JPEGENCODERHELPER_H

#include <stdio.h>  // For jpeglib.h.

// C++ build requires extern C for jpeg internals.
#ifdef __cplusplus
extern "C" {
#endif

#include <jerror.h>
#include <jpeglib.h>

#ifdef __cplusplus
}  // extern "C"
#endif

#include <cstdint>
#include <vector>

#include "ultrahdr_api.h"

namespace ultrahdr {

/*!\brief module for managing output */
struct destination_mgr_impl : jpeg_destination_mgr {
  static const int kBlockSize = 16384;  // result buffer resize step
  std::vector<JOCTET> mResultBuffer;    // buffer to store encoded data
};

/*!\brief Encapsulates a converter from raw to jpg image format. This class is not thread-safe */
class JpegEncoderHelper {
 public:
  JpegEncoderHelper() = default;
  ~JpegEncoderHelper() = default;

  /*!\brief This function encodes the raw image that is passed to it and stores the results
   * internally. The result is accessible via getter functions.
   *
   * \param[in]  img        image to encode
   * \param[in]  qfactor    quality factor [1 - 100, 1 being poorest and 100 being best quality]
   * \param[in]  iccBuffer  pointer to icc segment that needs to be added to the compressed image
   * \param[in]  iccSize    size of icc segment
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t compressImage(const uhdr_raw_image_t* img, const int qfactor,
                                  const void* iccBuffer, const size_t iccSize);

  /*!\brief This function encodes the raw image that is passed to it and stores the results
   * internally. The result is accessible via getter functions.
   *
   * \param[in]  planes     pointers of all planes of input image
   * \param[in]  strides    strides of all planes of input image
   * \param[in]  width      image width
   * \param[in]  height     image height
   * \param[in]  format     input raw image format
   * \param[in]  qfactor    quality factor [1 - 100, 1 being poorest and 100 being best quality]
   * \param[in]  iccBuffer  pointer to icc segment that needs to be added to the compressed image
   * \param[in]  iccSize    size of icc segment
   *
   * \return uhdr_error_info_t #UHDR_CODEC_OK if operation succeeds, uhdr_codec_err_t otherwise.
   */
  uhdr_error_info_t compressImage(const uint8_t* planes[3], const unsigned int strides[3],
                                  const int width, const int height, const uhdr_img_fmt_t format,
                                  const int qfactor, const void* iccBuffer, const size_t iccSize);

  /*! Below public methods are only effective if a call to compressImage() is made and it returned
   * true. */

  /*!\brief returns pointer to compressed image output */
  uhdr_compressed_image_t getCompressedImage();

  /*!\brief returns pointer to compressed image output
   * \deprecated This function is deprecated instead use getCompressedImage().
   */
  void* getCompressedImagePtr() { return mDestMgr.mResultBuffer.data(); }

  /*!\brief returns size of compressed image
   * \deprecated This function is deprecated instead use getCompressedImage().
   */
  size_t getCompressedImageSize() { return mDestMgr.mResultBuffer.size(); }

 private:
  // max number of components supported
  static constexpr int kMaxNumComponents = 3;

  uhdr_error_info_t encode(const uint8_t* planes[3], const unsigned int strides[3], const int width,
                           const int height, const uhdr_img_fmt_t format, const int qfactor,
                           const void* iccBuffer, const size_t iccSize);

  uhdr_error_info_t compressYCbCr(jpeg_compress_struct* cinfo, const uint8_t* planes[3],
                                  const unsigned int strides[3]);

  destination_mgr_impl mDestMgr;  // object for managing output

  // temporary storage
  std::unique_ptr<uint8_t[]> mPlanesMCURow[kMaxNumComponents];

  unsigned int mPlaneWidth[kMaxNumComponents];
  unsigned int mPlaneHeight[kMaxNumComponents];
};

} /* namespace ultrahdr  */

#endif  // ULTRAHDR_JPEGENCODERHELPER_H
