#    This file is part of pysdl_mixer.
#
#    pysdl_mixer 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 2 of the License, or
#    (at your option) any later version.
#
#    pysdl_mixer 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 pysdl_mixer; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

# This module is based on SDL_mixer

cdef extern from "SDL/SDL.h":
  int SDL_InitSubSystem(unsigned int flags)
  int SDL_INIT_AUDIO

cdef extern from "SDL/SDL_mixer.h":
  ctypedef struct Mix_Chunk:
    int allocated
    unsigned int *abuf
    unsigned int alen
    unsigned int volume

  ctypedef enum Mix_Fading:
    MIX_NO_FADING
    MIX_FADING_IN
    MIX_FADING_OUT

  ctypedef enum Mix_MusicType:
    MUS_NONE
    MUS_CMD
    MUS_WAV
    MUS_MOD
    MUS_MID
    MUS_OGG
    MUS_MP3

  int MIX_CHANNELS
  
  int MIX_DEFAULT_FREQUENCY
  int MIX_DEFAULT_FORMAT
  int MIX_DEFAULT_CHANNELS
  int MIX_MAX_VOLUME

  ctypedef struct Mix_Music

  # almost all sdl_mixer commands have been defined here
  # the only commands ive left out are those for hooks as i cant make those
  # work yet

  # reference for these functions can be found at
  # http://jcatki.no-ip.org/SDL_mixer/SDL_mixer.html
  
  # general comands
  int Mix_OpenAudio(int frequency, unsigned int format, int channels,int chunksize)
  void Mix_CloseAudio()
  char *Mix_GetError()
  int Mix_QuerySpec(int *frequency, unsigned int *format, int *channels)
  
  # sample commands
  Mix_Chunk *Mix_LoadWAV(char *file)
  int Mix_VolumeChunk(Mix_Chunk *chunk, int volume)
  void Mix_FreeChunk(Mix_Chunk *chunk)
  
  # channel commands
  int Mix_AllocateChannels(int numchans)
  int Mix_Volume(int channel, int Volume)
  int Mix_PlayChannel(int channel,Mix_Chunk *chunk, int loops)
  int Mix_PlayChannelTimed(int channel, Mix_Chunk *chunk, int loops, int ticks)
  int Mix_FadeInChannel(int channel, Mix_Chunk *chunk, int loops, int ms)
  int Mix_FadeInChannelTimed(int channel, Mix_Chunk *chunk, int loops, int ms, int ticks)
  void Mix_Pause(int channel)
  void Mix_Resume(int channel)
  void Mix_HaltChannel(int channel)
  int Mix_ExpireChannel(int channel, int ticks)
  int Mix_FadeOutChannel(int channel, int ms)
  int Mix_Playing(int channel)
  int Mix_Paused(int channel)
  Mix_Fading Mix_FadingChannel(int channel)

  # group commands
  int Mix_ReserveChannels(int num)
  int Mix_GroupChannel(int channel, int tag)
  int Mix_GroupChannels(int f, int t, int tag)
  int Mix_GroupCount(int tag)
  int Mix_GroupAvailable(int tag)
  int Mix_GroupOldest(int tag)
  int Mix_GroupNewer(int tag)
  int Mix_FadeOutGroup(int tag, int ms)
  int Mix_HaltGroup(int tag)

  # music commands
  Mix_Music *Mix_LoadMUS(char *file)
  int Mix_FreeMusic(Mix_Music *music)
  int Mix_PlayMusic(Mix_Music *music,int loops)
  int Mix_FadeInMusic(Mix_Music *music, int loops, int ms)
  int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position)
  int Mix_VolumeMusic(int volume)
  void Mix_PauseMusic()
  void Mix_ResumeMusic()
  void Mix_RewindMusic()
  int Mix_SetMusicPosition(double position)
  int Mix_HaltMusic()
  int Mix_FadeOutMusic(int ms)
  Mix_MusicType Mix_GetMusicType(Mix_Music *music)
  int Mix_PlayingMusic()
  int Mix_PausedMusic()
  int Mix_FadingMusic()

  # effect commands
  int Mix_SetPanning(int channel, unsigned int left, unsigned int right)
  int Mix_SetDistance(int channel, int distance)
  int Mix_SetPosition(int channel, int angle, unsigned int distance)
  int Mix_SetReverseStereo(int channel, int flip)

