// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    flac.h

    FLAC compression wrappers

***************************************************************************/

#pragma once

#ifndef __FLAC_H__
#define __FLAC_H__

#include "osdcore.h"
#include "corefile.h"

#ifdef FLAC__NO_DLL
#include "../../lib/libflac/include/FLAC/all.h"
#else
#include <FLAC/all.h>
#endif


//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************

// ======================> flac_encoder

class flac_encoder
{
public:
	// construction/destruction
	flac_encoder();
	flac_encoder(void *buffer, UINT32 buflength);
	flac_encoder(core_file &file);
	~flac_encoder();

	// configuration
	void set_sample_rate(UINT32 sample_rate) { m_sample_rate = sample_rate; }
	void set_num_channels(UINT8 num_channels) { m_channels = num_channels; }
	void set_block_size(UINT32 block_size) { m_block_size = block_size; }
	void set_strip_metadata(bool strip) { m_strip_metadata = strip; }

	// getters (valid after reset)
	FLAC__StreamEncoderState state() const { return FLAC__stream_encoder_get_state(m_encoder); }
	const char *state_string() const { return FLAC__stream_encoder_get_resolved_state_string(m_encoder); }

	// reset
	bool reset();
	bool reset(void *buffer, UINT32 buflength);
	bool reset(core_file &file);

	// encode a buffer
	bool encode_interleaved(const INT16 *samples, UINT32 samples_per_channel, bool swap_endian = false);
	bool encode(INT16 *const *samples, UINT32 samples_per_channel, bool swap_endian = false);

	// finish up
	UINT32 finish();

private:
	// internal helpers
	void init_common();
	static FLAC__StreamEncoderWriteStatus write_callback_static(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data);
	FLAC__StreamEncoderWriteStatus write_callback(const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame);

	// internal state
	FLAC__StreamEncoder *   m_encoder;              // actual encoder
	core_file *             m_file;                 // output file
	UINT32                  m_compressed_offset;    // current offset with the compressed stream
	FLAC__byte *            m_compressed_start;     // start of compressed data
	UINT32                  m_compressed_length;    // length of the compressed stream

	// parameters
	UINT32                  m_sample_rate;          // sample rate
	UINT8                   m_channels;             // number of channels
	UINT32                  m_block_size;           // block size

	// header stripping
	bool                    m_strip_metadata;       // strip the the metadata?
	UINT32                  m_ignore_bytes;         // how many bytes to ignore when writing
	bool                    m_found_audio;          // have we hit the audio yet?
};


// ======================> flac_decoder

class flac_decoder
{
public:
	// construction/destruction
	flac_decoder();
	flac_decoder(const void *buffer, UINT32 length, const void *buffer2 = NULL, UINT32 length2 = 0);
	flac_decoder(core_file &file);
	~flac_decoder();

	// getters (valid after reset)
	UINT32 sample_rate() const { return m_sample_rate; }
	UINT8 channels() const { return m_channels; }
	UINT8 bits_per_sample() const { return m_bits_per_sample; }
	UINT32 total_samples() const { return FLAC__stream_decoder_get_total_samples(m_decoder); }
	FLAC__StreamDecoderState state() const { return FLAC__stream_decoder_get_state(m_decoder); }
	const char *state_string() const { return FLAC__stream_decoder_get_resolved_state_string(m_decoder); }

	// reset
	bool reset();
	bool reset(const void *buffer, UINT32 length, const void *buffer2 = NULL, UINT32 length2 = 0);
	bool reset(UINT32 sample_rate, UINT8 num_channels, UINT32 block_size, const void *buffer, UINT32 length);
	bool reset(core_file &file);

	// decode to a buffer; num_samples must be a multiple of the block size
	bool decode_interleaved(INT16 *samples, UINT32 num_samples, bool swap_endian = false);
	bool decode(INT16 **samples, UINT32 num_samples, bool swap_endian = false);

	// finish up
	UINT32 finish();

private:
	// internal helpers
	static FLAC__StreamDecoderReadStatus read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
	FLAC__StreamDecoderReadStatus read_callback(FLAC__byte buffer[], size_t *bytes);
	static void metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
	static FLAC__StreamDecoderTellStatus tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
	static FLAC__StreamDecoderWriteStatus write_callback_static(const FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
	FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
	static void error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);

	// output state
	FLAC__StreamDecoder *   m_decoder;              // actual encoder
	core_file *             m_file;                 // output file
	UINT32                  m_sample_rate;          // decoded sample rate
	UINT8                   m_channels;             // decoded number of channels
	UINT8                   m_bits_per_sample;      // decoded bits per sample
	UINT32                  m_compressed_offset;    // current offset in compressed data
	const FLAC__byte *      m_compressed_start;     // start of compressed data
	UINT32                  m_compressed_length;    // length of compressed data
	const FLAC__byte *      m_compressed2_start;    // start of compressed data
	UINT32                  m_compressed2_length;   // length of compressed data
	INT16 *                 m_uncompressed_start[8];// pointer to start of uncompressed data (up to 8 streams)
	UINT32                  m_uncompressed_offset;  // current position in uncompressed data
	UINT32                  m_uncompressed_length;  // length of uncompressed data
	bool                    m_uncompressed_swap;    // swap uncompressed sample data
	UINT8                   m_custom_header[0x2a];  // custom header
};


#endif // __FLAC_H__
