#!/usr/bin/python

import pygtk
import gtk
import gtk.glade
from Agent import *
from midiTools import MidiScore
from visual import vector
from generators import *

#setup a dictionary of user input values with some defaults
values = {'NoteDuration':[0.25],'TimeBetweenNotes':[0.025],'InstrumentOne':[0],'InstrumentTwo':[50],
'SigmaEntry':[1],'REntry':[1],'BEntry':[1],'CouplingEntry':[1],'AgentOneIC':[1,2,3],
'AgentTwoIC':[4,5,6],'DtEntry':[0.005],'OrbitLengthEntry':[3000],
'FilenameEntry':"hah.mid"}

#defaults have a different dicitonary format because of the way readValues and
#setDefaultValues work
defaults1 = {'NoteDuration':"0.36,0.12,0.12,0.24,0.24",'TimeBetweenNotes':"0.125,0.125,0.25,0.375,0.526",
'InstrumentOne':"0",'InstrumentTwo':"0",'SigmaEntry':"10",'REntry':"28",
'BEntry':"-2.5",'CouplingEntry':"0.9",'AgentOneIC':"10,24,33",
'AgentTwoIC':"1,1,1",'DtEntry':"0.005",'OrbitLengthEntry':"3000",
'FilenameEntry':"hah.mid"}

#main app class
class MainApp:

	def __init__(self):
		#load the gui xml file
		self.gladeFile = "mainGui.glade"
		self.wTree = gtk.glade.XML(self.gladeFile)
		#set up a reference to main window
		self.window = self.wTree.get_widget("MainWindow")
		#set up a reference to generate button
		self.genButton = self.wTree.get_widget("Generate")
		#set up a reference to load defaults button
		self.loadDefaults = self.wTree.get_widget("LoadDefault1")
		#set up a reference to progress bar
		self.progress = self.wTree.get_widget("GenerateProgress")
		#connect the event handlers
		self.loadDefaults.connect("clicked",self.setDefaultValues,defaults1)
		self.genButton.connect("clicked",self.generateAndSave)
		self.window.connect("destroy",gtk.main_quit)
		#show everything
		self.window.show_all()

	#function for reading the values that the user inputs
	def readValues(self):
		for key in values:
			if key != "FilenameEntry":
				values[key] = map(float,self.wTree.get_widget(key).get_text().split(","))
			else:
				values[key] = self.wTree.get_widget(key).get_text()
	
	#function for setting default values
	def setDefaultValues(self,widget,someDictionary):
		for key in someDictionary:
			self.wTree.get_widget(key).set_text(someDictionary[key])

	#function that does the output work
	#this code can be simplified to a great extend because there is a lot of redundancy
	def generateAndSave(self,widget):
		self.progress.set_text("")
		#read the values
		self.readValues()
		#setup a lorenz field. it's imporant to remember that values[key] is a list
		lorenz = lorenzField(values['SigmaEntry'][0],values['REntry'][0],values['BEntry'][0])
		#create the integrator
		integrator = NewEulerIntegrator(values['CouplingEntry'][0])
		#create agent 1 and a list to hold its orbit
		orbit1 = []
		orbiter1 = Agent(vector(values['AgentOneIC']),lorenz,integrator)
		#create agent 2 and a list to hold its orbit
		orbit2 = []
		orbiter2 = Agent(vector(values['AgentTwoIC']),lorenz,integrator)
		#create another orbit list
		orbit3 = []
		#now setup up the neighbor relations so the integrator can take account of the neighbor
		orbiter1.neighbor = orbiter2
		orbiter2.neighbor = orbiter1
		#this is where we accumulate the values into the lists
		for i in range(int(values['OrbitLengthEntry'][0])):
			map(lambda x:x.move(values['DtEntry'][0]),[orbiter1,orbiter2])
			map(lambda x:x.update(),[orbiter1,orbiter2])
			orbit1.append(abs(orbiter1.pos))
			orbit2.append(abs(orbiter2.pos))
			orbit3.append(abs(orbiter2.pos))
			self.progress.set_fraction(float(i)/values['OrbitLengthEntry'][0])
			gtk.main_iteration()
		#turn the orbit values to integers and mod out by 256 if needed because otherwise
		#midiScore gives an error message.
		orbit1 = map(lambda x: int(((x*10000)%100)%256)+0,orbit1)
		orbit2 = map(lambda x: int(((x*100)%100)%256)+0,orbit2)
		orbit3 = map(lambda x: int(x%100),orbit3)
		#setup the timer and note durations (both are generators)
		timer = timeGen()
		durations = noteDuration(values['NoteDuration'])
		#create the empty tracks and set volume and pan
		volume = 90
		pan = 60
		trackA = []
		trackB = []
		trackC = []
		#need to set time beforehand because of how python coroutines work
		time = timer.next()
		#iterate over orbit1 zipped with its indices and create the tracks
		for i in zip(orbit1,range(len(orbit1))):
			#choose a random number from [1..4] every so often
			if i[1]%100 == 0:
				blocks = choice1to4()
			#we only choose a different duration every so often
			#to give the music a bit more structure
			if i[1]%30 == 0:
				duration = durations.next()
			#collect the required number of notes from notes2
			notesFrom2 = orbit2[:blocks]
			#recycle the list so we don't run out of notes from notes2
			orbit2 = orbit2[blocks:]+orbit2[:blocks]
			#now we call splitter with duration,blocks and time, and
			#get back a list of (time,duration) pairs
			notesFrom2TandD = splitter(duration,blocks,time)
			#append the note to trackA in the required form
			trackA.append((time,duration,volume,i[0],pan))
			#append a note to trackC
			trackC.append((time,duration,volume,orbit3[i[1]],pan))
			#zip together notesFrom2 and notesFrom2TandD
			zipped = zip(notesFrom2,notesFrom2TandD)
			#iterate over zipped and put everything in the right form
			for j in zipped: #j is a tuple, j[0] is the note and j[1] is (time,duration)
				trackB.append((j[1][0],j[1][1],volume,j[0],pan))
			#update the new start time by sending in duration
			time = timer.send(duration)
			self.progress.pulse()
			gtk.main_iteration()
		#turn trackA and trackB to a tuple simply because 
		#this is the format for midiTools module
		trackA = tuple(trackA)
		trackB = tuple(trackB)
		trackC = (('trackC',int(values['InstrumentOne'][0]),None,tuple(trackC)),)
		#join the tracks into a midiScore object
		tracks = (('trackA',int(values['InstrumentOne'][0]),None,trackA),
				('trackB',int(values['InstrumentTwo'][0]),None,trackB))
		#create the score for midi playback
		score = MidiScore(tracks)
		#create midi files for the componenet tracks
		for i in tracks:
			s = MidiScore((i,))
			s.write(values['FilenameEntry'][0:-3]+i[0])
		#finally write the file
		score.write(values['FilenameEntry'])
		s = MidiScore(trackC)
		s.write(values['FilenameEntry'][0:-3]+'trackC')
		#print out done on progress bar
		self.progress.set_fraction(0)
		self.progress.set_text("Done")
		f = open('orbit1','w')
		p = 0
		for i in orbit1:
			if p%10 == 0:
				f.write(str(i)+'\n')
			else:
				f.write(str(i)+',')
			p += 1
		f.close()
app = MainApp()
gtk.main()
