/* -*-c++-*- $Id$ */
/**
 * OsgAL - OpenSceneGraph Audio Library
 * Copyright (C) 2004 VRlab, Ume University
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */


#ifndef __SoundState_h__
#define __SoundState_h__


#include "osgAL/Export"

#include <string>
#include <map>
#include <cassert>
#include <vector>

#include "osg/Object"
#include "osg/Vec3"



#include "openalpp/source.h"
#include "openalpp/listener.h"
#include "openalpp/sample.h"
#include "openalpp/filestream.h"
#include "openalpp/ref_ptr.h"


namespace osgAL {

/// Class that encapsulate the settings valid for a soundsource 
/*!
  This class stores the attributes for a sound source. It can exist with a sound source
  allocated to it, (hasSource() == true) which makes it the same thing as a sound source.
  Otherwise, it works as a placeholder (state) for a sound source. It can
  be assigned a sound source at anytime. Then apply() is called and it has a sound source
  the actual settings will be performed.
  It a state has a sound source all the set*() method calls apply automatically.
*/
class OSGAL_EXPORT SoundState : public osg::Object
{
private:
  /// Specifies what field that has been set.
  enum SetField { Gain, Looping, Ambient, Relative, SoundCone, ReferenceDistance, RolloffFactor, 
                  Stream, Sample, Pitch, Position, Direction, Velocity, MaxDistance, Play, Occluded, Last };

public:
  
  /*!
    Constructor
    \brief may be only used for clone methods of osg
  */
  SoundState();
  
  /*! 
    Constructor
    \param name - The identifier of the SoundState
  */
  SoundState( const std::string& name );
  

  /// Destructor, releases any allocated sound sources
  ~SoundState(); 

  /// Copy constructor
  SoundState(const SoundState& state) : Object() 
  {
    if (this == &state)
      return;
  }
  
  // Implementation of virtual functions of osg::Object
  virtual osg::Object* cloneType() const { return new SoundState(); }
  virtual osg::Object* clone(const osg::CopyOp& copyop) const {
	(void)copyop; 
	return new SoundState(*this); 
  }
  virtual const char* libraryName() const { return "osgAL"; }
  virtual const char* className() const { return "SoundState"; }

  /// Change the name of the SoundState to name
  void setName(const std::string& name) { m_name = name; }

  /// Assignment operator
  SoundState& operator=(const SoundState& state);

  /// Return the name of this soundstate
  std::string getName() const { return m_name; }


  /// Returns true if the SoundState has allocated a soundsource
  bool hasSource() const { return m_source != 0L; }

  /// Set the sample that this state will play
  void setSample(openalpp::Sample *sample) { m_stream = 0; m_sample = sample; set(Sample); if (m_source.valid()) apply(); }
  
  /// Set the stream that this state will play
  void setStream(openalpp::Stream *stream) { m_sample = 0; m_stream = stream; set(Stream); if (m_source.valid()) apply(); }

  /// Returns the sample if used
  const openalpp::Sample * getSample() const {
	if(hasSource())
		if(m_sample.valid() != 0)
			return m_sample.get();
	return NULL;
  }

  /// Returns the stream if used
  const openalpp::Stream * getStream() const {
	if(hasSource())
		if(m_stream.valid() != 0)
			return m_stream.get();
	return NULL;
  }

  /// Set the position of SoundState
  void setPosition(const osg::Vec3& pos) { m_position = pos; set(Position); if (m_source.valid()) apply(); }
  
  /// Get the position of SoundState
  osg::Vec3 getPosition() const { return m_position; }

  /// Set the velocity of the SoundState 
  void setVelocity(const osg::Vec3& vel) { m_velocity = vel; set(Velocity); if (m_source.valid()) apply(); }

  /// Get the velocity of the SoundState 
  osg::Vec3 getVelocity() const { return m_velocity; }


  /// Set the direction of the SoundState
  void setDirection(const osg::Vec3& dir) { m_direction = dir; set(Direction); if (m_source.valid()) apply(); }
  
  /// Get the direction of the SoundState
  osg::Vec3 getDirection() const { return m_direction; }

  /// Set the gain (volume) of the soundstate (1.0 is default)
  void setGain(float gain) { m_gain = gain; set(Gain); if (m_source.valid()) apply(); }
  
