/*
 *
 *  Copyright (C) 2001-2020, OFFIS e.V.
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation were developed by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module:  dcmjpeg
 *
 *  Author:  Marco Eichelberg, Norbert Olges
 *
 *  Purpose: abstract codec class for JPEG encoders.
 *
 */

#ifndef DJCODEC_H
#define DJCODEC_H

#include "dcmtk/config/osconfig.h"
#include "dcmtk/ofstd/oftypes.h"
#include "dcmtk/dcmdata/dccodec.h"    /* for class DcmCodec */
#include "dcmtk/dcmjpeg/djutils.h"    /* for enums */
#include "dcmtk/ofstd/oflist.h"
#include "dcmtk/ofstd/ofstring.h"     /* for class OFString */

class DataInterface;
class DJEncoder;
class DcmDataset;
class DJCodecParameter;
class DJDecoder;
class DcmItem;
class DcmPixelItem;
class DicomImage;
class DcmTagKey;


/** abstract codec class for JPEG encoders.
 *  This abstract class contains most of the application logic
 *  needed for a dcmdata codec object that implements a JPEG encoder
 *  using the DJEncoder interface to the underlying JPEG implementation.
 *  This class only supports compression, it neither implements
 *  decoding nor transcoding.
 */
class DCMTK_DCMJPEG_EXPORT DJCodecEncoder : public DcmCodec
{
public:

  /// default constructor
  DJCodecEncoder();

  /// destructor
  virtual ~DJCodecEncoder();

  /** decompresses the given pixel sequence and
   *  stores the result in the given uncompressedPixelData element.
   *  @param fromRepParam current representation parameter of compressed data, may be NULL
   *  @param pixSeq compressed pixel sequence
   *  @param uncompressedPixelData uncompressed pixel data stored in this element
   *  @param cp codec parameters for this codec
   *  @param objStack stack pointing to the location of the pixel data
   *    element in the current dataset.
   *  @param removeOldRep boolean flag that should be set to false before this method call
   *    and will be set to true if the codec modifies the DICOM dataset such
   *    that the pixel data of the original representation may not be usable
   *    anymore.
   *  @return EC_Normal if successful, an error code otherwise.
   */
  virtual OFCondition decode(
    const DcmRepresentationParameter * fromRepParam,
    DcmPixelSequence * pixSeq,
    DcmPolymorphOBOW& uncompressedPixelData,
    const DcmCodecParameter * cp,
    const DcmStack & objStack,
    OFBool& removeOldRep) const;

  /** decompresses a single frame from the given pixel sequence and
   *  stores the result in the given buffer.
   *  @param fromParam representation parameter of current compressed
   *    representation, may be NULL.
   *  @param fromPixSeq compressed pixel sequence
   *  @param cp codec parameters for this codec
   *  @param dataset pointer to dataset in which pixel data element is contained
   *  @param frameNo number of frame, starting with 0 for the first frame
   *  @param startFragment index of the compressed fragment that contains
   *    all or the first part of the compressed bitstream for the given frameNo.
   *    Upon successful return this parameter is updated to contain the index
   *    of the first compressed fragment of the next frame.
   *    When unknown, zero should be passed. In this case the decompression
   *    algorithm will try to determine the index by itself, which will always
   *    work if frames are decompressed in increasing order from first to last,
   *    but may fail if frames are decompressed in random order, multiple fragments
   *    per frame and multiple frames are present in the dataset, and the offset
   *    table is empty.
   *  @param buffer pointer to buffer where frame is to be stored
   *  @param bufSize size of buffer in bytes
   *  @param decompressedColorModel upon successful return, the color model
   *    of the decompressed image (which may be different from the one used
   *    in the compressed images) is returned in this parameter.
   *  @return EC_Normal if successful, an error code otherwise.
   */
  virtual OFCondition decodeFrame(
    const DcmRepresentationParameter * fromParam,
    DcmPixelSequence * fromPixSeq,
    const DcmCodecParameter * cp,
    DcmItem *dataset,
    Uint32 frameNo,
    Uint32& startFragment,
    void *buffer,
    Uint32 bufSize,
    OFString& decompressedColorModel) const;

