#include <windows.h>
#include "maplay.h"
#include "helper.h"
#include "player.h"

void CPlayer::NetStreamingThread()
{
	BYTE bRead[NET_READ_BUFF_LEN];
	DWORD cbInBuf;

	m_StreamingStatus = MAP_STREAMING_CONNECTING;
	UpdateStatus(MAP_STATUS_WAIT);

	if (!m_Receiver.Connect()) {
		UnpreparePlayback(FALSE, TRUE);
		return;
	}

	m_StreamingStatus = MAP_STREAMING_BUFFERING;

	if (!m_Receiver.Read(bRead, NET_READ_BUFF_LEN, &cbInBuf)) {
		UnpreparePlayback(FALSE, TRUE);
		return;
	}

	if (NetParseMpegStream(bRead, cbInBuf))
		m_fNet = NET_OPEN_MPEG;
	else if (NetParseOvStream(bRead, cbInBuf))
		m_fNet = NET_OPEN_OV;
	else if (PlugInParseStream(bRead, cbInBuf))
		m_fNet = NET_OPEN_PLUGIN;
	else {
		UnpreparePlayback(FALSE, TRUE);
		return;
	}

	if (!PreparePlayback()) {
		UnpreparePlayback(FALSE, TRUE);
		return;
	}

	if (!WaitForPrebuffering(m_Receiver.GetPrebufferingCount())) {
		UnpreparePlayback(FALSE, TRUE);
		return;
	}

	m_StreamingStatus = MAP_STREAMING_CONNECTED;
	UpdateStatus(MAP_STATUS_PLAY);

	if (m_fNet == NET_OPEN_OV)
		NetOvStreaming(bRead, cbInBuf);
	else if (m_fNet == NET_OPEN_PLUGIN)
		PlugInStreaming(bRead, cbInBuf);
	else 
		NetMpegStreaming(bRead, cbInBuf);
}

void CPlayer::NetStop()
{
	if (m_pOvd_buf) {
		ovd_uninit_stream(m_pOvd_buf);
		m_pOvd_buf = NULL;
	}
	*m_szOvdTitle = NULL;

	PlugInStopStreaming();
}

void CPlayer::NetClose()
{
	m_fNet = NET_OPEN_NONE;
	m_StreamingStatus = MAP_STREAMING_DISABLED;
}

BOOL CPlayer::NetParseMpegStream(LPBYTE pbBuf, DWORD cbBuf)
{
	DWORD i = 0;
	MPEG_AUDIO_INFO info;
	while (i < cbBuf - 4) {
		if (ParseHeader(pbBuf + i, &info)) {
			if (i + info.nFrameSize < cbBuf - 4) {
				if (CheckAudioHeader(pbBuf + i + info.nFrameSize)) {
					m_Info = info;
					break;
				}
			}
		}
		i++;
	}
	if (m_Info.nVersion == 0 || m_Info.nLayer == 0 ||
		m_Info.nSamplingRate == 0 || m_Info.nChannels == 0)
		return FALSE;

	return TRUE;
}

BOOL CPlayer::NetReconnect()
{
	if (m_Receiver.IsEos()) {
		UnpreparePlayback(TRUE);
		return FALSE;
	}

	if (!m_Receiver.IsShoutcast()) {
		UnpreparePlayback(TRUE);
		return FALSE;
	}

	MAP_STATUS fStatus = m_Status;
	m_StreamingStatus = MAP_STREAMING_CONNECTING;
	UpdateStatus(MAP_STATUS_WAIT);

	m_Receiver.Disconnect();
	if (!m_Receiver.Connect()) {
		UnpreparePlayback(FALSE, TRUE);
		return FALSE;
	}

	m_StreamingStatus = MAP_STREAMING_BUFFERING;

	if (!WaitForPrebuffering()) {
		UnpreparePlayback(FALSE, TRUE);
		return FALSE;
	}

	m_StreamingStatus = MAP_STREAMING_CONNECTED;
	UpdateStatus(m_Status == MAP_STATUS_WAIT ? fStatus : m_Status);
	return TRUE;
}

void CPlayer::NetMpegStreaming(LPBYTE pbBuf, DWORD cbBuf)
{
	DWORD cbInBuf, cbInBufLeft = 0;
	cbInBuf = cbBuf;

	while (TRUE) {
		if (m_fStop) {
			UnpreparePlayback();
			return;
		}

		if (m_Receiver.GetBufferingCount() < 2) {
			while (TRUE) {
				Sleep(1);
				if (m_Output.GetBufferingCount() < 1)
					break;
				if (m_Receiver.GetBufferingCount() > 1)
					goto read;
				if (m_fStop) {
					UnpreparePlayback();
					return;
				}
			}

			if (!NetReconnect())
				return;

			m_Output.Pause(TRUE);
			m_fPlay = TRUE;
		}

read:
		if (!m_Receiver.Read(pbBuf + cbInBufLeft, NET_READ_BUFF_LEN - cbInBufLeft, &cbInBuf) || !cbInBuf) {
			UnpreparePlayback(TRUE);
			return;
		}

		cbInBufLeft += cbInBuf;
		cbInBuf = 0;

		if (MpgRender(pbBuf, cbInBufLeft, &cbInBuf) == MAD_FATAL_ERR) {
			UnpreparePlayback(FALSE, TRUE);
			return;
		}
		
		memmove(pbBuf, pbBuf + cbInBuf, cbInBufLeft - cbInBuf);
		cbInBufLeft -= cbInBuf;
	}
}