def init(int frequency=MIX_DEFAULT_FREQUENCY, unsigned int format=MIX_DEFAULT_FORMAT, int channels=MIX_DEFAULT_CHANNELS, int chunksize=1024):
  """ initialises the sdl mixer. the default parameters should be fine i think """

  # this lets us init the audio after the rest of the sdl stuff has been initiailised.
  # this should stop any openal issues. 
  if SDL_InitSubSystem(SDL_INIT_AUDIO)==-1:
    print "%s" % Mix_GetError()
    raise "Cannot init audio!"
    
  if Mix_OpenAudio(frequency,format,channels,chunksize)==-1:
    print "Audio Error: %s" % Mix_GetError()
    return False
  
  return True

def shutdown():
  """ closes the audio """
  Mix_CloseAudio()

def print_info():
  """ prints debugging info on the audio system """

  cdef int freq
  cdef unsigned int format
  cdef int channels

  Mix_QuerySpec(&freq, &format, &channels)

  #TODO: decode format to something readable
  print "frequency: %d format: %d channels: %d" % (freq,format,channels)

# most of the channel functionality is in the AudioChannel class as well
# TODO: do we really need these functions if AudioChannel exists

def allocate_channels(int numchans):
  """ allocate "numchans" channels for mixing """

  Mix_AllocateChannels(numchans)

def channel_volume(int channel,int volume):
  """ set "channel"'s volume to "volume". passing -1 as channel will set the volume of all 
  channels """

  Mix_Volume(channel,volume)

def channel_panning(int channel,int left,int right):
  """ set "channel"'s panning to "left" and "right". passing -1 as channel will set the panning of all 
  channels """

  Mix_SetPanning(channel,left, right)

def channel_set_position(int channel,int angle,int distance):
  """ set "channel"'s panning to "left" and "right". passing -1 as channel will set the panning of all 
  channels """
  
  return Mix_SetPosition(channel, angle, distance)

def pause_channel(int channel=-1):
  """ pause "channel" or all playing channels if -1 """

  Mix_Pause(channel)

def resume_channel(int channel=-1):
  """ resume "channle" or all playing channels if -1 """

  Mix_Resume(channel)

def halt_channel(int channel=-1):
  """ stop "channel" or all channels if -1 """
  
  Mix_HaltChannel(channel)

def expire_channel(int channel, int ms):
  """ stop "channel" or all channels if -1 in so many "ms" """

  Mix_ExpireChannel(channel, ms)

def fade_out_channel(int channel, int ms):
  """ fade out "channel" or all chanels if -1 in so many "ms" """

  Mix_FadeOutChannel(channel,ms)

def channel_is_playing(int channel=-1):
  """ tests wether "channel" is currently playing. passing -1 will return how many 
  channels are currently playing """

  return Mix_Playing(channel)

def channel_is_paused(int channel=-1):
  """ tests wether "channel" is currently paused. passing -1 will return how many 
  channels are currently paused """

  return Mix_Paused(channel)

def channel_is_fading(int channel):
  """ tests wether "channel" is currently fading. passing -1 is not valid """

  fade=Mix_FadingChannel(channel)

  if fade==MIX_NO_FADING:
    return 0
  elif fade==MIX_FADING_OUT:
    return 1
  elif fade==MIX_FADING_IN:
    return -1

cdef class Sample:
  """ class for loading and playing audio samples.

  all play functions accept either a channel to play on or 
  the default (-1) is to play on the next available channel. 
  the play functions will return the channel the sound 
  was played on.

  to play sounds on specific channels you might want to use 
  the AudioChannel class.
  """

  # this is where our sound is actually stored
  cdef Mix_Chunk *_chunk

  cdef __load(self,char *file):
    self._chunk=Mix_LoadWAV(file)
    if not self._chunk:
      raise "Audio Error: %s" % Mix_GetError()
  
  def __init__(self,file):
    """ construct a new audio class with file as the sound """
    self.__load(file)

  cdef void __del__(self):
    Mix_FreeChunk(self._chunk)
    self._chunk=NULL

  def play(self,channel=-1,loops=0):
    """ play the sound on the specified channel "loops" amount of times 
    if loops is -1 the sound will play forever 
    if loops is 1 the sound will be played twice
    """

    if Mix_PlayChannel(channel,self._chunk,loops)==-1:
      raise "Audio Error: %s" % Mix_GetError()

  def play_timed(self,int ms,int channel=-1, int loops=0):
    c=Mix_PlayChannelTimed(channel,self._chunk,loops,ms)

    if c==-1:
      raise "Audio Error: %s" % Mix_GetError()

    return c

  def play_fade_in(self,int ms, int channel=-1, int loops=0):
    c=Mix_FadeInChannel(channel,self._chunk,loops,ms)

    if c==-1:
      raise "Audio Error: %s" % Mix_GetError()

    return c

  def play_fade_in_timed(self,int fade, int time, int channel=-1, int loops=0):
    c=Mix_FadeInChannelTimed(channel,self._chunk,loops,fade,time)
    
    if c==-1:
      raise "Audio Error: %s" % Mix_GetError()

    return c
  
  property volume:
    def __get__(self):
      return Mix_VolumeChunk(self._chunk,-1)
    def __set__(self,value):
      Mix_VolumeChunk(self._chunk,value)


