/*
	Copyright 1999, Be Incorporated.   All Rights Reserved.
	This file may be used under the terms of the Be Sample Code License.
*/

// This defines an engine that tracks the state of the CD player
// and supports the CD player control and status calls

#ifndef __CD_ENGINE__
#define __CD_ENGINE__

#include <Looper.h>
#include <String.h>
#include <View.h>

#include <vector>

#include "Observer.h"
#include "FunctionObjectMessage.h"
#include "CDDBSupport.h"

class CDEngine;
class PeriodicWatcher : public Notifier {
// watcher sits somewhere were it can get pulses and makes sure
// notices get sent if state changes
public:
	PeriodicWatcher(int devicefd);
	PeriodicWatcher(BMessage *);
	
	virtual ~PeriodicWatcher() {}

	void DoPulse();
	void UpdateNow();

	void AttachedToLooper(CDEngine *engine)
		{ this->engine = engine; }

	virtual BHandler *RecipientHandler() const;

protected:
	virtual bool UpdateState() = 0;

	int devicefd;
	CDEngine *engine;
};

enum CDState {
	kNoCD,
	kStopped,
	kPaused,
	kPlaying,
	kSkipping
};

class PlayState : public PeriodicWatcher {
	// this watcher sends notices to observers that are interrested
	// about play state changes
public:
	PlayState(int devicefd);
	PlayState(BMessage *message)
		:	PeriodicWatcher(message)
		{}

	CDState GetState() const;

private:
	bool UpdateState();
	bool CurrentState(CDState);
	CDState oldState;
};

class TrackState : public PeriodicWatcher {
	// this watcher sends notices to observers that are interrested
	// about changes in the current track
public:
	TrackState(int devicefd);
	TrackState(BMessage *message)
		:	PeriodicWatcher(message)
		{}

	int32 GetTrack() const;
	int32 GetNumTracks() const;

private:
	bool UpdateState();
	bool CurrentState(int32);
	int32 currentTrack;
};

class TimeState : public PeriodicWatcher {
	// this watcher sends notices to observers that are interrested
	// about changes in the current time
public:
	TimeState(int devicefd)
		:	PeriodicWatcher(devicefd)
		{ }
	TimeState(BMessage *message)
		:	PeriodicWatcher(message)
		{}

	void GetTime(int32 &minutes, int32 &seconds) const;

private:
	bool UpdateState();
	bool CurrentState(int32 minutes, int32 seconds);
	int32 oldMinutes;
	int32 oldSeconds;
};

class CDContentWatcher : public PeriodicWatcher {
public:
	CDContentWatcher(int devicefd);
	CDContentWatcher(BMessage *message);
	
	bool GetContent(BString *title, vector<BString> *tracks);
	
private:
	bool UpdateState();

	CDDBQuery cddbQuery;
	int32 discID;
	bool wasReady;
};

class VolumeState : public PeriodicWatcher {
	// this watcher sends notices to observers that are interrested
	// about changes in the current volume
	// currently not used yet
public:
	VolumeState(int devicefd)
		:	PeriodicWatcher(devicefd)
		{ }
	VolumeState(BMessage *message)
		:	PeriodicWatcher(message)
		{}

	bool UpdateState() { return true; }
	virtual void DoPulse() {}
	int32 GetVolume() const;
};

class CDEngine : public BHandler {
	// The CD engine defines all the different CD control calls; also,
	// it hosts the different state watchers and helps them send notices
	// to observers about the CD state changes
public:
	CDEngine(int devicefd);
	CDEngine(BMessage *);

	virtual ~CDEngine();

	// observing supprt
	virtual void MessageReceived(BMessage *);
	void AttachedToLooper(BLooper *);
	void DoPulse();

	// control calls
	void PlayOrPause();
	void PlayContinue();
	void Play();
	void Pause();
	void Stop();
	void Eject();
	void SkipOneForward();
	void SkipOneBackward();
	void StartSkippingBackward();
	void StartSkippingForward();
	void StopSkipping();
	void SelectTrack(int32);
	

	TrackState *TrackStateWatcher()
		{ return &trackState; }
		// to find the current Track, you may call the GetTrack function
		// TrackState defines

	PlayState *PlayStateWatcher()
		{ return &playState; }
		// to find the current play state, you may call the GetState function
		// PlayState defines

	TimeState *TimeStateWatcher()
		{ return &timeState; }
		// to find the current location on the CD, you may call the GetTime function
		// TimeState defines

	CDContentWatcher *ContentWatcher()
		{ return &contentWatcher; }

	static int FindCDPlayerDevice();
	
private:
	int devicefd;

	PlayState playState;
	TrackState trackState;
	TimeState timeState;
	VolumeState volumeState;
	CDContentWatcher contentWatcher;

	bigtime_t lastPulse;
};


// some function object glue
class CDEngineFunctorFactory : public FunctorFactoryCommon {
public:
	static BMessage *NewFunctorMessage(void (CDEngine::*func)(),
		CDEngine *target)
		{
			PlainMemberFunctionObject<void (CDEngine::*)(),
				CDEngine> tmp(func, target);
			return NewMessage(&tmp);
		}

	static BMessage *NewFunctorMessage(void (CDEngine::*func)(ulong),
		CDEngine *target, ulong param)
		{
			SingleParamMemberFunctionObject<void (CDEngine::*)(ulong),
				CDEngine, ulong> tmp(func, target, param);
			return NewMessage(&tmp);
		}
};


#endif