#!/usr/bin/ruby -w

# $Id: euphoria,v 1.31 2005/07/07 16:33:54 tsauerbeck Exp $

require "ecore"
require "ecore_x"
require "ecore_evas"
require "edje"
require "esmart"
require "esmart_draggies"
require "esmart_container"
require "xmmsclient"
require "xmmsclient_ecore"
require "singleton"
require "yaml"
require "find"
require "fileutils"

require "euphoria/playlist"
require "euphoria/playlist_item"
require "euphoria/slider"
require "euphoria/playtime_updater"
require "euphoria/playback_status_handler"
require "euphoria/file_dialog"

Config = Struct.new("Config", :ipc_path, :theme, :time_display_elapsed)

class Euphoria
	DATA_DIR = "/usr/local/share/euphoria"
	DATA_DIR_PRIV = File.expand_path("~/.xmms2/clients/euphoria")

	include Singleton

	attr_reader :xmms, :ee, :edje

	def initialize
		@edje = nil
		@xmms = nil

		# settings
		unless File.directory?(DATA_DIR_PRIV)
			FileUtils.mkdir_p(DATA_DIR_PRIV)
		end

		@config_file = DATA_DIR_PRIV + "/config.yaml"

		load_config
		init_xmms
		init_gui

		blk = lambda { |item| ui_fill_track_info(item) }
		@playlist = Playlist.new(@xmms, blk)

		setup_callbacks

		@playtime_updater = PlaytimeUpdater.new(xmms) do |current_pos|
			ui_refresh_time(current_pos)

			# refresh the seeker
			if !@playlist.current_item.nil?
				d = @playlist.current_item.duration
				if d > 0
					ui_refresh_seeker(current_pos / d.to_f)
				end
			end
		end

		PlaybackStatusHandler.new(xmms) do |state|
			@playback_state = state

			signal_playback_state
			@playtime_updater.enabled =
				(@playback_state == XmmsClient::XmmsClient::PLAY)
		end

		# is repeat enabled?
		s = @xmms.configval_get("playlist.repeat_all").wait.value
		@repeat = (s == "1")
		@edje.emit_signal("TOGGLE_REPEAT_MODE", "Euphoria") if @repeat

		@sliders = {}

		unless @edje.data("has_volume_slider").nil?
			p = "player.slider.volume"
			k = "output.volume"
			@sliders[k] = VolumeSlider.new(@edje, @xmms, p, k)
		end

		unless @edje.data("has_eq_slider").nil?
			1.upto(10) do |i|
				p = "player.slider.eq.band%02i" % i
				k = "effect.equalizer.gain#{i - 1}"
				@sliders[k] = EqSlider.new(@edje, @xmms, p, k)
			end
		end

		handle_current_pos(@xmms.playlist_current_pos.wait)
	end

	def show
		if @edje.data("has_separate_playlist").nil?
			@playlist.show(@theme, false)
		end

		@ee.show
	end

	def save_config
		File.open(@config_file, "w") { |fo| YAML.dump(@config, fo) }
	end

	def show_file_dialog
		FileDialog.open(@file_dialog_theme) do |sel|
			if File.directory?(sel)
				files = []
				Find.find(sel) { |f| files << f if File.file?(f) }

				files.sort.each do |f|
					@xmms.playlist_add("file://" + f).wait
				end
			elsif File.exists?(sel)
				@xmms.playlist_add("file://" + sel).wait
			end
		end
	end

