#include "thread.h"
#include "common.h"
#include <unistd.h>

#include "device.h"

cThreadEm84xx::cThreadEm84xx(cDeviceEm84xx* Em84Device):
		cThread("Em84xx replay"),
		mActive(false),
		mEm84Device(Em84Device)
{
//	if (!mMpegDecoder.Init())
//		esyslog("em84xx: failed to initialize mpeg-decoder");
}

cThreadEm84xx::~cThreadEm84xx()
{
	Stop();
//	mMpegDecoder.Release();
}

void cThreadEm84xx::Start(void)
{
	if (pipe(mPipe) != 0) {
		LOG_ERROR;
		return;
	}
	fcntl(mPipe[1], F_SETFL, O_NONBLOCK);
	cThread::Start();
}

void cThreadEm84xx::Stop(void)
{
	if (mActive) {
		mActive = false;
		close(mPipe[1]);
		Cancel(3);
	}
}

void cThreadEm84xx::Action(void)
{
	em84MsgHeader header;
	cPoller poller;
	//bool trickMode = false;
	//bool paused = false;
	em84ThreadState threadState = em84Playing;
	FMP_BUFFER buffer, *fmpbuf = NULL;
	DWORD flags = 0;

	mActive = true;
	FMPPlay();

	while (mActive) {
		poller.Add(mPipe[0], false);
		if (poller.Poll(150)) {
			int r;
			if ((r = safe_read(mPipe[0], &header, sizeof(em84MsgHeader))) == -1) {
				if (mActive) {
					LOG_ERROR;
					mActive = false;
				}
				break;
			} 

			switch (header.msgtype)
			{
				case em84VideoPacket:
				case em84AudioPacket:
				{
					while (header.msglen > 0)
					{
						if (fmpbuf == NULL)
						{
							DWORD rc;
							if ((rc = FMPGetBuffer(&buffer, TRUE)) != FMPE_OK) {
								esyslog("Couldn't allocate em8400 replaybuffer: %lu", rc);
								mActive = false;
								break;
							}
							fmpbuf = &buffer;
						}

						int bytes = min(buffer.dwBufferSize - buffer.dwDataSize, (DWORD)header.msglen);
						if ((r = safe_read(mPipe[0], buffer.pBuffer + buffer.dwDataSize, bytes)) == -1)
						{
							if (mActive) {
								LOG_ERROR;
								mActive = false;
							}
							break;
						}

						buffer.dwDataSize += r;
						header.msglen -= r;

						if (buffer.dwDataSize == buffer.dwBufferSize)
						{
							buffer.dwFlags = flags;
							if (threadState == em84Trickmode)
								buffer.dwFlags |= FMP_TIMEDISCONTINUITY;
							else
								buffer.dwFlags |= FMP_TIMEVALID;
	
							FMPPush(&buffer);
							flags = 0;
	
							fmpbuf = NULL;
							//usleep(5000);
						}
					}
				}
				break;

				case em84StillPicture:
				{
					if (threadState != em84Paused)
					{
						FMPPause();

						if (fmpbuf != NULL)
						{
							buffer.dwDataSize = 0;
							FMPPush(&buffer);
							fmpbuf = NULL;
						}
						FMPFlush(TRUE);

						threadState = em84Paused;
					}

					if (mEm84Device)
					{
						cIFrameDecoder* wFrame = mEm84Device->StillFrame();
						if (wFrame->FrameValid())
						{
							IOsd* wOsd;
							if (FMPQueryInterface(IID_IOSD, (void**)&wOsd))
								esyslog("em84xx: could not query osd to display still picture");
							else
							{
							wOsd->lpVtbl->ShowSplashEx(wOsd, wFrame->FrameBuffer(), wFrame->FrameWidth(), wFrame->FrameHeight(), 0, 0);
							usleep(10000);
							}
	  					}
					}
				}
				break;


				case em84Pause:
				if (threadState != em84Paused)
				{
					FMPPause();
					esyslog("em84xx: cThreadEm84xx::em84Paused");

					if (fmpbuf != NULL)
					{
						buffer.dwDataSize = 0;
						FMPPush(&buffer);
						fmpbuf = NULL;
					}
					FMPFlush(TRUE);

					if (threadState == em84Trickmode)
						flags = FMP_TRICKMODE_END | FMP_TYPECHANGED;

					threadState = em84Paused;
				}
				break;

				case em84Play:
				if (threadState != em84Playing)
				{
					esyslog("em84xx: cThreadEm84xx::em84Playing");
					if (fmpbuf != NULL)
					{
						buffer.dwDataSize = 0;
						FMPPush(&buffer);
						fmpbuf = NULL;
					}
					FMPFlush(TRUE);

					if (threadState == em84Trickmode)
						flags = FMP_TRICKMODE_END | FMP_TYPECHANGED;
	
					threadState = em84Playing;
					FMPPlay();
				}
				break;

				case em84TrickMode:
				if (threadState != em84Trickmode)
				{
					if (fmpbuf != NULL)
					{
						buffer.dwDataSize = 0;
						FMPPush(&buffer);
						fmpbuf = NULL;
					}
					esyslog("em84xx: cThreadEm84xx::em84Trickmode");
					//FMPFlush(FALSE);
					FMPFlush(TRUE);
				
					flags = FMP_TRICKMODE_START | FMP_TYPECHANGED;

					threadState = em84Trickmode;
					FMPPlay();
				}
				break;


				default:
					esyslog("em84xx: cThreadEm84xx::Action - unexpected switch case (0x%x - default label) (mLastMsgType: 0x%x)", header.msgtype, mLastMsgType);
					if (fmpbuf != NULL)
					{
						buffer.dwDataSize = 0;
						FMPPush(&buffer);
						fmpbuf = NULL;
					}
					//FMPFlush(FALSE);
					FMPFlush(TRUE);
				
					flags = FMP_TRICKMODE_START | FMP_TYPECHANGED;

					threadState = em84Trickmode;
					FMPPlay();
				break;
			}
			//mLastMsgType = threadState;
			mLastMsgType = header.msgtype;
		}
	}

	if (fmpbuf != NULL)
	{
		buffer.dwDataSize = 0;
		FMPPush(&buffer);
		//FMPFlush(FALSE);
		FMPFlush(TRUE);
	}
	FMPStop();
	close(mPipe[0]);
	mActive = false;
}

