/*
 * This file is part of libdcadec.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef DCA_CONTEXT_H
#define DCA_CONTEXT_H

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

/**@{*/
/** Make compact version code that can be compared easily */
#define DCADEC_VERSION_CODE(major, minor, patch) \
    (((major) << 24) | ((minor) << 12) | (patch) | 0U)

/**
 * Version of libdcadec API. Major number gets bumped at each API change that
 * breaks backward compatibility. Minor number gets bumped at each API change
 * that remains compatible. Patch is reserved for non-API related changes.
 */
#define DCADEC_API_VERSION_MAJOR    0
#define DCADEC_API_VERSION_MINOR    1
#define DCADEC_API_VERSION_PATCH    0

#define DCADEC_API_VERSION \
    DCADEC_VERSION_CODE(DCADEC_API_VERSION_MAJOR, \
                        DCADEC_API_VERSION_MINOR, \
                        DCADEC_API_VERSION_PATCH)
/**@}*/

/**@{*/
#if (defined _WIN32)
#define DCADEC_SHARED_EXPORT    __declspec(dllexport)
#define DCADEC_SHARED_IMPORT    __declspec(dllimport)
#elif (__GNUC__ >= 4)
#define DCADEC_SHARED_EXPORT    __attribute__((visibility("default")))
#define DCADEC_SHARED_IMPORT    __attribute__((visibility("default")))
#else
#define DCADEC_SHARED_EXPORT
#define DCADEC_SHARED_IMPORT
#endif

#ifdef DCADEC_SHARED
#ifdef DCADEC_INTERNAL
#define DCADEC_API  DCADEC_SHARED_EXPORT
#else
#define DCADEC_API  DCADEC_SHARED_IMPORT
#endif
#else
#define DCADEC_API
#endif
/**@}*/

/**@{*/
#define DCADEC_EINVAL       1   /**< Invalid argument */
#define DCADEC_EBADDATA     2   /**< Invalid bitstream format */
#define DCADEC_EBADCRC      3   /**< CRC check failed */
#define DCADEC_EBADREAD     4   /**< Bitstream navigation error */
#define DCADEC_ENOSYNC      5   /**< Synchronization error */
#define DCADEC_ENOSUP       6   /**< Unsupported feature */
#define DCADEC_ENOMEM       7   /**< Memory allocation error */
#define DCADEC_EOVERFLOW    8   /**< PCM output overflow */
#define DCADEC_EIO          9   /**< I/O error */
#define DCADEC_EOUTCHG     10   /**< PCM output parameters changed */
#define DCADEC_EFAIL       32   /**< Unspecified error */
/**@}*/

/**@{*/
#define DCADEC_WCOREAUXFAILED   1   /**< Failed to parse core auxiliary data */
#define DCADEC_WCOREEXTFAILED   2   /**< Failed to parse core extension */
#define DCADEC_WEXSSFAILED      3   /**< Failed to parse EXSS */
#define DCADEC_WXLLFAILED       4   /**< Failed to parse XLL */
#define DCADEC_WXLLSYNCERR      5   /**< XLL synchronization error */
#define DCADEC_WXLLBANDERR      6   /**< XLL frequency band error */
#define DCADEC_WXLLCONFERR      7   /**< XLL configuration error */
#define DCADEC_WXLLCLIPPED      8   /**< Clipping detected in XLL output */
#define DCADEC_WXLLLOSSY        9   /**< XLL output not lossless */
/**@}*/

/**@{*/
/** Decode DTS core only without extensions */
#define DCADEC_FLAG_CORE_ONLY           0x01

/** Force bit exact DTS core decoding */
#define DCADEC_FLAG_CORE_BIT_EXACT      0x02

/**
 * Force DTS core synthesis using X96 filter, with high frequency subbands set
 * to zero. If actual X96 data is present, it is discarded when this flag is
 * set. Effectively doubles output sample rate.
 */
#define DCADEC_FLAG_CORE_SYNTH_X96      0x04

/** Use IIR filter for floating point DTS core LFE channel interpolation */
#define DCADEC_FLAG_CORE_LFE_IIR        0x08

/** Use FIR filter for floating point DTS core LFE channel interpolation */
#define DCADEC_FLAG_CORE_LFE_FIR        0x10

/**
 * Extract embedded 2.0 downmix if present (or perform 5.1 to 2.0 downmix if
 * custom downmixing coefficients are present), otherwise extract 5.1 downmix.
 */
#define DCADEC_FLAG_KEEP_DMIX_2CH       0x20

/** Extract embedded 5.1 downmix (always present for 6.1 and 7.1 content) */
#define DCADEC_FLAG_KEEP_DMIX_6CH       0x40