private
	def init_gui
		@ee = Ecore::Evas::SoftwareX11.new
		@ee.title = "Euphoria"
		@ee.name_class = ["Euphoria", "Euphoria"]
		@ee.borderless = true

		#@ee.evas.font_path_append("/usr/X11R6/lib/X11/fonts/TTF")
		#@ee.evas.font_path_append(ENV["HOME"] + "/.fonts")

		@ee.on_pre_render { Edje::thaw }
		@ee.on_post_render { Edje::freeze }

		@ee.on_resize do
			x, y, w, h = @ee.geometry

			@edje.resize(w, h)
			@dragger.resize(w, h)
		end

		@dragger = Esmart::Draggies.new(@ee)
		@dragger.name = "dragger"
		@dragger.button = 1
		@dragger.show

		@theme = find_theme(@config.theme)
		raise "Cannot find theme - #{@config.theme}" if @theme.nil?

		if Edje.collections(@theme).include?("esmart.filedialog")
			@file_dialog_theme = @theme
		else
			@file_dialog_theme = find_theme("file_dialog")
		end

		@edje = Edje::Edje.new(@ee.evas)
		@edje.load(@theme, "euphoria")
		@edje.name = "edje"
		@edje.show

		w, h = @edje.get_size_max
		@ee.set_size_max(w, h)

		w, h = @edje.get_size_min
		@ee.set_size_min(w, h)

		@ee.resize(w, h)

		@ee.shaped = !@edje.data("shaped").nil?

		ui_refresh_seeker(0)
	end

	def init_xmms
		@xmms = XmmsClient::XmmsClient.new("Euphoria")
		@xmms.connect(ENV["XMMS_PATH"] || @config.ipc_path)
		@xmms.add_to_ecore_mainloop

		@xmms.on_disconnect do
			puts "daemon exited, shutting down"
			Ecore.main_loop_quit
		end
	end

	def setup_callbacks
		@edje.on_signal("quit") do
			Ecore::main_loop_quit
		end

		@edje.on_signal("close") do
			Ecore::main_loop_quit
		end

		@edje.on_signal("playback.play") do
			case @playback_state
				when XmmsClient::XmmsClient::PLAY
					@xmms.playback_stop.wait
					sleep(0.5) # evil hack, fix xmms2d instead
					@xmms.playback_start.wait
				else
					@xmms.playback_start.wait
			end
		end

		@edje.on_signal("playback.stop") do
			@xmms.playback_stop.wait
		end

		@edje.on_signal("playback.pause") do
			case @playback_state
				when XmmsClient::XmmsClient::PLAY
					@xmms.playback_pause.wait
				when XmmsClient::XmmsClient::PAUSE
					@xmms.playback_start.wait
			end
		end

		@edje.on_signal("playback.next") do
			@xmms.playlist_set_next_rel(1).wait
			@xmms.playback_tickle.wait

			if @playback_state != XmmsClient::XmmsClient::PLAY
				@xmms.playback_start.wait
			end
		end

		@edje.on_signal("playback.prev") do
			@xmms.playlist_set_next_rel(-1).wait
			@xmms.playback_tickle.wait

			if @playback_state != XmmsClient::XmmsClient::PLAY
				@xmms.playback_start.wait
			end
		end

		@edje.on_signal("TOGGLE_TIME_DISPLAY_MODE") do
			@config.time_display_elapsed = !@config.time_display_elapsed
		end

		@edje.on_signal("DEBUG") do |sig, source|
			puts source
		end

		@edje.on_signal("playlist.open") { @playlist.show(@theme, true) }

		@edje.on_signal("file_dialog.open") do
			show_file_dialog
		end

		@edje.on_signal("TOGGLE_REPEAT_MODE") do |sig, source|
			# don't react on our own emission
			next if source == "Euphoria"

			@repeat = !@repeat

			v = @repeat ? "1" : "0"
			@xmms.configval_set("playlist.repeat_all", v).wait
		end

		@xmms.broadcast_configval_changed.notifier do |res|
			k, v = res.stringlist

			case k
				when "playlist.repeat_all"
					tmp = (v == "1")
					if tmp != @repeat
						@repeat = tmp
						@edje.emit_signal("TOGGLE_REPEAT_MODE", "Euphoria")
					end
				when "output.volume"
					@sliders[k].value = v
				when /effect\.equalizer\.gain(\d+)/
					@sliders[k].value = v.to_f
			end
		end

		@xmms.broadcast_playlist_current_pos.notifier do |res|
			handle_current_pos(res)
		end
	end

	def handle_current_pos(res)
		begin
			pos = res.value
		rescue XmmsClient::ValueError
		else
			ui_fill_track_info(@playlist[pos])
		end
	end

	def ui_fill_track_info(item)
		unless item.nil?
			msg = Edje::Message.new(0, item.artist, item.title, item.album)
			@edje.send_message(msg)
		end
	end

	def ui_refresh_time(time)
		return unless @edje.part_exists?("player.time")

		fmt = "%i:%02i"

		unless @config.time_display_elapsed
			time = @playlist.current_item.duration - time
			fmt = "-" + fmt
		end

		tmp = fmt % [time / 60, time % 60]
		@edje.part("player.time").text = tmp
	end

	def ui_refresh_seeker(pos)
		if @edje.part_exists?("seeker")
			@edje.part("seeker").set_drag_value(pos, 0.1)
			#@edje.thaw
			#@edje.evas.render
		end
	end

	def signal_playback_state
		sig = case @playback_state
			when XmmsClient::XmmsClient::PAUSE
				"PLAYBACK_STATE_PAUSED"
			when XmmsClient::XmmsClient::PLAY
				"PLAYBACK_STATE_PLAYING"
			else
				"PLAYBACK_STATE_STOPPED"
		end

		@edje.emit_signal(sig, "Euphoria")
	end

	def load_config
		@config = nil

		if File.exist?(@config_file)
			File.open(@config_file) { |fo| @config = YAML.load(fo) }
		end

		if @config.nil? || !@config.is_a?(Config)
			@config = Config.new
			@config.ipc_path = nil
			@config.theme = "winter"
			@config.time_display_elapsed = true
		end
	end

	def find_theme(name)
		dirs = [DATA_DIR_PRIV, DATA_DIR]
		dirs.each do |dir|
			tmp = File.join(dir, "themes/#{name}.edj")
			return tmp if File.exist?(tmp)
		end

		nil
	end
end

$x = Euphoria.instance
Euphoria.instance.show
Ecore::main_loop_begin
Euphoria.instance.save_config
