/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * Additional copyright for this file:
 * Copyright (C) 1995-1997 Presto Studios, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef PEGASUS_TIMERS_H
#define PEGASUS_TIMERS_H

#include "common/rational.h"
#include "common/func.h"

#include "pegasus/constants.h"
#include "pegasus/notification.h"
#include "pegasus/util.h"

namespace Pegasus {

class Idler {
friend class PegasusEngine;

public:
	Idler();
	virtual ~Idler();

	virtual void startIdling();
	virtual void stopIdling();
	bool isIdling() const { return _isIdling; }

protected:
	virtual void useIdleTime() {}

	bool _isIdling;
	Idler *_nextIdler, *_prevIdler;
};

enum {
	kLoopTimeBase           = 1,
	kPalindromeLoopTimeBase = 2,
	kMaintainTimeBaseZero   = 4
};

class TimeBaseCallBack;

class TimeBase {
friend class TimeBaseCallBack;
public:
	TimeBase(const TimeScale = kDefaultTimeScale);
	virtual ~TimeBase();

	virtual void setTime(const TimeValue, const TimeScale = 0);
	virtual TimeValue getTime(const TimeScale = 0);

	virtual void setScale(const TimeScale scale) { _preferredScale = scale; }
	virtual TimeScale getScale() const { return _preferredScale; }

	virtual void setRate(const Common::Rational);
	virtual Common::Rational getRate() const { return _rate; }

	virtual void start();
	virtual void stop();
	virtual bool isRunning();

	virtual void pause();
	virtual void resume();
	bool isPaused() const { return _paused; }

	virtual void setFlags(const uint32 flags) { _flags = flags; }
	virtual uint32 getFlags() const { return _flags; }

	virtual void setStart(const TimeValue, const TimeScale = 0);
	virtual TimeValue getStart(const TimeScale = 0) const;

	virtual void setStop(const TimeValue, const TimeScale = 0);
	virtual TimeValue getStop(const TimeScale = 0) const;

	virtual void setSegment(const TimeValue, const TimeValue, const TimeScale = 0);
	virtual void getSegment(TimeValue&, TimeValue&, const TimeScale = 0) const;

	virtual TimeValue getDuration(const TimeScale = 0) const;

	virtual void setMasterTimeBase(TimeBase *timeBase);

	void disposeAllCallBacks();

	// ScummVM's API additions (to replace the need for actual timers)
	virtual void checkCallBacks();

protected:
	void addCallBack(TimeBaseCallBack *);
	void removeCallBack(TimeBaseCallBack *);
	virtual void updateTime();

	TimeBase *_master;
	TimeScale _preferredScale;
	TimeBaseCallBack *_callBackList;
	Common::Rational _rate, _pausedRate;
	bool _paused;
	uint32 _startTime, _startScale;
	uint32 _stopTime, _stopScale;
	uint32 _flags;

	Common::Rational _time;
	uint32 _lastMillis, _pauseStart;
};

// Type passed to initCallBack()
enum CallBackType {
	kCallBackNone = 0,
	kCallBackAtTime = 1,
	kCallBackAtExtremes = 4
};

// Trigger passed to scheduleCallBack()
enum CallBackTrigger {
	kTriggerNone = 0,

	// AtTime flags
	kTriggerTimeFwd = 1,

	// AtExtremes flags
	kTriggerAtStart = 1,
	kTriggerAtStop = 2
};

class TimeBaseCallBack {
friend class TimeBase;

public:
	TimeBaseCallBack();
	virtual ~TimeBaseCallBack();

	void initCallBack(TimeBase *, CallBackType type);

	void releaseCallBack();

	void scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3);
	void cancelCallBack();

protected:
	virtual void callBack() = 0;

	TimeBase *_timeBase;

	// Owned and operated by TimeBase;
	TimeBaseCallBack *_nextCallBack;

	// Our storage of the QuickTime timer crap
	CallBackType _type;
	CallBackTrigger _trigger;
	uint32 _param2, _param3;
	bool _hasBeenTriggered;

private:
	void disposeCallBack();
};

class IdlerTimeBase : public Idler, public TimeBase {
public:
	IdlerTimeBase();
	~IdlerTimeBase() override { stopIdling(); }

	TimeValue getLastTime() const { return _lastTime; }

protected:
	void useIdleTime() override;
	virtual void timeChanged(const TimeValue) {}

	TimeValue _lastTime;

};

class NotificationCallBack : public TimeBaseCallBack {
public:
	NotificationCallBack();
	~NotificationCallBack() override {}

	void setNotification(Notification *notifier) { _notifier = notifier; }

	void setCallBackFlag(const NotificationFlags flag) { _callBackFlag = flag; }
	NotificationFlags getCallBackFlag() const { return _callBackFlag; }

protected:
	void callBack() override;

	Notification *_notifier;
	NotificationFlags _callBackFlag;
};

class DynamicElement : public TimeBase {
public:
	TimeValue percentSeconds(const uint32 percent) { return getScale() * percent / 100; }
};

class Fuse : private NotificationReceiver {
public:
	Fuse();
	~Fuse() override {}

	void primeFuse(const TimeValue, const TimeScale = 1); // An appropriately named function :P
	void lightFuse();
	void stopFuse();
	bool isFuseLit() { return _fuseTimer.isRunning() || _fuseTimer.isPaused(); }
	void advanceFuse(const TimeValue);
	TimeValue getTimeRemaining();
	TimeScale getFuseScale() { return _fuseTimer.getScale(); }

	void pauseFuse() { _fuseTimer.pause(); }
	void resumeFuse() { _fuseTimer.resume(); }
	bool isFusePaused() { return _fuseTimer.isPaused(); }

protected:
	void receiveNotification(Notification *, const NotificationFlags) override;
	virtual void invokeAction() {}

	TimeBase _fuseTimer;
	NotificationCallBack _fuseCallBack;
	Notification _fuseNotification;
};

class FuseFunction : public Fuse {
public:
	FuseFunction() : _functor(0) {}
	~FuseFunction() override { delete _functor; }

	void setFunctor(Common::Functor0<void> *functor) { delete _functor; _functor = functor; }
protected:
	void invokeAction() override { if (_functor && _functor->isValid()) (*_functor)(); }

	Common::Functor0<void> *_functor;
};

} // End of namespace Pegasus

#endif