BOOL CPlayer::NetParseOvStream(LPBYTE pbBuf, DWORD cbBuf)
{
	if (cbBuf > OVD_STREAM_BUF_LEN)
		return FALSE;

	m_pOvd_buf = ovd_init_stream();
	memcpy(m_pOvd_buf->buf, pbBuf, min(cbBuf, OVD_STREAM_BUF_LEN));
	if (!ovd_parse_stream(m_pOvd_buf))
		return FALSE;

	ovd_info oi;
	memset(&oi, 0, sizeof(oi));
	memset(&m_Info, 0, sizeof(m_Info));
	if (!ovd_get_stream_info(m_pOvd_buf, &oi))
		return FALSE;

	m_Info.nChannels = oi.channels;
	m_Info.nSamplingRate = oi.rate;
	m_Info.nBitRate = oi.bitrate_nominal / 1000;
	m_Info.nFrameSize = 0;
	m_Info.nSamplesPerFrame = 0;

	if (m_Info.nSamplingRate == 0 || m_Info.nChannels == 0)
		return FALSE;

	return TRUE;
}

void CPlayer::NetOvStreaming(LPBYTE pbBuf, DWORD cbBuf)
{
	int nRet, nOutput;
	if (cbBuf < OVD_STREAM_BUF_LEN) {
		UnpreparePlayback(FALSE, TRUE);
		return;
	}

	ovd_comment comment;
	
	while (TRUE) {
		ovd_get_stream_comment(m_pOvd_buf, &comment);
		if (_tcslen(comment.szTitle)) {
			if (_tcslen(comment.szArtist))
				wsprintf(m_szOvdTitle, _T("%s - %s"), comment.szArtist, comment.szTitle);
			else
				_tcscpy(m_szOvdTitle, comment.szTitle);
			PostCallbackMessage(MAP_MSG_STREAM_TITLE, (WPARAM)m_szOvdTitle, 0);
		}

		if (m_fStop) {
			UnpreparePlayback();
			return;
		}

		if (m_Receiver.GetBufferingCount() < 2) {
			while (TRUE) {
				Sleep(1);
				if (m_Output.GetBufferingCount() < 1)
					break;
				if (m_Receiver.GetBufferingCount() > 1)
					goto read;
				if (m_fStop) {
					UnpreparePlayback();
					return;
				}
			}

			if (!NetReconnect())
				return;

			m_Output.Pause(TRUE);
			m_fPlay = TRUE;

			DWORD dwRead;
			if (!m_Receiver.Read(pbBuf, cbBuf, &dwRead) || !dwRead) {
				UnpreparePlayback(TRUE);
				return;
			}

			ovd_uninit_stream(m_pOvd_buf);
			m_pOvd_buf = NULL;
			if (!NetParseOvStream(pbBuf, cbBuf)) {
				UnpreparePlayback(TRUE);
				return;
			}
		}

read:
		if (m_pOvd_buf->len) {
			if (!m_Receiver.Read((BYTE*)m_pOvd_buf->buf, OVD_STREAM_BUF_LEN, &m_pOvd_buf->len) || !m_pOvd_buf->len) {
				UnpreparePlayback(TRUE);
				return;
			}
		}

		if (!m_pOutHdr) {
			m_pOutHdr = m_Output.GetBuffer();
			m_cbOutBufLeft = m_cbOutBuf;
		}

		nRet = ovd_decode_stream(m_pOvd_buf, 
			(BYTE*)m_pOutHdr->lpData + (m_cbOutBuf - m_cbOutBufLeft), m_cbOutBufLeft, &nOutput);

		Preamp((LPBYTE)m_pOutHdr->lpData + (m_cbOutBuf - m_cbOutBufLeft), nOutput);

		m_cbOutBufLeft -= nOutput;

		switch (nRet) {
		case OVD_FATAL_ERR:
		case OVD_ERR:
			UnpreparePlayback(FALSE, TRUE);
			break;
		case OVD_EOF:
			UnpreparePlayback(TRUE);
			return;
		case OVD_NEED_MORE_OUTPUT:
			OutputBuffer(m_pOutHdr, m_cbOutBuf - m_cbOutBufLeft);
			m_pOutHdr = NULL;
			break;
		}
	}
}
