#include "SeekLayer.h"
#include "Main.h"
#include "output/AudioOut.h"
#include "util.h"
#include <assert.h>
using namespace Nullsoft::Utility;

struct OpenThreadData
{
	IWMReaderCallback *callback;
	wchar_t *url;
	IWMReader *reader;

	void open()
	{
		reader->Open(url, callback, 0);
	}

	OpenThreadData(IWMReader *_reader, const wchar_t *_url, IWMReaderCallback *_callback)
	{
		reader = _reader;
		reader->AddRef();
		callback = _callback;
		callback->AddRef();

		url = _wcsdup(_url);
	}

	~OpenThreadData()
	{
		free(url);
		reader->Release();
		callback->Release();
	}
};
DWORD WINAPI OpenThread(void *param)
{
	OpenThreadData *data = (OpenThreadData *)param;
	data->open();
	delete data;
	return 0;
}

#define NEW_SEEK
SeekLayer::SeekLayer(IWMReader *_reader, ClockLayer *_clock)
		: seekPos(0), reader(_reader), playState(PLAYSTATE_CLOSED), metadata(NULL), clock(_clock),
		needPause(false), paused(false), needStop(false),
		seekGuard(GUARDNAME("Seek Guard")),
		oldState_buffer(PLAYSTATE_NONE)
{
	reader->AddRef();
	reader->QueryInterface(&reader2);
}

void SeekLayer::DoStop()
{
	if (paused)
		reader->Resume();

	reader->Stop();
	First().Stopping();
	First().Kill();
	if (paused)
	{
		paused = false;
		out->Pause(0);
	}
	needStop = false;
}

void SeekLayer::SeekTo(long position)
{
	AutoLock lock (seekGuard LOCKNAME("SeekTo"));
	if (paused)
	{
		reader->Resume();
	}
	First().Stopping();
	First().Kill();
	seekPos = position;
	clock->SetLastOutputTime(position);
	clock->SetStartTimeMilliseconds(position);
	out->Flush(position);
	QWORD qSeekPos = position;
	qSeekPos *= 10000;
	reader->Start(qSeekPos, 0, 1.0f, NULL);
	if (paused)
	{
		reader->Pause();
	}
}

void SeekLayer::Pause()
{
	AutoLock lock (seekGuard LOCKNAME("Pause"));
	if (playState == PLAYSTATE_STARTED)
	{
		paused = true;
		reader->Pause();
		out->Pause(1);
	}
	else
	{
		needPause = true;
	}
}

int SeekLayer::Open(const wchar_t *filename, IWMReaderCallback *callback)
{
	AutoLock lock (seekGuard LOCKNAME("Open"));
	assert(playState == PLAYSTATE_CLOSED);
	needStop = false;
	playState = PLAYSTATE_OPENING;
	DWORD dummyId;
	CreateThread(NULL, 0, OpenThread, new OpenThreadData(reader, filename, callback), 0, &dummyId);
	return 0;
}

void SeekLayer::Stop()
{
	AutoLock lock (seekGuard LOCKNAME("Stop"));
	needStop = true;

	switch (playState)
	{
	case PLAYSTATE_BUFFERING:
//				needStop=false;
		reader2->StopBuffering();
//		reader->Stop();
		break;

	case PLAYSTATE_OPENING:
		// wait for it to open (or connect) and then we'll kill it there
		break;

	case PLAYSTATE_NONE:
	case PLAYSTATE_STOPPED:
		if (FAILED(reader->Close())) // reader->Close() is sometimes synchronous, and sometimes not valid here
		{
			playState = PLAYSTATE_CLOSED;
			return ;
		}
		break;

	case PLAYSTATE_OPENED:
	case PLAYSTATE_STARTED:
		reader->Stop();
		First().Stopping();
		First().Kill();
		break;

	case PLAYSTATE_CLOSED:
		needStop = false;
		break;
		/*
			case PLAYSTATE_BUFFERING:
				reader2->StopBuffering();
				reader->Stop();
				break;*/

	case PLAYSTATE_SEEK:
		break;
	}

	while (playState != PLAYSTATE_CLOSED)
	{
		lock.ManualUnlock();
		Sleep(55);
		lock.ManualLock(MANUALLOCKNAME("[Manual Lock]Stop"));
	}
	needStop = false;
}

void SeekLayer::Unpause()
{
	AutoLock lock (seekGuard LOCKNAME("Unpause"));
	if (playState == PLAYSTATE_STARTED)
	{
		paused = false;
		out->Pause(0);
		reader->Resume();
		clock->Clock();
	}
	else
	{
		needPause = false;
	}
}