cdef class Channel:
  """ class to encapsulate a channel.
  
  this simplifies having to deal with channels.

  all the play functions accept an audiofile class, 
  this class is used to play the sound but the channel 
  argument is passed as well for simplicity """

  def __init__(int channel):
    self.channel=channel


  property volume:
    def __get__(self):
      return audio_channel_volume(self.channel,-1)
    def __set__(self,val):
      audio_channel_volume(self.channel,val)

  def fade_out(self,ms):
    audio_fade_out_channel(self.channel,ms)

  def expire_channel(self,ms):
    audio_export_channel(self.channel,ms)

  def pause(self):
    audio_pause_channel(self)

  def stop(self):
    audio_halt_channel(self.channel)

  def resume(self):
    audio_resume_channel(self.channel)
    
  def is_paused(self):
    return audio_channel_is_paused(self.channel)

  def is_playing(self):
    return audio_channel_is_playing(self.channel)

  def is_fading(self):
    return audio_channel_is_fading(self.channel)

  def play(self,audiofile,loops=0):
    audiofile.play(self.channel,loops)

  def play_timed(self,audiofile,ms,loops=0):
    audiofile.play_timed(ms,self.channel,loops)

  def play_fade_in(self,audiofile,ms,loops=0):
    audiofile.play_fade_in(ms,self.channel,loops)

  def play_fade_in_timed(self,audiofile,fade,time,loops=0):
    audiofile.play_fade_in_timed(fade,time,self.channel,loops)


cdef class Music:
  """ class for managing music playback """

  cdef Mix_Music *_music
  cdef object finished_callback

  cdef __load(self,char *file):
    self._music=Mix_LoadMUS(file)
    if not self._music:
      raise "%s" % Mix_GetError()

  cdef void __del__(self):
    Mix_FreeMusic(self._music)
    self._music=NULL
  
  def __init__(self, file):
    """ create a new MusicFile class with file as the music"""
    self.__load(file)
      
  def play(self,int loops=0):
    if Mix_PlayMusic(self._music,loops)==-1:
      print "%s" % Mix_GetError()
      return 

    #FIXME: see def end_music_callback
    #Mix_HookMusicFinished(end_music_callback)
    
  def play_fade_in(self,int ms,int loops=0):
    if Mix_FadeInMusic(self._music,loops,ms):
      print "%s" % Mix_GetError()

  def play_fade_in_at_pos(self,int ms,double position,int loops=0):
    if Mix_FadeInMusicPos(self._music,loops,ms,position): 
      print "%s" % Mix_GetError()

  property volume:
    def __get__(self):
      return Mix_VolumeMusic(-1)
    def __set__(self,int value):
      Mix_VolumeMusic(value)

  def rewind(self):
    Mix_RewindMusic()

  def set_position(self,double position):
    if Mix_SetMusicPosition(position)==-1:
      print "set_position failed: %s" % Mix_GetError()

  def stop(self):
    Mix_HaltMusic()

  def stop_fade_out(self,int ms):
    if Mix_FadeOutMusic(ms)==0:
      print "%s" % Mix_GetError()

  def resume(self):
    Mix_ResumeMusic()

  def pause(self):
    Mix_PauseMusic()

  def get_type(self):
    type=Mix_GetMusicType(self._music)

    if type==MUS_NONE:
      return None
    elif type==MUS_CMD:
      return "Command based playing"
    elif type==MUS_WAV:
      return "WAVE/RIFF"
    elif type==MUS_MOD:
      return "MOD"
    elif type==MUS_MID:
      return "MIDI"
    elif type==MUS_OGG:
      return "OGG"
    elif type==MUS_MP3:
      return "MP3"
    else:
      return "Unknown"

  def is_playing(self):
    return Mix_PlayingMusic()==1

  def is_paused(self):
    return Mix_PausedMusic()==1

  def is_fading(self):
    fading=Mix_FadingMusic()

    if fading==MIX_NO_FADING:
      return 0
    elif fading==MIX_FADING_OUT:
      return 1
    elif fading==MIX_FADING_IN:
      return -1

#FIXME: this doesnt work right!
cdef void end_music_callback():
  print "end music"
