#!/usr/bin/python2.3
import sys, os, re
import bsddb

import ogg.vorbis
import mad
import musicbrainz

class trmdb:
	def __init__(self, prefix="", dbname_fs="trmdb_fn_to_sig.db", dbname_sf="trmdb_sig_to_fn.db", mode="c"):
		self.ftos = bsddb.hashopen(os.path.join(prefix,dbname_fs),mode)
		self.stof = bsddb.hashopen(os.path.join(prefix,dbname_sf),mode)

	def close(self):
		self.ftos.close()
		self.stof.close()

	### Low level access functions
	def get_sig(self, sig):
		if self.stof.has_key(sig):
			return self.stof[sig].split("\0")
		return None

	def set_sig(self, sig, fns):
		if len(fns) <= 0:
			del self.stof[sig]
		else:
			self.stof[sig] = "\0".join(fns)

	def get_sigs(self):
		return self.stof.keys()

	def get_fn(self, fn):
		if self.ftos.has_key(fn):
			return self.ftos[fn]
		return None

	def set_fn(self, fn, sig):
		self.ftos[fn] = sig

	def rm_fn(self, fn):
		del self.ftos[fn]

	def get_fns(self):
		return self.ftos.keys()

	### operations
	def remove_fn_from_sig(self, sig, fn):
		fns = self.get_sig(sig)
		if not fns or not fn in fns:
			raise KeyError
		fns.remove(fn)
		self.set_sig(sig, fns)

	def add_fn_to_sig(self, sig, fn):
		fns = self.get_sig(sig)
		if not fns:
			fns = []
		fns.append(fn)
		self.set_sig(sig, fns)

	### end user methods
	def add_fn_sig(self, fn, sig):
		if self.get_fn(fn):
			raise KeyError
		self.add_fn_to_sig(sig, fn)
		self.set_fn(fn, sig)
	
	def rm_fn(self, fn):
		sig = self.get_fn(fn)
		if not sig:
			raise KeyError
		self.remove_fn_from_sig(sig, fn)
		self.rm_fn(fn)

	def update_fn_sig(self, fn, sig):
		oldsig = self.get_fn(fn)
		if oldsig:
			self.remove_fn_from_sig(oldsig, fn)
		self.add_fn_to_sig(sig, fn)
		self.set_fn(fn, sig)

	### sig calculation
	def make_sig(self, file):
		(path, ext) = os.path.splitext(file)
		if ext.lower() == '.ogg':
			ff = ogg.vorbis.VorbisFile(file)
		elif ext.lower() == '.mp3':
			ff = MadWrapper(file)
		else:
			return None
		info = ff.info()
		trm = musicbrainz.trm()
		trm.SetPCMDataInfo(info.rate, info.channels, 16)
		while 1:
			(buff, bytes, bit) = ff.read()
			if bytes == 0:
				break
			if trm.GenerateSignature(buff):
				break
		return trm.FinalizeSignature()

"""
These classes are copied from the musicbrainz examples
"""
class MadWrapper:
	def __init__(self, filename):
		self.ff = mad.MadFile(filename)
	
	def read(self):
		buff = self.ff.read()
		if buff:
			return (buff, len(buff), None)
		else:
			return ('', 0, None)

	def info(self):
		if self.ff.mode() == mad.MODE_SINGLE_CHANNEL:
			channels = 1
		else:
			channels = 2
		return AudioInfo(self.ff.samplerate(), channels)

class AudioInfo:
	def __init__(self, rate, channels):
		self.rate = rate
		self.channels = channels