/** Output native DTS channel layout, not WAVEFORMATEXTENSIBLE layout */
#define DCADEC_FLAG_NATIVE_LAYOUT       0x80

/** Don't conceal errors */
#define DCADEC_FLAG_STRICT              0x100

/** Don't clip returned PCM samples to output bit depth */
#define DCADEC_FLAG_DONT_CLIP           0x200
/**@}*/

/**@{*/
#define DCADEC_PROFILE_UNKNOWN  0       /**< Unknown Profile */
#define DCADEC_PROFILE_DS       0x01    /**< Digital Surround */
#define DCADEC_PROFILE_DS_96_24 0x02    /**< Digital Surround 96/24 */
#define DCADEC_PROFILE_DS_ES    0x04    /**< Digital Surround ES */
#define DCADEC_PROFILE_HD_HRA   0x08    /**< High Resolution Audio */
#define DCADEC_PROFILE_HD_MA    0x10    /**< Master Audio */
#define DCADEC_PROFILE_EXPRESS  0x20    /**< Express */
/**@}*/

/**@{*/
/** Not matrix encoded */
#define DCADEC_MATRIX_ENCODING_NONE         0

/**< Encoded for matrix surround decoding */
#define DCADEC_MATRIX_ENCODING_SURROUND     1

/**< Audio processed for headphone playback */
#define DCADEC_MATRIX_ENCODING_HEADPHONE    2
/**@}*/

/**@{*/
#define DCADEC_LOG_ERROR    0
#define DCADEC_LOG_WARNING  1
#define DCADEC_LOG_INFO     2
#define DCADEC_LOG_VERBOSE  3
#define DCADEC_LOG_DEBUG    4
/**@}*/

/**
 * Size in bytes of empty padding that must be present after the end of input
 * buffer. libdcadec may overread the input buffer up to this number of bytes.
 */
#define DCADEC_BUFFER_PADDING   8

struct dcadec_context;

struct dcadec_core_info {
    int     nchannels;      /**< Number of primary audio channels */
    int     audio_mode;     /**< Core audio channel arrangement (AMODE) */
    int     lfe_present;    /**< LFE channel presence flag (can be 0, 1 or 2) */
    int     sample_rate;    /**< Core audio sample rate in Hz */
    int     source_pcm_res; /**< Source PCM resolution in bits */
    bool    es_format;      /**< Extended surround (ES) mastering flag */
    int     bit_rate;       /**< Core stream bit rate in bytes per second,
                                 zero or negative if unavailable */
    int     npcmblocks;     /**< Number of audio sample blocks in a frame */
    bool    ext_audio_present;  /**< Extended audio present */
    int     ext_audio_type;     /**< Extended audio type (only meaningful
                                     when ext_audio_present is true) */
};

struct dcadec_exss_info {
    int nchannels;          /**< Number of audio channels encoded among all
                                 extension sub-streams */
    int sample_rate;        /**< Maximum encoded audio sample rate in Hz */
    int bits_per_sample;    /**< Highest encoded PCM resolution in bits */
    int profile;            /**< Type of DTS profile encoded */
    bool embedded_stereo;   /**< 2.0 downmix has been embedded into the stream */
    bool embedded_6ch;      /**< 5.1 downmix has been embedded into the stream */
    int spkr_mask;          /**< Speaker activity mask, zero if unavailable */
    int matrix_encoding;    /**< Matrix encoding type */
};

typedef void (*dcadec_log_cb)(int level, const char *file, int line,
                              const char *message, void *cbarg);

/**
 * Parse DTS packet. Packet data must be already converted into 16-bit
 * big-endian format. Caller must have already established byte stream
 * synchronization. Packet must start with a valid 32-bit sync word. EXSS frame
 * must be aligned on 4-byte boundary if present in the packet.
 *
 * @param dca   Pointer to decoder context.
 *
 * @param data  Pointer to packet data buffer. Buffer must be aligned on 4-byte
 *              boundary and padded at the end with DCADEC_BUFFER_PADDING bytes.
 *
 * @param size  Size in bytes of packet data. Size should not include padding.
 *
 * @return      0 or positive warning code on success, negative error code on
 *              failure.
 */
DCADEC_API int dcadec_context_parse(struct dcadec_context *dca, uint8_t *data, size_t size);

/**
 * Get information about DTS core payload of the parsed packet.
 *
 * @param dca   Pointer to decoder context.
 *
 * @return      Pointer to DTS core information structure on success,
 *              NULL on failure. Returned data should be freed with
 *              dcadec_context_free_core_info() function.
 */
DCADEC_API struct dcadec_core_info *dcadec_context_get_core_info(struct dcadec_context *dca);

