###############################################################
# Name:			 Renderer.py
# Purpose:	 Render a song on a dc
# Author:		 Luca Allulli (webmaster@roma21.it)
# Created:	 2009-02-21
# Copyright: Luca Allulli (https://www.skeed.it/songpress)
# License:	 GNU GPL v2
##############################################################

import wx

from .SongDecorator import *
from .SongTokenizer import *
from .SongFormat import *
from .SongBoxes import *
from .Transpose import translateChord, autodetectNotation
from .EditDistance import minEditDist


class BreakException(Exception):
	pass


class Renderer(object):
	def __init__(self, sf, sd=SongDecorator(), notations=[]):
		object.__init__(self)
		self.text = ""
		self.sd = sd
		self.dc = None
		# SongFormat
		self.starting_sf = sf
		self.sf = sf
		self.format = None
		self.currentBlock = None
		self.currentLine = None
		self.song = None
		self.notation = None
		self.notations = notations
		self.chordPatterns = []

	def BeginBlock(self, type, label=None):
		self.EndBlock()
		if type == SongBlock.verse:
			self.song.verseCount += 1
			if label is None:
				self.song.labelCount += 1
			self.sf.AddVerse()
			self.format = self.sf.verse[-1]
		elif type == SongBlock.chorus:
			self.format = self.sf.chorus
			self.song.chorusCount += 1
		else:
			self.format = self.sf.title
		self.currentBlock = SongBlock(type, self.format)
		self.currentBlock.label = label
		self.currentBlock.verseNumber = self.song.verseCount
		self.currentBlock.verseLabelNumber = self.song.labelCount

	def EndBlock(self):
		if self.currentBlock is not None:
			self.EndLine()
			if self.sf.showChords == 1:
				current = self.currentBlock.chords
				found = False
				for p in self.chordPatterns:
					led = len(p)
					med = minEditDist(p, current)
					if med < led and med < 4:
						found = True
						break
				if not found:
					self.chordPatterns.append(current)
				else:
					self.currentBlock.RemoveChordBoxes()
			self.song.AddBox(self.currentBlock)
			self.currentBlock = None

	def BeginVerse(self, label=None):
		if self.currentBlock == None:
			self.BeginBlock(SongBlock.verse, label)
			self.label = label

	def BeginChorus(self, label=None):
		self.BeginBlock(SongBlock.chorus, label)

	def ChorusVSkip(self):
		self.EndLine()
		self.BeginLine()
		self.EndLine()

	def AddText(self, text, type=SongText.text):
		if(
			text.strip() != ''
			and type != SongText.title
			and type != SongText.subtitle
			and type != SongText.comment
			and self.currentBlock is not None
			and self.currentBlock.type == SongBlock.title
		):
			self.EndBlock()
		self.BeginVerse()
		self.BeginLine()
		if type == SongText.comment:
			text = "(" + text + ")"
			format = self.format.comment
		elif type == SongText.chord:
			format = self.format.chord
			if self.sf.showChords == 1:
				self.currentBlock.chords.append(translateChord(text, self.notation, self.notation))
		else:
			format = self.format
		t = SongText(text, format.wxFont, type, format.color)
		if not type == SongText.chord or self.sf.showChords > 0:
			self.currentLine.AddBox(t)

	def AddTitle(self, title):
		if self.currentBlock is None or self.currentBlock.type != SongBlock.title:
			self.BeginBlock(SongBlock.title)
		self.format = self.sf.title
		self.AddText(title, SongText.title)

	def AddSubTitle(self, title):
		if self.currentBlock is None or self.currentBlock.type != SongBlock.title:
			self.BeginBlock(SongBlock.title)
		self.format = self.sf.subtitle
		self.AddText(title, SongText.subtitle)

	def BeginLine(self):
		if self.currentLine == None:
			if self.song.drawWholeSong or (self.fromLine <= self.lineCount and self.lineCount <= self.toLine):
				self.currentBlock.drawBlock = True
			self.currentLine = SongLine()

	def EndLine(self):
		if self.currentLine is not None:
			self.currentBlock.AddBox(self.currentLine)
			self.currentLine = None

	def GetAttribute(self):
		try:
			tok = self.tkz.next()
			if tok.token != SongTokenizer.colonToken:
				self.tkz.Repeat()
				return None
			tok = self.tkz.next()
			if tok.token != SongTokenizer.attrToken:
				self.tkz.Repeat()
				return ''
			return tok.content
		except StopIteration:
			pass
		return None

	def GetState(self):
		return None if self.currentBlock == None else self.currentBlock.type

	def Render(self, text, dc, fromLine = -1, toLine = -1):
		self.text = text
		self.dc = dc
		self.verseNumber = 0
		self.format = self.starting_sf
		self.currentLine = None
		self.currentBlock = None
		self.song = SongSong(self.sf)
		self.song.drawWholeSong = fromLine == -1
		self.lineCount = -1
		self.fromLine = fromLine
		self.toLine = toLine
		self.sf = SongFormat(self.starting_sf)
		if self.sf.showChords == 1:
			self.notation = autodetectNotation(text, self.notations)
			self.chordPatterns = []

		for l in self.text.splitlines():
			self.lineCount += 1
			state = self.GetState()
			self.tkz = SongTokenizer(l)
			empty = True
			for tok in self.tkz:
				state = self.GetState()
				empty = False
				t = tok.token
				if t == SongTokenizer.normalToken:
					self.AddText(tok.content)
				elif t == SongTokenizer.chordToken:
					self.AddText(tok.content[1:], SongText.chord)
				elif t == SongTokenizer.commandToken:
					cmd = tok.content.lower()
					if cmd == 'soc' or cmd == 'start_of_chorus':
						a = self.GetAttribute()
						self.BeginChorus(a)
					elif (cmd == 'eoc' or cmd == 'end_of_chorus') and state == SongBlock.chorus:
						self.EndBlock()
					elif cmd == 'c' or cmd == 'comment':
						a = self.GetAttribute()
						if a is not None:
							self.AddText(a, SongText.comment)
					elif cmd == 't' or cmd == 'title':
						a = self.GetAttribute()
						if a is not None:
							self.AddTitle(a)
					elif cmd == 'st' or cmd == 'subtitle':
						a = self.GetAttribute()
						if a is not None:
							self.AddSubTitle(a)
					elif cmd == 'verse':
						self.BeginVerse(self.GetAttribute())
					elif cmd == 'textsize':
						try:
							a = self.GetAttribute()
							if a is None:
								size = self.starting_sf.size
							else:
								a = a.strip()
								try:
									if a.endswith("%"):
										perc = int(a[:-1])
										size = self.format.size * perc / 100
									else:
										size = int(a)
								except TypeError:
									raise BreakException()
								except ValueError:
									raise BreakException()
							self.format = ParagraphFormat(self.format)
							self.format.size = size
							self.sf.size = size
							self.sf.chorus.size = size
							self.sf.title.size = size
							self.sf.subtitle.size = size
						except BreakException:
							pass
					elif cmd == 'textfont':
						try:
							face = self.GetAttribute()
							if face is None:
								face = self.starting_sf.face
							else:
								face = face.strip()
							self.format = ParagraphFormat(self.format)
							self.format.face = face
							self.sf.face = face
							self.sf.chorus.face = face
							self.sf.title.face = face
							self.sf.subtitle.face = face
						except BreakException:
							pass
					elif cmd == 'textcolour':
						try:
							color = self.GetAttribute()
							if color is None:
								color = self.starting_sf.color
							else:
								color = color.strip()
							self.format = ParagraphFormat(self.format)
							self.format.color = color
							self.sf.color = color
							self.sf.chorus.color = color
							self.sf.title.color = color
							self.sf.subtitle.color = color
						except BreakException:
							pass
					elif cmd == 'chordsize':
						try:
							a = self.GetAttribute()
							if a is None:
								size = self.starting_sf.chord.size
							else:
								a = a.strip()
								try:
									if a.endswith("%"):
										perc = int(a[:-1])
										size = self.format.chord.size * perc / 100
									else:
										size = int(a)
								except TypeError:
									raise BreakException()
								except ValueError:
									raise BreakException()
							self.format = ParagraphFormat(self.format)
							self.format.chord.size = size
							self.sf.chord.size = size
							self.sf.chorus.chord.size = size
						except BreakException:
							pass
					elif cmd == 'chordfont':
						try:
							face = self.GetAttribute()
							if face is None:
								face = self.starting_sf.chord.face
							else:
								face = face.strip()
							self.format = ParagraphFormat(self.format)
							self.format.chord.face = face
							self.sf.chord.face = face
							self.sf.chorus.chord.face = face
						except BreakException:
							pass
					elif cmd == 'chordcolour':
						try:
							color = self.GetAttribute()
							if color is None:
								color = self.starting_sf.chord.color
							else:
								color = color.strip()
							self.format = ParagraphFormat(self.format)
							self.format.chord.color = color
							self.sf.chord.color = color
							self.sf.chorus.chord.color = color
						except BreakException:
							pass

			self.EndLine()
			if empty:
				if state in {SongBlock.verse, SongBlock.title}:
					self.EndBlock()
				elif state == SongBlock.chorus:
					self.ChorusVSkip()
		self.EndBlock()
		w, h = self.sd.Draw(self.song, dc)
		self.dc = None
		return w, h

	def SetDecorator(self, sd):
		self.sd = sd
