#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include "quakedef.h"
#include "iframeencoder.h"
#include "interface.h"
#include "milesbase.h"
#include "tier0/dbg.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

void Con_Printf(char *pMsg, ...)
{
}


class FrameEncoder_Miles : public IFrameEncoder
{
protected:
	virtual			~FrameEncoder_Miles();

public:
					FrameEncoder_Miles();

	virtual bool	Init(int &rawFrameSize, int &encodedFrameSize);
	virtual void	Release();
	virtual void	EncodeFrame(const char *pUncompressed, char *pCompressed);
	virtual void	DecodeFrame(const char *pCompressed, char *pDecompressed);
	virtual bool	ResetState();


private:

	void			Shutdown();

	static S32 CALLBACK EncodeStreamCB(
		U32 user,				// User value passed to ASI_open_stream()
		void FAR *dest,			// Location to which stream data should be copied by app
		S32 bytes_requested,	// # of bytes requested by ASI codec
		S32 offset				// If not -1, application should seek to this point in stream
		);

	static S32 CALLBACK DecodeStreamCB(
		U32 user,				// User value passed to ASI_open_stream()
		void FAR *dest,			// Location to which stream data should be copied by app
		S32 bytes_requested,	// # of bytes requested by ASI codec
		S32 offset				// If not -1, application should seek to this point in stream
		);

	void			FigureOutFrameSizes();
	

private:

	// Encoder stuff.	
	ASISTRUCT			m_Encoder;

	// Decoder stuff.
	ASISTRUCT			m_Decoder;

	// Destination for encoding and decoding.
	const char			*m_pSrc;
	int					m_SrcLen;
	int					m_CurSrcPos;

	// Frame sizes..
	int					m_nRawBytes;
	int					m_nEncodedBytes;
};



// ------------------------------------------------------------------------ //
// Helper functions.
// ------------------------------------------------------------------------ //
void Convert16UnsignedToSigned(short *pDest, int nSamples)
{
	for(int i=0; i < nSamples; i++)
	{
		int val = *((unsigned short*)&pDest[i]) - (1 << 15);
		pDest[i] = (short)val;
	}
}


void Convert16SignedToUnsigned(short *pDest, int nSamples)
{
	for(int i=0; i < nSamples; i++)
	{
		int val = *((short*)&pDest[i]) + (1 << 15);
		*((unsigned short*)&pDest[i]) = (unsigned short)val;
	}
}


// ------------------------------------------------------------------------ //
// FrameEncoder_Miles functions.
// ------------------------------------------------------------------------ //
FrameEncoder_Miles::FrameEncoder_Miles()
{
}


FrameEncoder_Miles::~FrameEncoder_Miles()
{
	Shutdown();
}


bool FrameEncoder_Miles::Init(int &rawFrameSize, int &encodedFrameSize)
{
	Shutdown();


	// This tells what protocol we're using.
	C8 suffix[128] = ".v12"; // (.v12, .v24, .v29, or .raw)

	// encoder converts from RAW to v12
	if ( !m_Encoder.Init( (void *)this, ".RAW", suffix, &FrameEncoder_Miles::EncodeStreamCB ) )
	{
		Con_Printf("(FrameEncoder_Miles): Can't initialize ASI encoder.\n");
		Shutdown();
		return false;
	}
	
	// decoder converts from v12 to RAW
	if ( !m_Decoder.Init( (void *)this, suffix, ".RAW", &FrameEncoder_Miles::DecodeStreamCB ) )
	{
		Con_Printf("(FrameEncoder_Miles): Can't initialize ASI decoder.\n");
		Shutdown();
		return false;
	}


	FigureOutFrameSizes();

	
	// Output..	
	rawFrameSize = m_nRawBytes * 2; // They'll be using 16-bit samples and we're quantizing to 8-bit.
	encodedFrameSize = m_nEncodedBytes;

	return true;
}


void FrameEncoder_Miles::Release()
{
	delete this;
}


void FrameEncoder_Miles::EncodeFrame(const char *pUncompressedBytes, char *pCompressed)
{
	char samples[1024];

	if(!m_Encoder.IsActive() || m_nRawBytes > sizeof(samples))
		return;

	const short *pUncompressed = (const short*)pUncompressedBytes;
	for(int i=0; i < m_nRawBytes; i++)
		samples[i] = (char)(pUncompressed[i] >> 8);

	m_pSrc = samples;
	m_SrcLen = m_nRawBytes;
	m_CurSrcPos = 0;

	U32 len = m_Encoder.Process( pCompressed, m_nEncodedBytes );

	Assert(len == (U32)m_nEncodedBytes);	
}