  /** compresses the given uncompressed DICOM image and stores
   *  the result in the given pixSeq element.
   *  @param pixelData pointer to the uncompressed image data in OW format
   *    and local byte order
   *  @param length of the pixel data field in bytes
   *  @param toRepParam representation parameter describing the desired
   *    compressed representation (e.g. JPEG quality)
   *  @param pixSeq compressed pixel sequence (pointer to new DcmPixelSequence object
   *    allocated on heap) returned in this parameter upon success.
   *  @param cp codec parameters for this codec
   *  @param objStack stack pointing to the location of the pixel data
   *    element in the current dataset.
   *  @param removeOldRep boolean flag that should be set to false before this method call
   *    and will be set to true if the codec modifies the DICOM dataset such
   *    that the pixel data of the original representation may not be usable
   *    anymore.
   *  @return EC_Normal if successful, an error code otherwise.
   */
  virtual OFCondition encode(
    const Uint16 * pixelData,
    const Uint32 length,
    const DcmRepresentationParameter * toRepParam,
    DcmPixelSequence * & pixSeq,
    const DcmCodecParameter *cp,
    DcmStack & objStack,
    OFBool& removeOldRep) const;

  /** transcodes (re-compresses) the given compressed DICOM image and stores
   *  the result in the given toPixSeq element.
   *  @param fromRepType current transfer syntax of the compressed image
   *  @param fromRepParam current representation parameter of compressed data, may be NULL
   *  @param fromPixSeq compressed pixel sequence
   *  @param toRepParam representation parameter describing the desired
   *    new compressed representation (e.g. JPEG quality)
   *  @param toPixSeq compressed pixel sequence (pointer to new DcmPixelSequence object
   *    allocated on heap) returned in this parameter upon success.
   *  @param cp codec parameters for this codec
   *  @param objStack stack pointing to the location of the pixel data
   *    element in the current dataset.
   *  @param removeOldRep boolean flag that should be set to false before this method call
   *    and will be set to true if the codec modifies the DICOM dataset such
   *    that the pixel data of the original representation may not be usable
   *    anymore.
   *  @return EC_Normal if successful, an error code otherwise.
   */
  virtual OFCondition encode(
    const E_TransferSyntax fromRepType,
    const DcmRepresentationParameter * fromRepParam,
    DcmPixelSequence * fromPixSeq,
    const DcmRepresentationParameter * toRepParam,
    DcmPixelSequence * & toPixSeq,
    const DcmCodecParameter * cp,
    DcmStack & objStack,
    OFBool& removeOldRep) const;

  /** checks if this codec is able to convert from the
   *  given current transfer syntax to the given new
   *  transfer syntax
   *  @param oldRepType current transfer syntax
   *  @param newRepType desired new transfer syntax
   *  @return true if transformation is supported by this codec, false otherwise.
   */
  virtual OFBool canChangeCoding(
    const E_TransferSyntax oldRepType,
    const E_TransferSyntax newRepType) const;

  /** determine color model of the decompressed image
   *  @param fromParam representation parameter of current compressed
   *    representation, may be NULL
   *  @param fromPixSeq compressed pixel sequence
   *  @param cp codec parameters for this codec
   *  @param dataset pointer to dataset in which pixel data element is contained
   *  @param dataset pointer to DICOM dataset in which this pixel data object
   *    is located. Used to access photometric interpretation.
   *  @param decompressedColorModel upon successful return, the color model
   *    of the decompressed image (which may be different from the one used
   *    in the compressed images) is returned in this parameter
   *  @return EC_Normal if successful, an error code otherwise
   */
  virtual OFCondition determineDecompressedColorModel(
    const DcmRepresentationParameter *fromParam,
    DcmPixelSequence *fromPixSeq,
    const DcmCodecParameter *cp,
    DcmItem *dataset,
    OFString &decompressedColorModel) const;

  /** returns the transfer syntax that this particular codec
   *  is able to encode and decode.
   *  @return supported transfer syntax
   */
  virtual E_TransferSyntax supportedTransferSyntax() const = 0;

protected:

