core :: union_find
union–find algorithm using an efficient disjoint-set data structure
# Music Player Daemon client library
module mpd
import socket
# Connection to a MPD server
class MPDConnection
	# Socket connection to server.
	var socket: nullable TCPStream = null
	# Server hostname.
	var host: String
	# Server port.
	var port: Int
	# Server password.
	var password: nullable String
	# Last occured error.
	var error: nullable String = null
	# Connect to the MPD server
	fun connect
	do
		var p: nullable TCPStream = null
		p = new TCPStream.connect(host, port)
		assert p.connected
		sys.nanosleep(0,5000)
		var rep = p.read(1024)
		assert not rep.is_empty
		if not rep.has_prefix("OK") then
			print "MPD responded {rep}"
			abort
		end
		socket = p
		var password = password
		if password != null then
			write_and_check("password {password}\n")
		end
	end
	# Write a command to the MPD server
	protected fun write_and_check(msg: String)
	do
		if socket == null then connect
		socket.write(msg)
		sys.nanosleep(0,5000)
		var rep = socket.read(1024)
		if not rep.has_prefix("OK") then
			print "Error: MPD responded {rep}"
			socket.close
			socket = null
		end
	end
	# Get MPD server status
	fun status: nullable ServerStatus
	do
		if socket == null then connect
		var volume: nullable Int = null
		var state: nullable String = null
		var elapsed: nullable Int = null
		var time_at: nullable Int = null
		var time_total: nullable Int = null
		# get current status
		socket.write("status\n")
		var rep = socket.read(1024)
		for l in rep.split_with("\n") do
			var words = l.split_with(" ")
			if words.length > 1 then
				var key = words[0].to_lower
				var first_space = l.chars.index_of(' ')
				var rest = l.substring_from(first_space+1)
				if  key == "volume:" then
					volume = rest.to_i
					if volume == -1 then volume = null
				else if  key == "state:" then
					state = rest
				else if  key == "elapsed:" then
					elapsed = rest.to_i
				else if  key == "time:" then
					var times = rest.split(":")
					time_at = times[0].to_i
					time_total = times[1].to_i
				end
			end
		end
		if state != null then
			var status = new ServerStatus(volume, state, elapsed, time_at, time_total)
			return status
		else
			return null
		end
	end
	# Set the volume relatively
	fun relative_volume=(diff: Int)
	do
		if socket == null then connect
		var status = status
		if status != null then
			var vol = status.volume
			if vol != null then
				var new_vol = vol + diff
				new_vol = new_vol.clamp(0, 100)
				volume = new_vol
				return
			end
		end
		error = "Cannot get volume"
	end
	# Set MPD server volume.
	fun volume=(vol: Int) do write_and_check("setvol {vol}\n")
	# Pause music playing on the MPD server
	fun pause do write_and_check("pause\n")
	# Stop music playing on the MPD server
	fun stop do write_and_check("stop\n")
	# Play music playing on the MPD server
	fun play do write_and_check("play\n")
	# Get information on the currently playing song on the MPD server
	fun current_song: nullable SongInfo
	do
		if socket == null then connect
		var album: nullable String = null
		var artist: nullable String = null
		var title: nullable String = null
		var time: nullable Int = null
		socket.write("currentsong\n")
		var rep = socket.read(1024)
		for l in rep.split_with("\n") do
			var words = l.split_with(" ")
			if words.length > 1 then
				var key = words[0].to_lower
				var first_space = l.chars.index_of(' ')
				var rest = l.substring_from(first_space+1)
				if key == "album:" then
					album = rest
				else if key == "artist:" then
					artist = rest
				else if key == "title:" then
					title = rest
				else if key == "time:" then
					time = rest.to_i
				end
			end
		end
		if album != null and artist != null and
			title != null and time != null then
			return new SongInfo(album, artist, title, time)
		else
			return null
		end
	end
	# Load playlist named `name`.
	fun load_playlist(name: String)
	do
		write_and_check "load \"{name}\""
	end
end
# MPD song info
class SongInfo
	# Song album.
	var album: String
	# Song artist.
	var artist: String
	# Song title.
	var title: String
	# Song total duration.
	var time: Int
end
# MPD server status
class ServerStatus
	# MPD server volume.
	var volume: nullable Int
	# Playback state (play/stop/pause).
	var state: String
	# Is MPD server playing?
	fun playing: Bool do return state == "play"
	# Is MPD server stopped?
	fun stopped: Bool do return state == "stop"
	# Time elapsed within the current song.
	var elapsed: nullable Int
	# TODO comment
	var time_at: nullable Int
	# Total time of the current song.
	var time_total: nullable Int
	# Get the cursor position on the song duration.
	fun time_ratio: nullable Float do
		if time_at == null or time_total == null then return null
		return time_at.to_f / time_total.to_f
	end
	redef fun to_s do
		var vol = "unknown"
		if volume != null then vol = volume.to_s
		var time_at = time_at
		var time_total = time_total
		var elapsed = elapsed
		if time_at != null and time_total != null and elapsed != null then
			return "volume: {vol}\nstate: {state}\nelapsed: {elapsed}\ntime_[at|total]: {time_at}:{time_total}\ntime_ratio: {time_ratio.as(not null)}"
		else
			return "volume: {vol}\nstate: {state}"
		end
	end
end
lib/mpd/mpd.nit:17,1--257,3