/*
 *  tracker/PlayerController.h
 *
 *  Copyright 2009 Peter Barth
 *
 *  This file is part of Milkytracker.
 *
 *  Milkytracker 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.
 *
 *  Milkytracker 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 Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/*
 *  PlayerController.h
 *  MilkyTracker
 *
 *  Created by Peter Barth on Tue Mar 15 2005.
 *
 */

#ifndef __PLAYERCONTROLLER_H__
#define __PLAYERCONTROLLER_H__

#include "MilkyPlayCommon.h"
#include "TrackerConfig.h"

class XModule;
struct TXMSample;
struct TEnvelope;

class PlayerController
{
public:
	enum PlayModes
	{
		PlayMode_Auto,
		PlayMode_ProTracker2,
		PlayMode_ProTracker3,
		PlayMode_ScreamTracker3,
		PlayMode_FastTracker2,
		PlayMode_ImpulseTracker
	};	

	enum PlayModeOptions
	{
		PlayModeOptionPanning8xx = 1,
		PlayModeOptionPanningE8x = 2,
		// Only affects PTK playback mode
		PlayModeOptionForcePTPitchLimit = 4
	};

private:
	class MasterMixer* mixer;
	class PlayerSTD* player;
	class ModuleEditor* moduleEditor;
	XModule* module;
	class PlayerCriticalSection* criticalSection;
	class PlayerStatusTracker* playerStatusTracker;
			
	bool patternPlay;
	bool playRowOnly;
	mp_sint32 patternIndex;
	mp_sint32 nextOrderIndexToPlay;
	mp_sint32 nextPatternIndexToPlay;

	mp_sint32 lastPosition, lastRow;
	bool wasPlayingPattern;
	bool suspended;
	
	mp_ubyte panning[TrackerConfig::MAXCHANNELS];

	bool muteChannels[TrackerConfig::MAXCHANNELS];
	bool recordChannels[TrackerConfig::MAXCHANNELS];
	bool firstRecordChannelCall;
	
	mp_sint32 currentPlayingChannel;

	mp_sint32 numPlayerChannels;
	mp_sint32 numVirtualChannels;
	mp_sint32 totalPlayerChannels;
	bool useVirtualChannels;
	bool multiChannelKeyJazz;
	bool multiChannelRecord;

	mp_sint32 mixerDataCacheSize;
	mp_sint32* mixerDataCache;

	void assureNotSuspended();
	void continuePlaying(bool assureNotSuspended);
	
	// no construction outside
	PlayerController(class MasterMixer* mixer, bool fakeScopes);

	bool detachDevice();

	void reset();
	
public:
	~PlayerController();
	
	void attachModuleEditor(ModuleEditor* moduleEditor);
	
	ModuleEditor* getModuleEditor() { return moduleEditor; }
	
	PlayerCriticalSection* getCriticalSection() { return criticalSection; }
	
	void playSong(mp_sint32 startIndex, mp_sint32 rowPosition, mp_ubyte* muteChannels);
	void playPattern(mp_sint32 index, mp_sint32 songPosition, mp_sint32 rowPosition, mp_ubyte* muteChannels, bool playRowOnly = false);
	void setCurrentPatternIndex(mp_sint32 index);
	void stop(bool bResetMainVolume = true);
	
	void continuePlaying();
	void restartPlaying();

	bool isPlaying() const;
	bool isPlayingRowOnly() const;
	bool isActive() const;
	bool isPlayingPattern() const { return patternPlay; }
	bool isReallyPlayingPattern() const { return patternPlay && !playRowOnly; }
	bool isPlayingPattern(mp_sint32 index) const;
	bool isSuspended() const { return suspended; }

	void setNextOrderToPlay(mp_sint32 orderIndex);
	mp_sint32 getNextOrderToPlay() const;
	void setNextPatternToPlay(mp_sint32 patternIndex);
	mp_sint32 getNextPatternToPlay() const;

	void pause();
	void unpause();
	bool isPaused() const;

	void getSpeed(mp_sint32& BPM, mp_sint32& speed);
	void setSpeed(mp_sint32 BPM, mp_sint32 speed, bool adjustModuleHeader = true);
	
	void readjustSpeed(bool adjustModuleHeader = true);
	
	void playSample(const TXMSample& sample, mp_sint32 currentSamplePlayNote, 
					mp_sint32 rangeStart = -1, mp_sint32 rangeEnd = -1);
	void stopSample();
	
	void stopInstrument(mp_sint32 insIndex);

	void playNote(mp_ubyte chn, 
				  mp_sint32 note, mp_sint32 i, mp_sint32 vol = -1);

	void suspendPlayer(bool bResetMainVolume = true, bool stopPlaying = true);	
	void resumePlayer(bool continuePlaying);

	void muteChannel(mp_sint32 c, bool m);
	bool isChannelMuted(mp_sint32 c);

	void recordChannel(mp_sint32 c, bool m);
	bool isChannelRecording(mp_sint32 c);

private:
	bool reallocChannels();
	void reallocateChannels(mp_sint32 moduleChannels = 32, mp_sint32 virtualChannels = 0);
	void setUseVirtualChannels(bool bUseVirtualChannels);

	void setMultiChannelKeyJazz(bool b) { multiChannelKeyJazz = b; }
	void setMultiChannelRecord(bool b) { multiChannelRecord = b; }

public:
	void resetFirstPlayingChannel();
	mp_sint32 getNextPlayingChannel(mp_sint32 currentChannel);
	
	void initRecording();
	mp_sint32 getNextRecordingChannel(mp_sint32 currentChannel);
	
	mp_sint32 getSongMainVolume();
	void resetMainVolume();
	
	// in seconds
	void resetPlayTimeCounter();
	
	mp_int64 getPlayTime();

	mp_ubyte getPanning(mp_ubyte chn) { return panning[chn]; }
	void setPanning(mp_ubyte chn, mp_ubyte pan);
	const pp_uint8* getPanningTable() { return (const pp_uint8*)&panning; }

	void getPosition(mp_sint32& pos, mp_sint32& row);
	void getPosition(mp_sint32& order, mp_sint32& row, mp_sint32& ticker);
	void setPatternPos(mp_sint32 pos, mp_sint32 row);
	
	// change playmode
	void switchPlayMode(PlayModes playMode, bool exactSwitch = true);
	PlayModes getPlayMode();
	
	void enablePlayModeOption(PlayModeOptions option, bool b);
	bool isPlayModeOptionEnabled(PlayModeOptions option);	
	
	// queries on the mixer
	mp_sint32 getAllNumPlayingChannels();
	mp_sint32 getPlayerNumPlayingChannels();

private:
	mp_sint32 getCurrentSamplePosition();
	mp_sint32 getCurrentBeatIndex();

public:
	bool isSamplePlaying(const TXMSample& smp, mp_sint32 channel, mp_sint32& pos, mp_sint32& vol, mp_sint32& pan);
	bool isEnvelopePlaying(const TEnvelope& envelope, mp_sint32 envelopeType, mp_sint32 channel, mp_sint32& pos);
	bool isNotePlaying(mp_sint32 ins, mp_sint32 channel, mp_sint32& note, bool& muted);
	
	class SampleDataFetcher
	{
	public:
		virtual void fetchSampleData(mp_sint32 sample) = 0;
	};
	void grabSampleData(mp_uint32 chnIndex, mp_sint32 count, mp_sint32 fMul, SampleDataFetcher& fetcher);
	
	bool hasSampleData(mp_uint32 chnIndex);
	
	friend class PlayerMaster;
	friend class PlayerStatusTracker;
};

#endif