void SeekLayer::Opened()
{
	{
		AutoLock lock (seekGuard LOCKNAME("SeekLayer::Opened"));
		if (needStop)
		{
			playState = PLAYSTATE_OPENED;
			lock.ManualUnlock();
			reader->Close();
			lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::Opened"));
			return ;
		}

		playState = PLAYSTATE_OPENED;
	}
	WMHandler::Opened();
}

void SeekLayer::Stopped()
{
	{
		AutoLock lock (seekGuard LOCKNAME("Stopped"));
		if (needStop)
		{
			playState = PLAYSTATE_STOPPED;
			lock.ManualUnlock();
			reader->Close();
			lock.ManualLock(MANUALLOCKNAME("Stopped"));
		}
		else
		{
			playState = PLAYSTATE_STOPPED;
		}

		WMHandler::Stopped();
	}
}

void SeekLayer::Started()
{
	{
		AutoLock lock (seekGuard LOCKNAME("Started"));
		playState = PLAYSTATE_STARTED;

		if (needStop)
		{
			reader->Stop();
			First().Stopping();
			First().Kill();
			return ;
		}
		else if (needPause)
		{
			Pause();
			needPause = false;
		}
	}
	WMHandler::Started();
}

void SeekLayer::Closed()
{
	playState = PLAYSTATE_CLOSED;
	paused = false;
	needPause = false;
	needStop = false;
	seekPos = 0;

	WMHandler::Closed();
}

void SeekLayer::BufferingStarted()
{
	{
		AutoLock lock (seekGuard LOCKNAME("BufferingStarted"));
		if (playState == PLAYSTATE_OPENED)
			oldState_buffer = PLAYSTATE_NONE;
		else
			oldState_buffer = playState;
		if (playState != PLAYSTATE_STARTED)
			playState = PLAYSTATE_BUFFERING;
		if (needStop)
			reader2->StopBuffering();

	}
	WMHandler::BufferingStarted();
}


void SeekLayer::BufferingStopped()
{
	{
		AutoLock lock (seekGuard LOCKNAME("BufferingStopped"));
		if (needStop)
			reader->Stop();
		playState = oldState_buffer;
	}
	WMHandler::BufferingStopped();
}


void SeekLayer::EndOfFile()
{
	{
		AutoLock lock (seekGuard LOCKNAME("EndOfFile"));
		if (needStop)
			return ;
	}
	WMHandler::EndOfFile();

}

void SeekLayer::Connecting()
{
	{
		AutoLock lock (seekGuard LOCKNAME("SeekLayer::Connecting"));
		if (needStop)
		{
			playState = PLAYSTATE_NONE;
			lock.ManualUnlock();
			reader->Close();
			lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::Connecting"));
			return ;
		}
		playState = PLAYSTATE_NONE;
	}
	WMHandler::Connecting();
}


void SeekLayer::Locating()
{
	{
		AutoLock lock (seekGuard LOCKNAME("SeekLayer::Locating"));
		if (needStop)
		{
			playState = PLAYSTATE_NONE;
			lock.ManualUnlock();
			reader->Close();
			lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::Locating"));
			return ;
		}
		playState = PLAYSTATE_NONE;
	}
	WMHandler::Locating();
}


void SeekLayer::OpenCalled()
{
	{
		AutoLock lock (seekGuard LOCKNAME("SeekLayer::OpenCalled"));
		if (needStop)
		{
			playState = PLAYSTATE_NONE;
			lock.ManualUnlock();
			reader->Close();
			lock.ManualLock(MANUALLOCKNAME("[Manual Lock]SeekLayer::OpenCalled"));
			return ;
		}

		playState = PLAYSTATE_NONE;
	}
	WMHandler::OpenCalled();
}

void SeekLayer::OpenFailed()
{
	{
		AutoLock lock (seekGuard LOCKNAME("SeekLayer::OpenFailed"));
		if (playState == PLAYSTATE_OPENING)
			playState = PLAYSTATE_NONE;
	}
	WMHandler::OpenFailed();
}

void SeekLayer::Error()
{
	{
		AutoLock lock (seekGuard LOCKNAME("SeekLayer::Error"));
		/*if (playState == PLAYSTATE_OPENING)
			playState = PLAYSTATE_CLOSED;
		else */if (playState != PLAYSTATE_CLOSED)
			playState = PLAYSTATE_NONE;
	}
	WMHandler::Error();
}