void FrameEncoder_Miles::DecodeFrame(const char *pCompressed, char *pDecompressed)
{
	if(!m_Decoder.IsActive())
		return;

	m_pSrc = pCompressed;
	m_SrcLen = m_nEncodedBytes;
	m_CurSrcPos = 0;

	U32 outputSize = m_nRawBytes*2;
	U32 len = m_Decoder.Process( pDecompressed, outputSize );
	
	Assert(len == outputSize);
}


void FrameEncoder_Miles::Shutdown()
{
	m_Decoder.Shutdown();
	m_Encoder.Shutdown();
}


bool FrameEncoder_Miles::ResetState()
{
	if(!m_Decoder.IsActive() || !m_Encoder.IsActive())
		return true;

	for(int i=0; i < 2; i++)
	{
		char data[2048], compressed[2048];
		memset(data, 0, sizeof(data));
		m_pSrc = data;
		m_SrcLen = m_nRawBytes;
		m_CurSrcPos = 0;
		
		U32 len = m_Encoder.Process( compressed, m_nEncodedBytes );
		
		Assert(len == (U32)m_nEncodedBytes);

		m_pSrc = compressed;
		m_SrcLen = m_nEncodedBytes;
		m_CurSrcPos = 0;

		m_Decoder.Process( data, m_nRawBytes * 2 );
	}

	// Encode and decode a couple frames of zeros.
	return true;
}


S32 FrameEncoder_Miles::EncodeStreamCB(
	U32 user,				// User value passed to ASI_open_stream()
	void FAR *dest,			// Location to which stream data should be copied by app
	S32 bytes_requested,	// # of bytes requested by ASI codec
	S32 offset				// If not -1, application should seek to this point in stream
	)
{
	FrameEncoder_Miles *pThis = (FrameEncoder_Miles*)user;
	Assert(pThis && offset == -1);

	// Figure out how many samples we can safely give it.
	int maxSamples = pThis->m_SrcLen - pThis->m_CurSrcPos;
	int samplesToGive = min(maxSamples, bytes_requested/2);

	// Convert to 16-bit signed mono.
	short *pOut = (short*)dest;
	for(int i=0; i < samplesToGive; i++)
	{
		pOut[i] = pThis->m_pSrc[pThis->m_CurSrcPos+i] << 8;
	}

	pThis->m_CurSrcPos += samplesToGive;
	return samplesToGive * 2;
}


S32 FrameEncoder_Miles::DecodeStreamCB(
	U32 user,				// User value passed to ASI_open_stream()
	void FAR *dest,			// Location to which stream data should be copied by app
	S32 bytes_requested,	// # of bytes requested by ASI codec
	S32 offset				// If not -1, application should seek to this point in stream
	)
{
	FrameEncoder_Miles *pThis = (FrameEncoder_Miles*)user;
	Assert(pThis && offset == -1);

	int maxBytes = pThis->m_SrcLen - pThis->m_CurSrcPos;
	int bytesToGive = min(maxBytes, bytes_requested);
	memcpy(dest, &pThis->m_pSrc[pThis->m_CurSrcPos], bytesToGive);

	pThis->m_CurSrcPos += bytesToGive;
	return bytesToGive;
}


void FrameEncoder_Miles::FigureOutFrameSizes()
{
	// Figure out the frame sizes. It is probably not prudent in general to assume fixed frame sizes with Miles codecs
	// but it works with the voxware codec right now and simplifies things a lot.
	m_nRawBytes = (int)m_Encoder.GetAttribute( m_Encoder.INPUT_BLOCK_SIZE );
	
	char uncompressed[1024];
	char compressed[1024];

	Assert(m_nRawBytes <= sizeof(uncompressed));
	
	m_pSrc = uncompressed;
	m_SrcLen = m_nRawBytes;
	m_CurSrcPos = 0;

	m_nEncodedBytes = (int)m_Encoder.Process( compressed, sizeof(compressed) );
}



class IVoiceCodec;
extern IVoiceCodec* CreateVoiceCodec_Frame(IFrameEncoder *pEncoder);
void* CreateVoiceCodec_Miles()
{
	IFrameEncoder *pEncoder = new FrameEncoder_Miles;
	if(!pEncoder)
		return NULL;

	return CreateVoiceCodec_Frame(pEncoder);
}

EXPOSE_INTERFACE_FN(CreateVoiceCodec_Miles, IVoiceCodec, "vaudio_miles")