  /** format compression ratio as string and append to given string object.
   *  @param arg string to append to
   *  @param ratio compression ratio
   */
  static void appendCompressionRatio(OFString& arg, double ratio);

    /** toggles Planar Configuration of 8-bit pixel data from "by pixel" to
   *  "by plane" and vice versa.
   *  @param pixelData - [in/out] Original pixel data (input), contains
   *                     pixel data with toggled Planar configuration after
   *                     returning (output).
   *  @param numValues - [in] The number of 8 bit values in pixelData
   *  @param samplesPerPixel - [in] Number of components for one pixel
   *  @param oldPlanarConfig - [in] The old Planar Configuration, that should
   *                           be toggled. 0 means "by pixel", 1 "by color"
   *  @return EC_Normal, if conversion was successful; error else
   */
  static OFCondition togglePlanarConfiguration8(
    Uint8 *pixelData,
    const size_t numValues,
    const Uint16 samplesPerPixel,
    const Uint16 oldPlanarConfig);

  /** toggles Planar Configuration of 16-bit pixel data from "by pixel" to
   *  "by plane" and vice versa.
   *  @param pixelData - [in/out] Original pixel data (input), contains
   *                     pixel data with toggled Planar configuration after
   *                     returning (output).
   *  @param numValues - [in] The number of 16 bit values in pixelData
   *  @param samplesPerPixel - [in] Number of components for one pixel
   *  @param oldPlanarConfig - [in] The old Planar Configuration, that should
   *                           be toggled. 0 means "by pixel", 1 "by color"
   *  @return EC_Normal, if conversion was successful; error else
   */
  static OFCondition togglePlanarConfiguration16(
    Uint16 *pixelData,
    const size_t numValues,
    const Uint16 samplesPerPixel,
    const Uint16 oldPlanarConfig);

private:

  /** compresses the given uncompressed DICOM color image and stores
   *  the result in the given pixSeq element.
   *  @param YBRmode true if the source image has YBR_FULL or YBR_FULL_422
   *    photometric interpretation and can thus be compressed without color
   *    space conversion.
   *  @param dataset DICOM dataset containing the pixel data as well as
   *    descriptive attributes
   *  @param toRepParam representation parameter describing the desired
   *    compressed representation (e.g. JPEG quality)
   *  @param pixSeq compressed pixel sequence (pointer to new DcmPixelSequence object
   *    allocated on heap) returned in this parameter upon success.
   *  @param cp codec parameters for this codec
   *  @param compressionRatio compression ratio returned in this parameter
   *    if successful.
   *  @return EC_Normal if successful, an error code otherwise.
   */
  virtual OFCondition encodeColorImage(
    OFBool YBRmode,
    DcmItem *dataset,
    const DcmRepresentationParameter * toRepParam,
    DcmPixelSequence * & pixSeq,
    const DJCodecParameter *cp,
    double& compressionRatio) const;

  /** compresses the given uncompressed monochrome DICOM image and stores
   *  the result in the given pixSeq element.
   *  @param dataset DICOM dataset containing the pixel data as well as
   *    descriptive attributes
   *  @param toRepParam representation parameter describing the desired
   *    compressed representation (e.g. JPEG quality)
   *  @param pixSeq compressed pixel sequence (pointer to new DcmPixelSequence object
   *    allocated on heap) returned in this parameter upon success.
   *  @param cp codec parameters for this codec
   *  @param compressionRatio compression ratio returned in this parameter
   *    if successful.
   *  @return EC_Normal if successful, an error code otherwise.
   */
  virtual OFCondition encodeMonochromeImage(
        DcmItem *dataset,
        const DcmRepresentationParameter * toRepParam,
        DcmPixelSequence * & pixSeq,
        const DJCodecParameter *cp,
        double& compressionRatio) const;

