library Muscii
	import SoundControl

	public struct Note
		string path
		integer volume
		real pitch
		Note previous
		Note next

		method copy takes nothing returns Note
			return Note.create(this.path,this.volume,this.pitch)
		end

		method play takes nothing returns nothing
			SoundControl.play(this.path,this.pitch,this.volume)
		end

		static method create takes string path, integer volume, real pitch returns Note
			Note t = new Note()
			t.path = path
			t.volume = volume
			t.pitch = pitch
			t.previous = null
			t.next = null
			return t
		end
	end



	public struct Chord
		Note first
		Note last
		Chord previous
		Chord next

		method copy takes nothing returns Chord
			Note iterator = this.first
			Chord new = Chord.create(iterator)
			loop
				iterator = iterator.next
				exitwhen iterator == null
				new.addNote(iterator)
			end
			return new
		end

		method play takes nothing returns nothing
			Note iterator = this.first
			loop
				exitwhen iterator == null
				iterator.play()
				iterator = iterator.next
			end
		end

		method addNote takes Note n returns Chord
			Note copy = n.copy()
			Note temp = this.last
			temp.next = copy
			copy.previous = temp
			return this
		end

		static method create takes Note n returns Chord
			Note q = Note.create(n.path,n.volume,n.pitch)
			Chord c = new Chord()
			c.previous = null
			c.next = null
			c.first = q
			c.last = q
			return c
		end
	end

	public struct Muscii
		private static Muscii array db
		private static integer dbIndex = -1
		Chord first
		Chord last
		Chord iterator
		real tempo //in whole notes per minute
		timer clock
		integer count
		integer queueCount
		MyCallBackFunc cbf
		Muscii queued

		static method interrupt takes nothing returns nothing
			integer index = 0
			Muscii temp
			loop
				exitwhen index>dbIndex
				temp = db[index]
				PauseTimer(temp.clock)
				index = index + 1
			end
		end

		method callback takes MyCallBackFunc func returns Muscii
			this.cbf = func
			return this
		end

		method copy takes nothing returns Muscii
			Chord iterator = this.first
			Muscii new = Muscii.create(iterator,this.tempo)
			loop
				iterator = iterator.next
				exitwhen iterator == null
				new.addChord(iterator.copy())
			end
			return new
		end

		method play takes nothing returns Muscii
			playCounted(-1)
			return this // for chaining
		end

		method playCounted takes integer count returns Muscii
			this.count = count
			TimerStart(this.clock,1. / (this.tempo / 60.),true, function p)
			return this
		end

		method enqueue takes Muscii next, integer queueCount returns Muscii
			// if the target Muscii is already queuing something, it is in use - we
			// therefore copy it.
			if next.queued == null then
				this.queued = next
			else
				this.queued = next.copy()
			end
			this.queueCount = queueCount
			return this.queued
		end

		method stop takes nothing returns Muscii
			PauseTimer(this.clock)
			this.iterator = this.first
			return this
		end

		method addChord takes Chord c returns Muscii
			Chord q = this.last
			Chord copy = c.copy()
			q.next = copy
			this.last = copy
			copy.previous = q
			return this
		end

		static method create takes Chord c, real tempo returns Muscii
			Muscii m = new Muscii()
			Chord cop = c.copy()
			m.first = cop
			m.last = cop
			m.iterator = cop
			m.tempo = tempo
			m.clock = CreateTimer()
			m.queued = null
			m.cbf = null
			m.clock.setData(m castTo int)
			dbIndex = dbIndex + 1
			db[dbIndex] = m
			return m
		end

		private static method p takes nothing returns nothing
			Muscii m = GetExpiredTimer().getData() castTo Muscii
			Muscii next
			Chord now = m.iterator
			now.play()
			if now.next != null then
				m.iterator = now.next
			else
				m.iterator = m.first
				if m.count>-1 then
					m.count=m.count-1
					if m.count==-1 then
						m.stop()
						next = m.queued
						if next != null then
							next.playCounted(m.queueCount)
						end
						m.queued = null
						if m.cbf != null then
							m.cbf.doCallback()
							m.cbf = null
						end
					end
				end
			end
		end
	end

	interface MyCallBackFunc
	   function doCallback() returns nothing
	   end
	end

	// usage example
	//init
	//	Muscii m = new Muscii()
	//	m.callback(() -> print("hello"))
	//end

endlibrary
