/*  DecMPA - simple MPEG Audio decoding library.
    Copyright (C) 2002  Hauke Duden

    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

	For more information look at the file License.txt in this package.

	email: hazard_hd@users.sourceforge.net
*/


#ifndef _MPAFRAMEFINDER_H_
#define _MPAFRAMEFINDER_H_

#include "IFileAccess.h"
#include "mpegAudioFrame/mpegAudioHeader.h"

#include "MemBuffer.h"

class CMPAFrameFinder
{
public:
	CMPAFrameFinder();
	~CMPAFrameFinder();

	inline void SetLookAheadFrames(int nFrames);
	inline void SetAllowFrequencyChanges(bool bAllow);	

	inline int GetLookAheadFrames();
	inline bool GetAllowFrequencyChanges();	

	//if false, the finder needs more input data or the stream
	//invalid
	bool ReadNextFrame();

	inline bool IsStreamInvalid();
	inline bool KnowsCharacteristics();

	inline bool HasFrame();
	inline void* GetFrameData();
	inline int GetFrameSize();
	inline double GetAvgFrameSize();	
	inline MpegAudioHeader* GetFrameHeader();

	void SetInput(void* pBuffer,int nBytes,long nStreamPosition);
	bool ReadInput(IFileAccess* pAccess);

	//throw away buffered data and find next frame
	void Flush();

	//start with a completely blank slate for a new data stream
	void Restart();

	inline long GetFirstFramePosition();
	
protected:
	struct HeaderRecord
	{
		MpegAudioHeader Header;
		long nPosition;

		HeaderRecord* pNext;
	};

	bool FindCharacteristics();
	bool FindConsistentHeaders();
	bool CanHaveConsistentHeaders(HeaderRecord* pRecord,int nHeaderCount);
	bool HasConsistentHeaders(HeaderRecord* pCheckRecord,HeaderRecord* pRecord,int nHeadersNeeded);
	bool AreHeadersConsistent(HeaderRecord* pFirst,HeaderRecord* pSecond);

	bool IsConformingHeader(MpegAudioHeader* pHeader);

	void FindNextSync();
	void ReadHeader();
	void CheckHeader();
	void ReadFrame();
	
	void ResetState();

	void DeleteHeaderRecords();

	static bool IsValidHeader(MpegAudioHeader* pHeader);

	CMemBuffer m_OutBuffer;

	CMemBuffer* m_pInBuffer;
	CMemBuffer m_MyInBuffer;
	CMemBuffer m_UserInBuffer;

	int m_State;

	int m_nLookAheadFrames;
	bool m_bAllowFrequencyChanges;

	int m_nLayer;
	int m_nInputStereo;
	int m_nFrequency;
	int m_nLayer25;
	int m_nVersion;
	
	MpegAudioHeader m_Header;
	int m_nFrameSize;

	long m_nReadStartPosition;
	long m_nFirstFramePosition;

	enum
	{
		STATE_FINDCHARACTERISTICS=0,
		STATE_FINDSYNC,
		STATE_READHEADER,
		STATE_CHECKHEADER,
		STATE_READFRAME,

		STATE_HAVEFRAME
	};

	HeaderRecord* m_pFirstHeader;
	HeaderRecord* m_pLastHeader;

	HeaderRecord* m_pNewHeaderRecord;

	bool m_bStreamInvalid;
};

inline void CMPAFrameFinder::SetLookAheadFrames(int nFrames)
{
	if(nFrames<0)
		nFrames=0;

	m_nLookAheadFrames=nFrames;
}

inline void CMPAFrameFinder::SetAllowFrequencyChanges(bool bAllow)
{
	m_bAllowFrequencyChanges=bAllow;
}

inline int CMPAFrameFinder::GetLookAheadFrames()
{
	return m_nLookAheadFrames;
}

inline bool CMPAFrameFinder::GetAllowFrequencyChanges()
{
	return m_bAllowFrequencyChanges;
}

inline bool CMPAFrameFinder::HasFrame()
{
	return (m_State==STATE_HAVEFRAME);
}

inline void* CMPAFrameFinder::GetFrameData()
{
	return m_OutBuffer.GetPtr();
}

inline int CMPAFrameFinder::GetFrameSize()
{
	return m_OutBuffer.GetPos();
}

inline double CMPAFrameFinder::GetAvgFrameSize()
{
	return m_Header.GetAvgFrameSize();
}

inline MpegAudioHeader* CMPAFrameFinder::GetFrameHeader()
{
	return &m_Header;
}

inline bool CMPAFrameFinder::IsStreamInvalid()
{
	return m_bStreamInvalid;
}

inline bool CMPAFrameFinder::KnowsCharacteristics()
{
	return (m_State>STATE_FINDCHARACTERISTICS);
}

inline long CMPAFrameFinder::GetFirstFramePosition()
{
	return m_nFirstFramePosition;
}

#endif