  /// Get the gain (volume) of the soundstate (1.0 is default)
  float getGain() const { return m_gain; }

  /// Return true if the sound state is in looping mode
  bool getLooping() const { return m_looping; }

  /// Set the SoundState in looping mode
  void setLooping(bool flag) {  m_looping = flag; set(Looping); if (m_source.valid()) apply(); }
  
  /// Set the soundstate in ambient (no attenuation will be calculated)
  void setAmbient(bool flag) {  m_ambient = flag; set(Ambient); if (m_source.valid()) apply(); }
  
  /// Get the soundstate in ambient (no attenuation will be calculated)
  bool getAmbient() const {  return m_ambient; }

  /// Set the soundstate so it will always be relative to the listener in position
  void setRelative(bool flag) { m_relative = flag; set(Relative); if (m_source.valid()) apply(); }

  /// Get the soundstate so it will always be relative to the listener in position
  bool getRelative() const { return m_relative; }

  
  /*! 
    Specifies the sound cone for a directional sound source.
    When outside the cone, the outer gain is the gain used in the attenuation calculation.
  */
  void setSoundCone(float innerAngle, float outerAngle, float outerGain) 
  { m_innerAngle = innerAngle; m_outerAngle = outerAngle; m_outerGain = outerGain; set(SoundCone); 
    if (m_source.valid()) apply(); 
  }
  
  /// Get the the inner angle of the cone for the SoundState in degrees
  float getInnerAngle() const { return m_innerAngle; }

  /// Get the the outer angle of the cone for the SoundState in degrees
  float getOuterAngle() const { return m_outerAngle; }

  /// Get the the outer gain of the cone for the SoundState
  float getOuterGain() const { return m_outerGain; }

  /// \return true if the source is in playing state. If there is no source associated, it will return false.
  bool isActive();

  /// Set the reference distance for the SoundState
  void setReferenceDistance(float distance) { m_referenceDistance = distance; set(ReferenceDistance); if (m_source.valid()) apply(); }
  
  /// Get the reference distance for the SoundState
  float getReferenceDistance() const { return m_referenceDistance; }

  /*! Set the maximum distance for the SoundState, that is further away from the listener the source will be
      turned of when in the InverseClamp sound mode 
      1.0 is default
  */
  void setMaxDistance(float max) { m_maxDistance = max; set(MaxDistance); if (m_source.valid()) apply(); }

  /*! Get the maximum distance for the SoundState, that is further away from the listener the source will be
      turned of when in the InverseClamp sound mode 
      1.0 is default
  */
  float getMaxDistance() const { return m_maxDistance; }

  /// Specifies the roll of factor for the SoundState, 1.0 is default
  void setRolloffFactor(float roll) {m_rolloffFactor = roll; set(RolloffFactor); if (m_source.valid()) apply(); }

  /// Return the roll of factor for the SoundState, 1.0 is default
  float getRolloffFactor() const {return m_rolloffFactor; }

  /// Set the pitch (rate) for the SoundState (1.0 is default)
  void setPitch(float pitch) {  m_pitch = pitch; set(Pitch); if (m_source.valid()) apply(); }

  /// Get the pitch (rate) for the SoundState (1.0 is default)
  float getPitch() const { return m_pitch; }

  /// Starts to play the SoundState
  void setPlay(bool flag) { m_play = flag; set(Play); if (m_source.valid()) apply(); }

  /// Return if the soundstate will play
  bool getPlay() { return m_play; }

  void setOccludeDampingFactor(float d) { m_occlude_damping_factor = d; }
  float getOccludeDampingFactor() const { return m_occlude_damping_factor; }

  void setOccludeScale(float d) { m_occlude_scale = d; }
  float getOccludeScale() const { return m_occlude_scale; }
    
  void setOccluded(bool f) { m_is_occluded = f; set(Occluded); if (m_source.valid()) apply(); }
  bool getOccluded() const { return m_is_occluded; }

  /// Set whether paus or stop should be used when calling setPlay(false) 
  void setStopMethod(openalpp::SourceState s) 
  { 
      if ( s == openalpp::Paused ) 
          m_pause = true;
      else
          m_pause = false;
  }

  /// Returns true if there is a source and it is playing
  bool isPlaying() { return (m_source.valid() && m_source->getState() == openalpp::Playing); }

