#---------------------------------------------------------------------------
#
#   Albow - Music
#
#---------------------------------------------------------------------------

from __future__ import division
import os
from random import randrange

try:
	from pygame.mixer import music
except ImportError:
	music = None
	print "Music not available"

if music:
	import root
	music.set_endevent(root.MUSIC_END_EVENT)

from resource import resource_path
from root import schedule

#---------------------------------------------------------------------------

fadeout_time = 1  # Time over which to fade out music (sec)
change_delay = 2  # Delay between end of one item and starting the next (sec)

#---------------------------------------------------------------------------

music_enabled = True
current_music = None
current_playlist = None
next_change_delay = 0

#---------------------------------------------------------------------------

class PlayList(object):
	"""A collection of music filenames to be played sequentially or
	randomly. If random is true, items will be played in a random order.
	If repeat is true, the list will be repeated indefinitely, otherwise
	each item will only be played once."""

	def __init__(self, items, random = False, repeat = False):
		self.items = list(items)
		self.random = random
		self.repeat = repeat
	
	def next(self):
		"""Returns the next item to be played."""
		items = self.items
		if items:
			if self.random:
				n = len(items)
				if self.repeat:
					n = (n + 1) // 2
				i = randrange(n)
			else:
				i = 0
			item = items.pop(i)
			if self.repeat:
				items.append(item)
			return item

#---------------------------------------------------------------------------

def get_music(*names, **kwds):
	"""Return the full pathname of a music file from the "music" resource
	subdirectory."""
	prefix = kwds.pop('prefix', "music")
	return resource_path(prefix, *names)

def get_playlist(*names, **kwds):
	prefix = kwds.pop('prefix', "music")
	dirpath = get_music(*names, **{'prefix': prefix})
	items = [os.path.join(dirpath, filename)
		for filename in os.listdir(dirpath)
			if not filename.startswith(".")]
	items.sort()
	return PlayList(items, **kwds)

def change_playlist(new_playlist):
	"""Fade out any currently playing music and start playing from the given
	playlist."""
	#print "albow.music: change_playlist" ###
	global current_music, current_playlist, next_change_delay
	if music and new_playlist is not current_playlist:
		current_playlist = new_playlist
		if music_enabled:
			music.fadeout(fadeout_time * 1000)
			next_change_delay = max(0, change_delay - fadeout_time)
			jog_music()
		else:
			current_music = None

def change_music(new_music, repeat = False):
	"""Fade out any currently playing music and start playing the given
	music file."""
	#print "albow.music: change_music" ###
	if music and new_music is not current_music:
		if new_music:
			new_playlist = PlayList([new_music], repeat = repeat)
		else:
			new_playlist = None
		change_playlist(new_playlist)

def music_end():
	#print "albow.music: music_end" ###
	schedule(next_change_delay, jog_music)

def jog_music():
	"""If no music is currently playing, start playing the next item
	from the current playlist."""
	if music_enabled and not music.get_busy():
		start_next_music()

def start_next_music():
	"""Start playing the next item from the current playlist immediately."""
	#print "albow.music: start_next_music" ###
	global current_music, next_change_delay
	if music_enabled and current_playlist:
		next_music = current_playlist.next()
		if next_music:
			print "albow.music: loading", repr(next_music) ###
			music.load(next_music)
			music.play()
			next_change_delay = change_delay
		current_music = next_music

def get_music_enabled():
	return music_enabled

def set_music_enabled(state):
	global music_enabled
	if music_enabled != state:
		music_enabled = state
		if state:
			# Music pausing doesn't always seem to work.
			#music.unpause()
			if current_music:
				# After stopping and restarting currently loaded music,
				# fadeout no longer works.
				#print "albow.music: reloading", repr(current_music) ###
				music.load(current_music)
				music.play()
			else:
				jog_music()
		else:
			#music.pause()
			music.stop()

#---------------------------------------------------------------------------

from pygame import Rect
from albow.widget import Widget
from albow.controls import Label, Button, CheckBox
from albow.layout import Row, Column, Grid
from albow.dialogs import Dialog

class EnableMusicControl(CheckBox):
	
	def get_value(self):
		return get_music_enabled()
	
	def set_value(self, x):
		set_music_enabled(x)

class MusicVolumeControl(Widget):

	def __init__(self, **kwds):
		Widget.__init__(self, Rect((0, 0), (100, 20)), **kwds)
	
	def draw(self, surf):
		r = self.get_margin_rect()
		r.width = int(round(music.get_volume() * r.width))
		surf.fill(self.fg_color, r)
	
	def mouse_down(self, e):
		self.mouse_drag(e)
	
	def mouse_drag(self, e):
		m = self.margin
		w = self.width - 2 * m
		x = max(0.0, min(1.0, (e.local[0] - m) / w))
		music.set_volume(x)
		self.invalidate()

class MusicOptionsDialog(Dialog):

	def __init__(self):
		Dialog.__init__(self)
		emc = EnableMusicControl()
		mvc = MusicVolumeControl()
		controls = Grid([
			[Label("Enable Music"), emc],
			[Label("Music Volume"), mvc],
		])
		buttons = Button("OK", self.ok)
		contents = Column([controls, buttons], align = 'r', spacing = 20)
		contents.topleft = (20, 20)
		self.add(contents)
		self.shrink_wrap()

def show_music_options_dialog():
	dlog = MusicOptionsDialog()
	dlog.present()