/**
 * Free DTS core information structure.
 *
 * @param info  Pointer to DTS core information structure.
 */
DCADEC_API void dcadec_context_free_core_info(struct dcadec_core_info *info);

/**
 * Get information about extension sub-stream (EXSS) payload of the parsed
 * packet. When no EXSS is present information about extended audio in core
 * sub-stream is returned.
 *
 * @param dca   Pointer to decoder context.
 *
 * @return      Pointer to EXSS information structure on success,
 *              NULL on failure. Returned data should be freed with
 *              dcadec_context_free_exss_info() function.
 */
DCADEC_API struct dcadec_exss_info *dcadec_context_get_exss_info(struct dcadec_context *dca);

/**
 * Free EXSS information structure.
 *
 * @param info  Pointer to EXSS information structure.
 */
DCADEC_API void dcadec_context_free_exss_info(struct dcadec_exss_info *info);

/**
 * Filter the parsed packet and return per-channel PCM data. All parameters
 * except decoder context are optional and can be NULL. This function should be
 * called at least once after each successfull call to dcadec_context_parse().
 * Multiple calls per packet are allowed and return the same data.
 *
 * @param dca       Pointer to decoder context.
 *
 * @param samples   Filled with address of array of pointers to planes
 *                  containing PCM data for active channels. This data is valid
 *                  until the next call to dcadec_context_parse() or _destroy().
 *                  Returned array is tightly packed, there are no gaps for
 *                  missing channels. Use channel_mask to determine total number
 *                  of channels and size of returned array. By default channels
 *                  are ordered according to WAVEFORMATEXTENSIBLE specification,
 *                  but if DCADEC_FLAG_NATIVE_LAYOUT flag was set when creating
 *                  decoder context, returned channels are in native DTS order.
 *
 * @param nsamples  Filled with number of PCM samples in each returned plane.
 *
 * @param channel_mask  Filled with bit mask indicating active channels. 1 at
 *                      the given bit position (counting from the least
 *                      significant bit) means that the channel is present in
 *                      the array of pointers to planes, 0 otherwise. Number of
 *                      bits set to 1 indicates the total number of planes
 *                      returned.
 *
 * @param sample_rate       Filled with decoded audio sample rate in Hz.
 *
 * @param bits_per_sample   Filled with decoded audio PCM resolution in bits.
 *
 * @param profile           Filled with type of DTS profile actually decoded.
 *                          This can be different from encoded profile since
 *                          certain extensions may be not decoded.
 *
 * @return                  0 or positive warning code on success, negative
 *                          error code on failure. Return value of 0 indicates
 *                          that no errors affecting audio integrity were
 *                          detected. When profile indicates Master Audio,
 *                          positive return value indicates that audio has not
 *                          been losslessly reconstructed for at least some part
 *                          of this frame.
 */
DCADEC_API int dcadec_context_filter(struct dcadec_context *dca, int ***samples,
                                     int *nsamples, int *channel_mask,
                                     int *sample_rate, int *bits_per_sample,
                                     int *profile);

/**
 * Clear all inter-frame history of the decoder. Call this before parsing
 * packets out of sequence, e.g. after seeking to the arbitrary position within
 * the DTS stream.
 *
 * @param dca   Pointer to decoder context.
 */
DCADEC_API void dcadec_context_clear(struct dcadec_context *dca);

/**
 * Create DTS decoder context.
 *
 * @param flags Any number of DCADEC_FLAG_* constants OR'ed together.
 *
 * @return      Pointer to decoder context on success, NULL on failure.
 */
DCADEC_API struct dcadec_context *dcadec_context_create(int flags);

/**
 * Destroy DTS decoder context.
 *
 * @param dca   Pointer to decoder context.
 */
DCADEC_API void dcadec_context_destroy(struct dcadec_context *dca);

/**
 * Set or clear logging callback for decoder context.
 *
 * @param dca       Pointer to decoder context.
 *
 * @param log_cb    Pointer to logging callback function. Pass NULL to disable
 *                  logging.
 *
 * @param log_cbarg Opaque pointer that will be passed through to callback
 *                  function.
 */
DCADEC_API void dcadec_context_set_log_cb(struct dcadec_context *dca,
                                          dcadec_log_cb log_cb,
                                          void *log_cbarg);

/**
 * Convert negative libdcadec error code or positive warning code into string.
 *
 * @param errnum    Error or warning code returned by libdcadec function.
 *
 * @return          Constant string describing error or warning code.
 */
DCADEC_API const char *dcadec_strerror(int errnum);

/**
 * Get libdcadec API version.
 *
 * @return  API version code of the currently running libdcadec.
 */
DCADEC_API unsigned int dcadec_version(void);

#endif
