import pygtk
pygtk.require('2.0')
import gtk
import cairo
import colors
from math import ceil, floor

from map import look_up_note, qwerty_map, draw_map

MASKS = gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK
MASKS2 = gtk.gdk.ENTER_NOTIFY_MASK | gtk.gdk.LEAVE_NOTIFY_MASK
MASKS3 = gtk.gdk.KEY_PRESS_MASK | gtk.gdk.KEY_RELEASE_MASK

class Keyboard(gtk.DrawingArea):
	def __init__(self, width=200, height=100, num_notes=24, 
			note_type="pch", bg_color=colors.aqua):
		self.note_type = note_type.lower()
		self._bg_color = bg_color
		self.setNoteOnCallback(None)
		self.setNoteOffCallback(None)
		self.num_notes = num_notes
		self.width = width
		self.height = int(height)
		self.height2 = int(self.height*3./5)
		self.spacing_x = int(float(self.width)/self.num_notes)
		self.hover_note = -1
		self.button_playing = -1
		gtk.DrawingArea.__init__(self)
		self.set_size_request(width, height)
		self.set_events(MASKS | MASKS2 | MASKS3)
		self.notes_playing = []
		#self.set_flags(gtk.CAN_FOCUS)
		#self.grab_focus()
		self.connect("expose-event", self.onExpose)
		self.connect("key-press-event", self.onKeyPress)
		self.connect("key-release-event", self.onKeyRelease)
		
	def onExpose(self, widget, event):
		is_playing = False
		x = event.area.x
		y = event.area.y
		width = event.area.width
		height = event.area.height
		context = widget.window.cairo_create()
		context.rectangle(x, y, width, height)
		context.clip()
				
		for i in range(0, self.num_notes):
			
			chrom = i%12
			
			if (chrom == 1) or (chrom == 3) or (chrom == 6) or  (chrom == 8) or (chrom == 10):
				context.set_source_rgb(colors.white[0], colors.white[1], colors.white[2])
				context.rectangle(i*self.spacing_x, self.height2, self.spacing_x, self.height-self.height2)
				context.fill()
			
			if (chrom == 1) or (chrom == 3) or (chrom == 6) or  (chrom == 8) or (chrom == 10):
				context.set_source_rgb(colors.black[0], colors.black[1], colors.black[2])
			else:
				context.set_source_rgb(colors.white[0], colors.white[1], colors.white[2])
			
			for k in range(0, len(draw_map)):
					if i == int(draw_map[k]):
						break
			for j in range(0, len(self.notes_playing)):
				if self.note_type == "midi":
						pch = float(look_up_note[k])
						note = int(round(48 + 12*(int(pch) - 8) + (pch - int(pch))*100))
				else:
						note = look_up_note[k]
				if self.notes_playing[j] == note:
					context.set_source_rgb(self._bg_color[0],
						self._bg_color[1],
						self._bg_color[2])
					is_playing = True
					break

			if is_playing == True:
				if (chrom == 1) or (chrom == 3) or (chrom == 6) or  (chrom == 8) or (chrom == 10):
					context.rectangle(int(draw_map[k])*self.spacing_x, 0, self.spacing_x, self.height2)
					context.fill()
				else:
					context.rectangle(int(draw_map[k])*self.spacing_x, 0, self.spacing_x, self.height)
					context.fill()
			else:
				if (chrom == 1) or (chrom == 3) or (chrom == 6) or  (chrom == 8) or (chrom == 10):
					context.set_source_rgb(colors.black[0], colors.black[1], colors.black[2])
					context.rectangle(i*self.spacing_x, 0, self.spacing_x, self.height2)
					context.fill()
				else:
					context.set_source_rgb(colors.white[0], colors.white[1], colors.white[2])
					context.rectangle(i*self.spacing_x, 0, self.spacing_x, self.height)
					context.fill()
			
			context.set_line_width(2.0)
			context.set_source_rgb(colors.black[0], colors.black[1], colors.black[2])
			if (chrom == 1) or (chrom == 6):
				context.move_to(i*self.spacing_x + int(self.spacing_x*(3./4)), self.height2)
				context.line_to(i*self.spacing_x + int(self.spacing_x*(3./4)), self.height)
				context.stroke()
			elif (chrom == 3) or (chrom == 10):
				context.move_to(i*self.spacing_x + int(self.spacing_x*(1./4)), self.height2)
				context.line_to(i*self.spacing_x + int(self.spacing_x*(1./4)), self.height)
				context.stroke()
			elif (chrom == 8):
				context.move_to(i*self.spacing_x + self.spacing_x/2, self.height2)
				context.line_to(i*self.spacing_x + self.spacing_x/2, self.height)
				context.stroke()
			elif (chrom == 0) or (chrom == 5):
				context.move_to((i)*self.spacing_x, 0)
				context.line_to((i)*self.spacing_x,  self.height)
				context.stroke()
			elif (chrom == 11):
				context.move_to((i+1)*self.spacing_x, 0)
				context.line_to((i+1)*self.spacing_x,  self.height)
				context.stroke()
		

	def onMotionNotify(self, widget, event):
		hover_old = self.hover_note
		hover = int((event.x) - ( int(event.x) % self.spacing_x ))
		
		if self.hover_note == hover:
			should_redraw = False
		else:
			self.hover_note = hover
			should_redraw = True
	
		
		if should_redraw == True:
			if (self.notes_playing != -1):
				if hover_old < self.hover_note:
					self.queue_draw_area(hover_old, 0, (self.hover_note-hover_old) + self.spacing_x, self.height)
				else:
					self.queue_draw_area(self.hover_note, 0, (hover_old-self.hover_note) + self.spacing_x, self.height)

	
	def onKeyPress(self, widget, event):
		key = gtk.gdk.keyval_name(event.keyval)
		for i in range(0, len(qwerty_map)):
			if key == qwerty_map[i]:
				self.playNote(i)
				break
		
	
	def onKeyRelease(self, widget, event):
		key = gtk.gdk.keyval_name(event.keyval)
		for i in range(0, len(qwerty_map)):
			if key == qwerty_map[i]:
				self.stopNote(i)
				break
	
	def playNote(self, i):
		is_currently_playing = False
		
		if self.note_type == "midi":
			pch = float(look_up_note[i])
			note = int(round(48 + 12*(int(pch) - 8) + (pch - int(pch))*100))
		else:
			note = look_up_note[i]
		for j in range(0, len(self.notes_playing)):
			if self.notes_playing[j] == note:
				is_currently_playing = True
				break
		if is_currently_playing == False:
			#print "play note", note
			self.notes_playing.append(note)
			if (self.note_on_callback != None):
				self.note_on_callback(note, self.note_on_user_data)
			self.queue_draw_area(int(draw_map[i])*self.spacing_x, 0, self.spacing_x, self.height)
	
	def setNoteOnCallback(self, callback, data=None):
		self.note_on_callback = callback
		self.note_on_user_data = data
		
	def setNoteOffCallback(self, callback, data=None):
		self.note_off_callback = callback
		self.note_off_user_data = data
		
	def stopNote(self, i):
		if self.note_type == "midi":
				pch = float(look_up_note[i])
				note = int(round(48 + 12*(int(pch) - 8) + (pch - int(pch))*100))
		else:
				note = look_up_note[i]
		#print "stop note", note
		self.notes_playing.remove(note)
		if (self.note_on_callback != None):
			self.note_off_callback(note, self.note_off_user_data)
		self.queue_draw_area(int(draw_map[i])*self.spacing_x, 0, self.spacing_x, self.height)
