#!/usr/bin/python

import os
import sys
import fileinput
import re
import sha
import base64
import subprocess
import musicbrainz2.webservice as ws
import musicbrainz2
import logging
import glob
import urllib2
import MusicBrainzHelper
import FlacHelper

# GUI
from Tkinter import *

class Application(Frame):
	def __init__(self, flacFilenamesGlobs, master=None):
		Frame.__init__(self, master)

		# the list of files
		self.flacFilenames = []
		self.flacFilenamesIndex = 0
		self.flacFilename = ""

		for flacFilenamesGlob in flacFilenamesGlobs:
			self.flacFilenames += glob.glob(flacFilenamesGlob)

		self.mb = MusicBrainzHelper.MusicBrainzHelper()

		# an array of musicBrainz Results
		self.results = []
		self.resultsIndex = 0

		self.grid()
		self.createWidgets()
		self.loadFlac()


	def createWidgets(self):
		# labels showing flac attributes
		self.flacInfoFrame = Frame(self, borderwidth=3, relief=GROOVE)
		self.filenameVariable = StringVar()
		self.filenameLabelLabel = Label(self.flacInfoFrame, width=20, anchor=E, text="Filename:")
		self.filenameLabelLabel.grid(row=0, column=0)
		self.filenameLabel = Label(self.flacInfoFrame, width=80, textvariable=self.filenameVariable, anchor=W)
		self.filenameLabel.grid(row=0, column=1)

		self.discIdVariable = StringVar()
		self.discIdLabelLabel = Label(self.flacInfoFrame, width=20, anchor=E, text="Disc Id:")
		self.discIdLabelLabel.grid(row=1, column=0)
		self.discIdLabel = Label(self.flacInfoFrame, width=80, textvariable=self.discIdVariable, anchor=W)
		self.discIdLabel.grid(row=1, column=1)

		self.trackCountVariable = StringVar()
		self.trackCountLabelLabel = Label(self.flacInfoFrame, width=20, anchor=E, text="Track Count:")
		self.trackCountLabelLabel.grid(row=2, column=0)
		self.trackCountLabel = Label(self.flacInfoFrame, width=80, textvariable=self.trackCountVariable, anchor=W)
		self.trackCountLabel.grid(row=2, column=1)

		self.cddbArtistVariable = StringVar()
		self.cddbArtistLabelLabel = Label(self.flacInfoFrame, width=20, anchor=E, text="CDDB Artist:")
		self.cddbArtistLabelLabel.grid(row=3, column=0)
		self.cddbArtistLabel = Label(self.flacInfoFrame, width=80, textvariable=self.cddbArtistVariable, anchor=W)
		self.cddbArtistLabel.grid(row=3, column=1)

		self.cddbTitleVariable = StringVar()
		self.cddbTitleLabelLabel = Label(self.flacInfoFrame, width=20, anchor=E, text="CDDB Title:")
		self.cddbTitleLabelLabel.grid(row=4, column=0)
		self.cddbTitleLabel = Label(self.flacInfoFrame, width=80, textvariable=self.cddbTitleVariable, anchor=W)
		self.cddbTitleLabel.grid(row=4, column=1)

		self.mbidVariable = StringVar()
		self.mbidLabelLabel = Label(self.flacInfoFrame, width=20, anchor=E, text="MusicBrainz ID:")
		self.mbidLabelLabel.grid(row=5, column=0)
		self.mbidLabel = Label(self.flacInfoFrame, width=80, textvariable=self.mbidVariable, anchor=W)
		self.mbidLabel.grid(row=5, column=1)

		self.flacInfoFrame.grid(row=0)

		# frame for the information for this release
		self.releaseInfoFrame = Frame(self, borderwidth=3, relief=GROOVE)

		self.artistVariable = StringVar()
		self.artistLabelLabel = Label(self.releaseInfoFrame, width=20, anchor=E, text="Artist:")
		self.artistLabelLabel.grid(row=0, column=0)
		self.artistLabel = Label(self.releaseInfoFrame, width=80, textvariable=self.artistVariable, anchor=W)
		self.artistLabel.grid(row=0, column=1)

		self.titleVariable = StringVar()
		self.titleLabelLabel = Label(self.releaseInfoFrame, width=20, anchor=E, text="Title:")
		self.titleLabelLabel.grid(row=1, column=0)
		self.titleLabel = Label(self.releaseInfoFrame, width=80, textvariable=self.titleVariable, anchor=W)
		self.titleLabel.grid(row=1, column=1)

		self.scoreVariable = StringVar()
		self.scoreLabelLabel = Label(self.releaseInfoFrame, width=20, anchor=E, text="Score:")
		self.scoreLabelLabel.grid(row=2, column=0)
		self.scoreLabel = Label(self.releaseInfoFrame, width=80, textvariable=self.scoreVariable, anchor=W)
		self.scoreLabel.grid(row=2, column=1)

		self.mbTrackCountVariable = StringVar()
		self.mbTrackCountLabelLabel = Label(self.releaseInfoFrame, width=20, anchor=E, text="Track Count:")
		self.mbTrackCountLabelLabel.grid(row=3, column=0)
		self.mbTrackCountLabel = Label(self.releaseInfoFrame, width=80, textvariable=self.mbTrackCountVariable, anchor=W)
		self.mbTrackCountLabel.grid(row=3, column=1)

		self.mbidSaveVariable = StringVar()
		self.mbidSaveLabelLabel = Label(self.releaseInfoFrame, width=20, anchor=E, text="MusicBrainz ID:")
		self.mbidSaveLabelLabel.grid(row=4, column=0)
		self.mbidEntry = Entry(self.releaseInfoFrame, width=80, textvariable=self.mbidSaveVariable)
		self.mbidEntry.grid(row=4, column=1)
		self.mbidEntry.bind("<Key-Return>", self.newMbid)

		self.asinVariable = StringVar()
		self.asinLabelLabel = Label(self.releaseInfoFrame, width=20, anchor=E, text="ASIN (Amazon ID):")
		self.asinLabelLabel.grid(row=5, column=0)
		self.asinLabel = Label(self.releaseInfoFrame, width=80, textvariable=self.asinVariable, anchor=W)
		self.asinLabel.grid(row=5, column=1)

		self.releaseInfoFrame.grid(row=1)

		# frame for the tracks listbox
		self.tracksFrame = Frame(self, borderwidth=3, relief=GROOVE)
		self.yScroll = Scrollbar(self.tracksFrame, orient=VERTICAL)
		self.yScroll.grid(row=3, column=4, sticky=N+S)
		self.tracksListbox = Listbox(self.tracksFrame, width=100, yscrollcommand=self.yScroll.set)
		self.tracksListbox.grid(row=3, column=0, columnspan=3, sticky=N+S+E+W)
		self.yScroll["command"] = self.tracksListbox.yview
		self.tracksFrame.grid(row=2)

		# buttons along the bottom and status bar
		self.buttonsFrame = Frame(self, borderwidth=3, relief=GROOVE)
		self.saveButton = Button(self.buttonsFrame, text="Save", command=self.save)
		self.saveButton.grid(row=0, column=0)
		self.prevResultButton = Button(self.buttonsFrame, text="Previous Result", command=self.prevResult)
		self.prevResultButton.grid(row=0, column=1)
		self.nextResultButton = Button(self.buttonsFrame, text="Next Result", command=self.nextResult)
		self.nextResultButton.grid(row=0, column=2)
		self.prevFlacButton = Button(self.buttonsFrame, text="Previous FLAC")
		self.prevFlacButton.grid(row=0, column=3)
		self.nextFlacButton = Button(self.buttonsFrame, text="Next FLAC")
		self.nextFlacButton.grid(row=0, column=4)
		self.statusBarVariable = StringVar()
		self.statusBarLabel = Label(self.buttonsFrame, width=100, textvariable=self.statusBarVariable)
		self.statusBarLabel.grid(row=1, columnspan=5)
		self.buttonsFrame.grid(row=3)

		self.imageFrame = Frame(self, borderwidth=3, relief=GROOVE)

		# hot keys
		self.bind_all("<Alt-p>", self.prevResultEvent)
		self.bind_all("<Alt-n>", self.nextResultEvent)
		self.bind_all("<Control-p>", self.prevFlacEvent)
		self.bind_all("<Control-n>", self.nextFlacEvent)
		self.bind_all("<Control-N>", self.nextUntaggedFlacEvent)
		self.bind_all("<Alt-q>", self.quitEvent)
		self.bind_all("<Alt-s>", self.saveEvent)
		self.bind_all("<Alt-l>", self.loadEvent)

	def loadFlac(self, fastLoad=True, mbidEntered=None):
		if len(self.flacFilenames) == 0:
			return

		self.flacFilename = self.flacFilenames[self.flacFilenamesIndex]
		print self.flacFilename

		self.filenameVariable.set(self.flacFilename)
		self.clearAlbumData()

		if mbidEntered == None:
			self.flac = FlacHelper.FlacHelper(self.flacFilename)
			#try:
			[artist, title] = self.flac.getArtistAndTitle()
			#print "Artist: %s" % artist
			#print "Title: %s" % title
			#except:
			#	artist = None
			#	title = None
			try:
				[ discId, trackCount ] = self.flac.getDiscIdAndTrackCount()
				print "Disc Id: %s" % discId
				print "Track Count: %s" % trackCount
			except:
				discId = None
				trackCount = None
			mbid = self.flac.getMusicBrainzId()
			print "MusicBrainzID: %s" % mbid
			self.cddbArtistVariable.set(artist)
			self.cddbTitleVariable.set(title)
			self.discIdVariable.set(discId)
			self.trackCountVariable.set(trackCount)
			self.mbidVariable.set(mbid)
		else:
			mbid = mbidEntered

		# if we have a musicbrainz id already in this flac then load
		# that
		self.results = []
		self.mbidResults = 0
		if (mbid != None):
			mbidRelease = self.mb.getReleaseById(mbid)
			if (mbidRelease != None):
				result = ReleaseResult(mbidRelease, 100)
				self.results = [ result ]
				self.mbidResults = 1
				self.resultsIndex = 0

		# only do more lookups if we are doing a full load or don't have
		# any hits yet
		if not fastLoad or len(self.results) == 0 and discId != None:
			discIdResults = self.mb.findByDiscId(discId)
			self.results += discIdResults
			self.discIdResults = len(discIdResults)
			self.resultsIndex = 0
		else:
			self.discIdResults = 0

		# only do more lookups if we are doing a full load or don't have
		# any hits yet
		if not fastLoad or len(self.results) == 0:
			cddbResults = self.mb.findByArtistAndTitle(artist, title)
			self.cddbResults = len(cddbResults)
			self.results += cddbResults
			self.resultsIndex = self.mb.findBestResult(self.results, artist, title)
		else:
			self.cddbResults = 0

		# update the album view
		self.updateAlbumData()

	def clearAlbumData(self):
		self.cddbArtistVariable.set("Loading...")
		self.cddbTitleVariable.set("")
		self.discIdVariable.set("")
		self.mbidVariable.set("")
		self.artistVariable.set("")
		self.titleVariable.set("")
		self.scoreVariable.set("0")
		self.mbidSaveVariable.set("")
		self.tracksListbox.delete(0, END)
		self.mbTrackCountVariable.set(0)

	def updateAlbumData(self):
		# we can't display anything if there are no results
		if len(self.results) == 0:
			self.artistVariable.set("(None)")
			self.titleVariable.set("")
			self.scoreVariable.set("0")
			self.mbidSaveVariable.set("")
			self.tracksListbox.delete(0, END)
			self.mbTrackCountVariable.set(0)
			return

		release = self.results[self.resultsIndex].release

		# update colors to show how we found this result
		if self.resultsIndex == 0 and self.mbidResults == 1:
			# found by mbid
			self.discIdLabel["fg"] = "Black"
			self.cddbArtistLabel["fg"] = "Black"
			self.cddbTitleLabel["fg"] = "Black"
			self.mbidLabel["fg"] = "Red"
		elif self.resultsIndex > (self.mbidResults + self.discIdResults - 1):
			# found by cddb
			self.discIdLabel["fg"] = "Black"
			self.cddbArtistLabel["fg"] = "Red"
			self.cddbTitleLabel["fg"] = "Red"
			self.mbidLabel["fg"] = "Black"
		else:
			# discId
			self.discIdLabel["fg"] = "Red"
			self.cddbArtistLabel["fg"] = "Black"
			self.cddbTitleLabel["fg"] = "Black"
			self.mbidLabel["fg"] = "Black"

		# show the artist and title
		self.artistVariable.set(release.artist.name)
		self.titleVariable.set(release.title)
		self.scoreVariable.set(self.results[self.resultsIndex].score)
		self.mbidSaveVariable.set(release.id)
		if (release.asin != None):
			self.asinVariable.set(release.asin)
		else:
			self.asinVariable.set("(None)")

		# populate the tracks
		self.mbTrackCountVariable.set(len(release.tracks))
		self.tracksListbox.delete(0, END)
		singleArtist = release.isSingleArtistRelease()
		for trackIndex in range(0, len(release.tracks)):
			track = release.tracks[trackIndex]
			if singleArtist or track.artist == None:
				self.tracksListbox.insert(
					END, 
					"%02d -- %s" % (trackIndex + 1, track.title))
			else:
				self.tracksListbox.insert(
					END, 
					"%02d -- %s [%s]" % (trackIndex + 1, track.title, track.artist.name))

		# make the track numbers red if they don't match
		if self.mbTrackCountVariable.get() != self.trackCountVariable.get():
			self.trackCountLabel["fg"] = "Red"
			self.mbTrackCountLabel["fg"] = "Red"
		else:
			self.trackCountLabel["fg"] = "Black"
			self.mbTrackCountLabel["fg"] = "Black"


		# update the status bar
		self.statusBarVariable.set("result %d of %d (%d MusicBrainz ID, %d Disc ID, %d CDDB Names)" % 
			(self.resultsIndex + 1, 
			 len(self.results),
			 self.mbidResults,
			 self.discIdResults,
			 self.cddbResults))

	def save(self):
		saveMbid = self.mbidSaveVariable.get()
		if saveMbid != "":
			print "saving mbid: %s" % saveMbid
			print "to file: %s" % self.flacFilename
			self.flac.setMusicBrainzId(saveMbid)
			print "saved"
			self.statusBarVariable.set("Saved MusicBrainz ID")
		else:
			self.statusBarVariable.set("Can't save invalid MusicBrainz ID")

	def load(self):
		self.loadFlac(False)

	def prevFlac(self):
		if self.flacFilenamesIndex > 0:
			self.flacFilenamesIndex = self.flacFilenamesIndex - 1
			self.loadFlac()

	def nextFlac(self):
		if self.flacFilenamesIndex < (len(self.flacFilenames) - 1):
			self.flacFilenamesIndex = self.flacFilenamesIndex + 1
			self.loadFlac()

	def nextUntaggedFlac(self):
		print "finding next untagged flac"
		untagged = False
		while not untagged:
			if self.flacFilenamesIndex < (len(self.flacFilenames) - 1):
				self.flacFilenamesIndex = self.flacFilenamesIndex + 1
				self.loadFlac()
				mbid = self.flac.getMusicBrainzId()
				if mbid == None:
					untagged = True
			else:
				# end of loop
				untagged = True

	def prevResult(self):
		if self.resultsIndex > 0:
			self.resultsIndex = self.resultsIndex - 1
			self.updateAlbumData()

	def nextResult(self):
		if self.resultsIndex < (len(self.results) - 1):
			self.resultsIndex = self.resultsIndex + 1
			self.updateAlbumData()

	# this event is called when someone hits enter in the mbid entry box
	def newMbid(self, event):
		print "loading from musicbrainz id %s" % self.mbidSaveVariable.get()
		self.loadFlac(True, self.mbidSaveVariable.get())
		None

	# keypress events
	def prevFlacEvent(self, event):
		self.prevFlac()

	def nextFlacEvent(self, event):
		self.nextFlac()

	def nextUntaggedFlacEvent(self, event):
		self.nextUntaggedFlac()

	def prevResultEvent(self, event):
		self.prevResult()

	def nextResultEvent(self, event):
		self.nextResult()

	def quitEvent(self, event):
		self.quit()

	def saveEvent(self, event):
		self.save()

	def loadEvent(self, event):
		self.load()

class ReleaseResult():
	def __init__(self, release, score):
		self.release = release
		self.score = score


	#####################################################################
	#####################################################################
def findReleaseFromFlac(flacFilename):
	release = None
	cuesheet = readFlacCuesheet(sys.argv[1])
	if cuesheet:
		discId = flacCuesheetToDiscId(cuesheet)
		release = findInMusicBrainzByDiscId(discId)
	if release == None:
		print "No match for DiscId: %s" % discId
		[artist, title] = getArtistAndTitle(sys.argv[1])
		release = findInMusicBrainzByArtistAndTitle(artist, title)
	return release
	



#release = findReleaseFromFlac(sys.argv[1])
#printRelease(release)

#log = logging.getLogger("musicbrainz2.webservice.WebService")
#logging.basicConfig(level=logging.DEBUG)
#log.setLevel(logging.DEBUG)

if len(sys.argv) > 1:
	app = Application(sys.argv[1:])
else:
	app = Application(["*.flac"])
app.master.title("ripperchain tagger")
app.mainloop()

