#pragma once
#include "nx/nx.h"
#include "nu/LockFreeItem.h"
#include "player/ifc_playback.h"
#include "player/ifc_player.h"
#include "player/svc_output.h"
#include "nu/ThreadLoop.h"
#include "filelock/api_filelock.h"

/* TODO: we can probably redo this without the mutex, possibly using semaphores */
class PlaybackBase : public ifc_playback, public cb_filelock
{
public:
	using ifc_playback::Retain;
	using ifc_playback::Release;


	/* ifc_playback implementation */
	int WASABICALL Playback_Play(svc_output *output, ifc_playback_parameters *secondary_parameters);
	int WASABICALL Playback_SeekSeconds(double seconds);
	int WASABICALL Playback_Pause();
	int WASABICALL Playback_Unpause();
	int WASABICALL Playback_Stop();
	int WASABICALL Playback_Close();

	/* cb_filelock implementation */
	int WASABICALL FileLockCallback_Interrupt();
protected:
	nx_thread_t playback_thread;
	svc_output *output_service;
	ifc_player *player;
	ifc_playback_parameters *secondary_parameters;
	nx_uri_t filename;
	
	enum
	{
		WAKE_KILL=(1<<0),
		WAKE_PLAY=(1<<1),
		WAKE_PAUSE=(1<<2), 
		WAKE_STOP=(1<<3),
		WAKE_INTERRUPT=(1<<4),
		WAKE_UNPAUSE=(1<<5), // this is actually unused in wake_flags, just used as a return value from Wake/WakeReason		
		WAKE_RESUME=(1<<6), // this is actually unused in wake_flags, just used as a return value from Wake/WakeReason		
		WAKE_START_MASK = WAKE_PLAY|WAKE_STOP, 
		WAKE_KILL_MASK = WAKE_KILL|WAKE_STOP,
		WAKE_ALL_MASK = WAKE_KILL|WAKE_PLAY|WAKE_PAUSE|WAKE_STOP|WAKE_INTERRUPT,
	};
	

protected:
	PlaybackBase();
	~PlaybackBase();

	/* === API for derived classes to use === */
	int Initialize(nx_uri_t filename, ifc_player *player);
	int Init();

	/* this checks if one of the flags in mask has toggled.  
	   if playback is stopped and WAKE_PLAY is in the mask, this will sleep until a flag changes
	   if playback is paused and WAKE_PAUSE is in the mask, this will sleep until a flag changes
		 if both WAKE_PLAY and WAKE_PAUSE are in the mask, this will sleep until either happens 
	   returns 0 if no flag changed */
	int Wake(int mask); /* Sleeps indefinitely until a flag changes */
	int Check(int mask); /* like Wake() but never actually goes to sleep for any reason */
	int Wait(unsigned int milliseconds, int mask); /* Sleeps for a limited amount of time for a flag to change */
	int Sleep(unsigned int milliseconds, int mask); /* unlike Wait, this one does *not* update last flags */
	int WakeReason(int mask) const;

	void OnInterrupted(); /* turn off the WAKE_INTERRUPT flag */
	void OnStopPlaying(); /* turn off the WAKE_PLAY flag */
	bool PendingSeek();
	Agave_Seek *GetSeek();
	void FreeSeek(Agave_Seek *seek);
  /* === End of API for derived classes to use === */
private:
	void ToggleFlags(int wake_reason);

	int wake_flags; /* marked volatile so the compiler doesn't cache */
	int last_wake_flags;
	Agave_Seek *queued_seek;

	ThreadLoop thread_loop;
	static void APC_Play(void *_playback_base, void *param2, double real_value);
	static void APC_Seek(void *_playback_base, void *param2, double real_value);
	static void APC_Pause(void *_playback_base, void *param2, double real_value);
	static void APC_Unpause(void *_playback_base, void *param2, double real_value);
	static void APC_Stop(void *_playback_base, void *param2, double real_value);
	static void APC_Close(void *_playback_base, void *param2, double real_value);
	static void APC_Interrupt(void *_playback_base, void *param2, double real_value);
	
};