int cThreadEm84xx::PlayPacket(const uchar *Data, int Length, bool isAudio)
{
	uchar buffer[sizeof(em84MsgHeader) + Length];
	em84MsgHeader *header = (em84MsgHeader*)buffer;

	header->msgtype = isAudio?em84AudioPacket:em84VideoPacket;
	header->msglen  = Length;
	memcpy(buffer + sizeof(em84MsgHeader), Data, Length);

	// if audio do pcm 0xBD check (I now that this don't fit here - but: hey, it alread one o clock past midnight
	if (isAudio)
	{
		// check pcm packets with stream id 0xBD (like from mp3-plugin)
		// em84 won't replay this - which is acc. to the spec I think ;)
		// this check assumes that complete pes-packets are send here and only once at a time (I hope this is correct)
		if ((Length >= 9) && (Data[0] == 0x00) && (Data[1] == 0x00) && (Data[2] == 0x01) && (Data[3] == 0xBD) && ((Data[6] & 0xC0) == 0x80))
		{
			unsigned char pesHeaderDataLength = Data[8];
			if ((Length > (9+pesHeaderDataLength)) && (Data[9+pesHeaderDataLength] == 0xA0))
				buffer[sizeof(em84MsgHeader)+3] = 0xA0;
		}
	}

	// check pcm samplerate
	if (isAudio)
	{
		// check pcm packets with stream id 0xBD (like from mp3-plugin)
		// em84 won't replay this - which is acc. to the spec I think ;)
		// this check assumes that complete pes-packets are send here and only once at a time (I hope this is correct)
		if ((Length >= 9) && (Data[0] == 0x00) && (Data[1] == 0x00) && (Data[2] == 0x01) && (buffer[sizeof(em84MsgHeader)+3] == 0xA0) && ((Data[6] & 0xC0) == 0x80))
		{
			unsigned char pesHeaderDataLength = Data[8];
			if ((Length > (14+pesHeaderDataLength)) && (Data[9+pesHeaderDataLength] == 0xA0))
			{
				unsigned char sr = ((Data[14+pesHeaderDataLength] >> 4) & 0x03);

/*
case 48000: srMode=0<<4; break;
      case 96000: srMode=1<<4; break;
      case 44100: srMode=2<<4; break;
      case 32000: srMode=3<<4; break;
      }
buff[14+FHS]|=srMode;*/

				switch (sr)
				{
					case 0:
						mEm84Device->SetAudioSampleRate(48000);
					break;

					case 1:
						mEm84Device->SetAudioSampleRate(96000);
					break;

					case 2:
						mEm84Device->SetAudioSampleRate(44100);
					break;

					case 3:
						mEm84Device->SetAudioSampleRate(32000);
					break;

					default:
					break;
				}
			}
		}
	}

	int r = WriteAllOrNothing(mPipe[1], buffer, sizeof(em84MsgHeader) + Length, 1000, 10);
	//int r = (int)safe_write(mPipe[1], buffer, sizeof(em84MsgHeader) + Length);

	return r;
}

int cThreadEm84xx::StillPicture(const uchar *Data, int Length) 
{
	/*uchar buffer[sizeof(em84MsgHeader) + Length];
	em84MsgHeader *header = (em84MsgHeader*)buffer;

	header->msgtype = em84StillPicture;
	header->msglen  = Length;
	memcpy(buffer + sizeof(em84MsgHeader), Data, Length);

	int r = WriteAllOrNothing(mPipe[1], buffer, sizeof(em84MsgHeader) + Length, 1000, 10);

	return r;*/

	em84MsgHeader header = { em84StillPicture, 0 };
	cPoller poller(mPipe[1], true);
	if (poller.Poll(500))
		safe_write(mPipe[1], &header, sizeof(em84MsgHeader));
	else
		esyslog("ERROR: Couldn't issue command on em84xx player thread");

	return Length;
}

void cThreadEm84xx::TrickSpeed(int Speed)
{
	em84MsgHeader header = { em84TrickMode, 0 };
	cPoller poller(mPipe[1], true);
	if (poller.Poll(500))
		safe_write(mPipe[1], &header, sizeof(em84MsgHeader));
	else
		esyslog("ERROR: Couldn't issue command on em84xx player thread");
}

void cThreadEm84xx::Play(void)
{
	em84MsgHeader header = { em84Play, 0 };
	cPoller poller(mPipe[1], true);
	if (poller.Poll(500))
		safe_write(mPipe[1], &header, sizeof(em84MsgHeader));
	else
		esyslog("ERROR: Couldn't issue command on em84xx player thread");
}

void cThreadEm84xx::Freeze(void)
{
	em84MsgHeader header = { em84Pause, 0 };
	cPoller poller(mPipe[1], true);
	if (poller.Poll(500))
		safe_write(mPipe[1], &header, sizeof(em84MsgHeader));
	else
		esyslog("ERROR: Couldn't issue command on em84xx player thread");
}

void cThreadEm84xx::AddPoller(cPoller &Poller)
{
	Poller.Add(mPipe[1], true);
}

