/*
 *  21.04.2004 Original verion. davagin@udm.ru.
 *-----------------------------------------------------------------------
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *----------------------------------------------------------------------
 */
package davaguine.jmac.encoder;

import davaguine.jmac.info.CompressionLevel;
import davaguine.jmac.info.InputSource;
import davaguine.jmac.info.WaveFormat;
import davaguine.jmac.tools.ByteBuffer;
import davaguine.jmac.tools.File;
import davaguine.jmac.tools.IntegerPointer;

import java.io.IOException;

/**
 * Author: Dmitry Vaguine
 * Date: 07.05.2004
 * Time: 13:10:46
 */
public abstract class IAPECompress {
    public final static int CREATE_WAV_HEADER_ON_DECOMPRESSION = -1;
    public final static int MAX_AUDIO_BYTES_UNKNOWN = -1;

    /**
     * ******************************************************************************************
     * Start
     * *******************************************************************************************
     */

    //////////////////////////////////////////////////////////////////////////////////////////////
    // Start(...) / StartEx(...) - starts encoding
    //
    // Parameters:
    //	CIO * pioOutput / const wchar_t * pFilename
    //		the output... either a filename or an I/O source
    //	WAVEFORMATEX * pwfeInput
    //		format of the audio to encode (use FillWaveFormatEx() if necessary)
    //	int nMaxAudioBytes
    //		the absolute maximum audio bytes that will be encoded... encoding fails with a
    //		ERROR_APE_COMPRESS_TOO_MUCH_DATA if you attempt to encode more than specified here
    //		(if unknown, use MAX_AUDIO_BYTES_UNKNOWN to allocate as much storage in the seek table as
    //		possible... limit is then 2 GB of data (~4 hours of CD music)... this wastes around
    //		30kb, so only do it if completely necessary)
    //	int nCompressionLevel
    //		the compression level for the APE file (fast - extra high)
    //		(note: extra-high is much slower for little gain)
    //	const unsigned char * pHeaderData
    //		a pointer to a buffer containing the WAV header (data before the data block in the WAV)
    //		(note: use NULL for on-the-fly encoding... see next parameter)
    //	int nHeaderBytes
    //		number of bytes in the header data buffer (use CREATE_WAV_HEADER_ON_DECOMPRESSION and
    //		NULL for the pHeaderData and MAC will automatically create the appropriate WAV header
    //		on decompression)
    //////////////////////////////////////////////////////////////////////////////////////////////
    public void Start(String pOutputFilename, WaveFormat pwfeInput) throws IOException {
        Start(pOutputFilename, pwfeInput, MAX_AUDIO_BYTES_UNKNOWN, CompressionLevel.COMPRESSION_LEVEL_NORMAL, null, CREATE_WAV_HEADER_ON_DECOMPRESSION);
    }

    public void Start(String pOutputFilename, WaveFormat pwfeInput, int nMaxAudioBytes) throws IOException {
        Start(pOutputFilename, pwfeInput, nMaxAudioBytes, CompressionLevel.COMPRESSION_LEVEL_NORMAL, null, CREATE_WAV_HEADER_ON_DECOMPRESSION);
    }

    public void Start(String pOutputFilename, WaveFormat pwfeInput, int nMaxAudioBytes, int nCompressionLevel) throws IOException {
        Start(pOutputFilename, pwfeInput, nMaxAudioBytes, nCompressionLevel, null, CREATE_WAV_HEADER_ON_DECOMPRESSION);
    }

    public void Start(String pOutputFilename, WaveFormat pwfeInput, int nMaxAudioBytes, int nCompressionLevel, byte[] pHeaderData) throws IOException {
        Start(pOutputFilename, pwfeInput, nMaxAudioBytes, nCompressionLevel, pHeaderData, CREATE_WAV_HEADER_ON_DECOMPRESSION);
    }

    public abstract void Start(String pOutputFilename, WaveFormat pwfeInput, int nMaxAudioBytes, int nCompressionLevel, byte[] pHeaderData, int nHeaderBytes) throws IOException;

    public void StartEx(File pioOutput, WaveFormat pwfeInput) throws IOException {
        StartEx(pioOutput, pwfeInput, MAX_AUDIO_BYTES_UNKNOWN, CompressionLevel.COMPRESSION_LEVEL_NORMAL, null, CREATE_WAV_HEADER_ON_DECOMPRESSION);
    }

    public void StartEx(File pioOutput, WaveFormat pwfeInput, int nMaxAudioBytes) throws IOException {
        StartEx(pioOutput, pwfeInput, nMaxAudioBytes, CompressionLevel.COMPRESSION_LEVEL_NORMAL, null, CREATE_WAV_HEADER_ON_DECOMPRESSION);
    }

    public void StartEx(File pioOutput, WaveFormat pwfeInput, int nMaxAudioBytes, int nCompressionLevel) throws IOException {
        StartEx(pioOutput, pwfeInput, nMaxAudioBytes, nCompressionLevel, null, CREATE_WAV_HEADER_ON_DECOMPRESSION);
    }

