# Bryan Schumaker (11/05/2010)

import libsaria
import libsaria.sources
import libsaria.path.files
from libsaria import threads
splitext  = libsaria.path.splitext

from libsaria.trees import *
from index import Index

os    = None
tagpy = None
Track = None

FileRef   = None
fs_tree   = None
tag_tree  = None
index     = None
tracks    = None
locations = None
size      = None
visible   = None
source    = None
ttable    = None

cur_id   = -1
lib_lock = threads.get_mutex("library")
loaded   = False
filtered = False
badfiles = set()

def init():
	global source
	global ttable

	import string
	ttable = dict()
	for s in string.punctuation:
		ttable[ord(s)] = u""
	for s in string.lowercase:
		ttable[ord(s)] = ord(s) - 32

	source = libsaria.sources.Source()
	source.get_attrs  = get_attrs
	source.set_attr   = set_attr
	source.get_cur_id = get_cur_id
	source.save       = save

def reset():
	from libsaria.trees import FSTree, DLFSTree, DLValTree
	from index import Index

	global index
	global tracks
	global fs_tree
	global tag_tree
	global locations
	global visible

	locations = []
	fs_tree   = DLFSTree()
	tag_tree  = DLValTree()
	index     = Index()
	tracks    = dict()
	size      = 0
	visible   = set()

def default_objects():
	return ([], dict(), DLFSTree(), DLValTree(), Index())

def load():
	global fs_tree
	global tag_tree
	global index
	global tracks
	global locations
	global lib_lock
	global loaded

	objects = libsaria.storage.load_obj("library", default_objects)
	if objects == None or len(objects) != 5:
		reset()
	else:
		(locations, tracks, fs_tree, tag_tree, index) = objects

	lib_lock.acquire()
	loaded = True
	lib_lock.release()

def is_loaded():
	lib_lock.acquire()
	val = loaded
	lib_lock.release()
	return val

def init_bg():
	init()
	load()

def save():
	global sources
	libsaria.storage.save_obj("library", (locations, tracks, fs_tree, tag_tree, index))

def walk():
	for tag in tag_tree.walk():
		yield tag[3]

def get_locations():
	for loc in locations:
		yield loc

def artists():
	keys = tag_tree.keys()
	keys.sort()
	res = []
	for key in keys:
		res.append( (tag_tree[key].value, key) )
	return res

def albums(artist):
	art = tag_tree[artist]
	keys = art.keys()
	keys.sort()
	res = []
	for key in keys:
		res.append( (art[key].value, key) )
	return res

def titles(artist, album):
	art  = tag_tree[artist]
	alb  = art[album]
	keys = alb.keys()
	keys.sort()
	res = []
	for key in keys:
		res.append( (alb[key].value, key) )
	return res

def song_id(artist, album, title):
	art   = tag_tree[artist]
	alb   = art[album]
	title = alb[title]
	return title.keys()[0]

def has_id(id):
	return tracks.get(id, None) != None

def get_cur_id():
	return cur_id

def get_attrs(id, *attrs):
	res = []
	rec = tracks.get(id, None)
	if rec == None:
		return [0] * len(attrs)

	get  = rec.__dict__.get
	tags = rec.tags.walk_vals_backwards()
	append = res.append
	for attr in attrs:
		if attr == "id":
			append(id)
		elif attr == "filepath":
			append(rec.fs.walk_path_backwards())
		elif attr == "art":
			from libsaria import lastfm
			append(lastfm.get_artwork_id(id))
		elif attr == "artist":
			append(tags[0])
		elif attr == "album":
			append(tags[1])
		elif attr == "title":
			append(tags[2])
		else:
			append(get(attr, None))
	if len(res) == 1:
		return res[0]
	return res

def walk_library(*attrs):
	res = []
	for id in walk():
		res.append(get_attrs(id, *attrs))
	return res

def set_attr(id, attr, new_value):
	rec = tracks.get(id, None)
	if rec:
		if attr == "art":
			from libsaria import lastfm
			artist, album = get_attrs(id, "artist", "album")
			lastfm.set_artwork_tags(artist, album, new_value)
		else:
			rec.__dict__[attr] = new_value
		save()

def inc_count(id = cur_id):
	rec = tracks.get(id, None)
	if rec:
		rec.count += 1
		save()

def play_id(id):
	if load_id(id) == True:
		libsaria.controls.play()
		return True
	return False

def load_id(id):
	global cur_id
	if tracks.get(id, None) == None:
		return False
	cur_id = id
	return source.load_file(get_attrs(id, "filepath"))

def filter(text):
	global visible
	global index
	global filtered

	if len(text) > 0:
		visible  = index.filter(text)
		filtered = True
	else:
		visible  = None
		filtered = False

def test_filter(text):
	return index.filter(text)

def is_visible(id):
	global filtered
	global visible
	if filtered == False:
		return True
	else:
		return id in visible

def num_visible():
	if filtered == True:
		return len(visible)
	else:
		return len(tag_tree)

def add_location(path):
	global locations
	commonprefix = libsaria.path.commonprefix
	if path.__class__ == unicode:
		path = str(path)
	to_remove = []
	for loc in locations:
		# Don't add a subdirectory of a current path
		if commonprefix([path, loc]) == loc:
			return
		# Remove subdirectories of the requested path
		if commonprefix([path, loc]) == path:
			to_remove.append(loc)
	for loc in to_remove:
		locations.remove(loc)
	locations.append(path)

def scan(path):
	add_location(path)
	update()
	save()

def insert_track(path, ref):
	global tracks
	global ttable
	global fs_tree
	global tag_tree
	global index
	tags   = ref.tag()
	audio  = ref.audioProperties()
	ino    = os.stat(path).st_ino
	track  = Track(tags, audio)
	artist = tags.artist or u"Unknown Artist"
	album  = tags.album  or u"Unknown Album"
	title  = tags.title  or u"Unknown Title"
	fs     = fs_tree.insert_path(path)
	tag    = tag_tree.insert( [artist.translate(ttable),
				   album.translate(ttable),
				   title.translate(ttable), ino],
				  [artist, album, title, ino]
				)

	index.insert(ino, (artist, album, title))
	track.fs   = fs
	track.tags = tag
	tracks[ino] = track

def update_path(path):
	global badfiles
	tree = libsaria.path.make_tree(path)
	for path in tree.walk_paths():
		ext = splitext(path)[1]
		if ext in badfiles:
			continue
		try:
			ref = FileRef(path)
		except Exception, e:
			badfiles.add(ext)
			print e
			continue
		try:
			insert_track(path, ref)
		except Exception, e:
			print e

def update():
	global os
	global tagpy
	global FileRef
	global Track

	import os
	import tagpy
	from track import Track
	FileRef = tagpy.FileRef

	sep = libsaria.path.sep
	for path in locations:
		update_path(path)

def update_bg2():
	update()
	save()

def update_bg():
	if is_loaded():
		from libsaria import threads
		thr = threads.BG_Thread(update_bg2)
		thr.start()