  /// Returns with const true if there is a source and it is playing
  bool isPlaying() const { return (m_source.valid() && m_source->getState() == openalpp::Playing); }

  // Get stop method -  used by openalpp when calling setPlay(false)
  openalpp::SourceState getStopMethod()  const
  { 
      if (m_pause)
          return openalpp::Paused;
      else
          return openalpp::Stopped;
  }

  /*! Allocates a soundsource for this soundstate
    \param priority - This priority is used when fighting with other active soundstates for the limited resource of SoundSources.
    If a active (playing) SoundState has a lower priority than this, it will be discarded, and the sound source will be used for this
    SoundState
    \param priority - The priority of this state. The higher, the better chance of actually
    get a Sound source allocated.
    \param registrate_as_active - This specifies wether the Allocated SoundSource will be added to the list of active SoundSources,
    in that case it can be 
    \return true if a source was successfully allocated.
  */
  bool allocateSource(unsigned int priority=0, bool registrate_as_active=true); 

  /// Return the priority set for this SoundState
  unsigned int getPriority() const { return m_priority; }


  /// Set the Source for this SoundState
  void setSource(openalpp::Source *source); 

  /// Return the Source for this SoundState
  const openalpp::Source *getSource() const { return m_source.get(); }

  /// Release the Source that is allocated for this SoundState
  void releaseSource(); 


  /// Performs the actual modification to the allocated Source.
  void apply(); 

private:

  
  /// Clear all the flags indicating a value has been set
  void setAll(bool flag) { for(unsigned int i=0; i < m_is_set.size(); i++) m_is_set[i]=flag; }
  
  openalpp::ref_ptr<openalpp::Stream> m_stream;
  openalpp::ref_ptr<openalpp::Sample> m_sample;
  openalpp::ref_ptr<openalpp::Source> m_source;

  std::string m_name;
  osg::Vec3 m_position;
  osg::Vec3 m_direction;
  osg::Vec3 m_velocity;
  float m_gain, m_innerAngle, m_outerAngle, m_outerGain; 
  float m_referenceDistance, m_maxDistance;
  float m_rolloffFactor, m_pitch;
  float m_occlude_damping_factor, m_occlude_scale;
  bool m_is_occluded;

  bool m_looping, m_ambient, m_relative, m_play, m_pause;
  unsigned m_priority;



  /// Return true if SetField f is set since last call to apply() or clear()
  bool isSet(SetField f) { return m_is_set[f]; }

  /// Set the given field to true
  void set(SetField f) { m_is_set[f] = true; }

  // Clear the given field to false
  void clear(SetField f)  { m_is_set[f] = false; }

  std::vector<bool> m_is_set;

};

} // Namespace osgAL
#endif // 

/*------------------------------------------
* $Source$
* $Revision$ 
* $Date$
* $Author$ 
  $Locker$

  Author: Anders Backman
  VRlab, Ume University, 2002
 
* $Log$
* Revision 1.2  2006/07/24 15:31:10  loic
* autotools
*
* Revision 1.1.1.3  2005/09/07 16:11:36  loic
* upstream update
*
* Revision 1.6  2005/05/27 07:21:35  vr-anders
*   Alberto Jaspe (videaLAB / University of La Corua)
*   - Several miscelaneus bugfixes.
*   - Added support for reading/writing from/to osg files.
*   - Important bugfixes in doppler issues.
*   - Using just one update frequency for listener and sources, in SoundManager.
*   - Using a state for clamp function for max velocites in both listener and sources,
*     disabled by default.
*   - Replaced the FilePath Manager for the osgDB support.
*   - Added a new example called osgalviewer, that acts like osgviewer, but with some
*     features oriented to treat with Sound Nodes.
*   - Compiled and tested with OSG 0.9.9.
*
* Revision 1.5  2004/11/19 07:46:09  andersb
* *** empty log message ***
*
* Revision 1.4  2004/11/11 07:47:36  andersb
* Added a simple occlude method for occluding soundsources against geometry
*
* Revision 1.3  2004/04/20 12:26:04  andersb
* Added SoundRoot
*
* Revision 1.2  2004/03/03 07:50:49  andersb
* *** empty log message ***
*
* Revision 1.1.1.1  2004/03/02 07:20:58  andersb
* Initial Version of the osgAL library
*

--------------------------------------------*/