    public void StartEx(File pioOutput, WaveFormat pwfeInput, int nMaxAudioBytes, int nCompressionLevel, byte[] pHeaderData) throws IOException {
        StartEx(pioOutput, pwfeInput, nMaxAudioBytes, nCompressionLevel, pHeaderData, CREATE_WAV_HEADER_ON_DECOMPRESSION);
    }

    public abstract void StartEx(File pioOutput, WaveFormat pwfeInput, int nMaxAudioBytes, int nCompressionLevel, byte[] pHeaderData, int nHeaderBytes) throws IOException;

    /**
     * ******************************************************************************************
     * Add / Compress Data
     * - there are 3 ways to add data:
     * 1) simple call AddData(...)
     * 2) lock MAC's buffer, copy into it, and unlock (LockBuffer(...) / UnlockBuffer(...))
     * 3) from an I/O source (AddDataFromInputSource(...))
     * *******************************************************************************************
     */

    //////////////////////////////////////////////////////////////////////////////////////////////
    // AddData(...) - adds data to the encoder
    //
    // Parameters:
    //	unsigned char * pData
    //		a pointer to a buffer containing the raw audio data
    //	int nBytes
    //		the number of bytes in the buffer
    //////////////////////////////////////////////////////////////////////////////////////////////
    public abstract void AddData(byte[] pData, int nBytes) throws IOException;

    //////////////////////////////////////////////////////////////////////////////////////////////
    // GetBufferBytesAvailable(...) - returns the number of bytes available in the buffer
    //	(helpful when locking)
    //////////////////////////////////////////////////////////////////////////////////////////////
    public abstract int GetBufferBytesAvailable();

    //////////////////////////////////////////////////////////////////////////////////////////////
    // LockBuffer(...) - locks MAC's buffer so we can copy into it
    //
    // Parameters:
    //	int * pBytesAvailable
    //		returns the number of bytes available in the buffer (DO NOT COPY MORE THAN THIS IN)
    //
    // Return:
    //	pointer to the buffer (add at that location)
    //////////////////////////////////////////////////////////////////////////////////////////////
    public abstract ByteBuffer LockBuffer(IntegerPointer pBytesAvailable);

    //////////////////////////////////////////////////////////////////////////////////////////////
    // UnlockBuffer(...) - releases the buffer
    //
    // Parameters:
    //	int nBytesAdded
    //		the number of bytes copied into the buffer
    //	BOOL bProcess
    //		whether MAC should process as much as possible of the buffer
    //////////////////////////////////////////////////////////////////////////////////////////////
    public void UnlockBuffer(int nBytesAdded) throws IOException {
        UnlockBuffer(nBytesAdded, true);
    }

    public abstract void UnlockBuffer(int nBytesAdded, boolean bProcess) throws IOException;

    //////////////////////////////////////////////////////////////////////////////////////////////
    // AddDataFromInputSource(...) - use a CInputSource (input source) to add data
    //
    // Parameters:
    //	CInputSource * pInputSource
    //		a pointer to the input source
    //	int nMaxBytes
    //		the maximum number of bytes to let MAC add (-1 if MAC can add any amount)
    //	int * pBytesAdded
    //		returns the number of bytes added from the I/O source
    //////////////////////////////////////////////////////////////////////////////////////////////
    public int AddDataFromInputSource(InputSource pInputSource) throws IOException {
        return AddDataFromInputSource(pInputSource, -1);
    }

    public abstract int AddDataFromInputSource(InputSource pInputSource, int nMaxBytes) throws IOException;

    /**
     * ******************************************************************************************
     * Finish / Kill
     * *******************************************************************************************
     */

    //////////////////////////////////////////////////////////////////////////////////////////////
    // Finish(...) - ends encoding and finalizes the file
    //
    // Parameters:
    //	unsigned char * pTerminatingData
    //		a pointer to a buffer containing the information to place at the end of the APE file
    //		(comprised of the WAV terminating data (data after the data block in the WAV) followed
    //		by any tag information)
    //	int nTerminatingBytes
    //		number of bytes in the terminating data buffer
    //	int nWAVTerminatingBytes
    //		the number of bytes of the terminating data buffer that should be appended to a decoded
    //		WAV file (it's basically nTerminatingBytes - the bytes that make up the tag)
    //////////////////////////////////////////////////////////////////////////////////////////////
    public abstract void Finish(byte[] pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes) throws IOException;

    //////////////////////////////////////////////////////////////////////////////////////////////
    // Kill(...) - stops encoding and deletes the output file
    // --- NOT CURRENTLY IMPLEMENTED ---
    //////////////////////////////////////////////////////////////////////////////////////////////
    public abstract void Kill();

    public static IAPECompress CreateIAPECompress() {
        return new APECompress();
    }

}