  /** compresses the given uncompressed DICOM image and stores
   *  the result in the given pixSeq element. No colorspace
   *  conversions, modality or windowing transformations are applied
   *  to guarantee, that the quality of the source image is fully preserved.
   *  @param toRepParam - [in] representation parameter describing the desired
   *  @param pixSeq - [out] compressed pixel sequence (pointer to new DcmPixelSequence
   *    object allocated on heap) returned in this parameter upon success
   *  @param cp - [in] codec parameters for this codec
   *  @param objStack - [in/out] stack pointing to the location of the pixel data
   *    element in the current dataset.
   *  @return EC_Normal if successful, an error code otherwise.
   */
  virtual OFCondition encodeTrueLossless(
    const DcmRepresentationParameter * toRepParam,
    DcmPixelSequence * & pixSeq,
    const DcmCodecParameter *cp,
    DcmStack & objStack) const;

  /** create Lossy Image Compression and Lossy Image Compression Ratio.
   *  @param dataset dataset to be modified
   *  @param ratio image compression ratio > 1. This is not the "quality factor"
   *    but the real effective ratio between compressed and uncompressed image,
   *    i. e. 30 means a 30:1 lossy compression.
   *  @return EC_Normal if successful, an error code otherwise
   */
  virtual OFCondition updateLossyCompressionRatio(DcmItem *dataset, double ratio) const;

  /** create Derivation Description.
   *  @param dataset dataset to be modified
   *  @param toRepParam representation parameter passed to encode()
   *  @param cp codec parameter passed to encode()
   *  @param bitsPerSample bits per sample of the original image data prior to compression
   *  @param ratio image compression ratio. This is not the "quality factor"
   *    but the real effective ratio between compressed and uncompressed image,
   *    i. e. 30 means a 30:1 lossy compression.
   *  @return EC_Normal if successful, an error code otherwise
   */
  virtual OFCondition updateDerivationDescription(
    DcmItem *dataset,
    const DcmRepresentationParameter * toRepParam,
    const DJCodecParameter *cp,
    Uint8 bitsPerSample,
    double ratio) const;

  /** for all overlay groups create (60xx,3000) Overlay Data.
   *  @param dataset dataset to be modified
   *  @param image DicomImage object for this dataset
   *  @return EC_Normal if successful, an error code otherwise
   */
  virtual OFCondition adjustOverlays(DcmItem *dataset, DicomImage& image) const;

  /** returns true if the transfer syntax supported by this
   *  codec is lossless.
   *  @return lossless flag
   */
  virtual OFBool isLosslessProcess() const = 0;

  /** creates 'derivation description' string after encoding.
   *  @param toRepParam representation parameter passed to encode()
   *  @param cp codec parameter passed to encode()
   *  @param bitsPerSample bits per sample of the original image data prior to compression
   *  @param ratio image compression ratio. This is not the "quality factor"
   *    but the real effective ratio between compressed and uncompressed image,
   *    i. e. 30 means a 30:1 lossy compression.
   *  @param derivationDescription derivation description returned in this
   *    parameter which is initially empty
   */
  virtual void createDerivationDescription(
    const DcmRepresentationParameter * toRepParam,
    const DJCodecParameter *cp,
    Uint8 bitsPerSample,
    double ratio,
    OFString& derivationDescription) const = 0;

  /** creates an instance of the compression library to be used
   *  for encoding/decoding.
   *  @param toRepParam representation parameter passed to encode()
   *  @param cp codec parameter passed to encode()
   *  @param bitsPerSample bits per sample for the image data
   *  @return pointer to newly allocated codec object
   */
  virtual DJEncoder *createEncoderInstance(
    const DcmRepresentationParameter * toRepParam,
    const DJCodecParameter *cp,
    Uint8 bitsPerSample) const = 0;

  /** modifies all VOI window center/width settings in the image.
   *  Modifications are based on the pixel value mapping
   *  f(x) = (x+voiOffset)*voiFactor
   *  @param dataset dataset to be updated
   *  @param voiOffset offset to be added to each pixel
   *  @param voiFactor factor to be multiplied
   *  @return EC_Normal if successful, an error code otherwise
   */
  static OFCondition correctVOIWindows(DcmItem *dataset, double voiOffset, double voiFactor);

  OFCondition updatePlanarConfiguration(
    DcmItem *item,
    const Uint16 newPlanConf) const;
};

#endif
