from sugar.activity import activity
import logging

from CsoundServer import *
from CsoundEventSequencer import *
from CSDParser import *

import random
import sys, os
import gtk

import gobject

from KeyboardView import *


class CSEditorActivity(activity.Activity):
    orchestraDefault = """
    nchnls = 2

gisin   ftgen   1, 0, 4096, 10, 1

    instr       1
iPitch  =       cpspch(p4)

aAmp    line    ampdbfs(-9), p3, 0
a1      oscil   aAmp, iPitch, gisin

    outs        a1, a1
    endin
    """

    orchestraMIDIDefault = """


        nchnls = 2

giwave  ftgen   0, 0, 4096, 10, 1, 0.5, 0.25, 0.125
gisin   ftgen   0, 0, 4096, 10, 1

        instr   1
kpb         init 0

kFreq   cpsmidib
iVel    ampmidi ampdbfs(-12)
iRise   = 0.05
iDec    = 0.5
iAtDec  = 0.01

kLFOAmp midictrl    1
kLFOFreq midictrl    2

; scale the value from 0 to 1
kLFOAmp = kLFOAmp / 127


aLFO    oscil   kLFOAmp, kLFOFreq, gisin
aOsc    oscil   iVel, kFreq, giwave
aEnv    linenr  1.0, iRise, iDec, iAtDec

aOsc = aOsc * aEnv * (aLFO + 1.0)

        outs    aOsc, aOsc
        endin


        instr   2
iFreq   cpsmidib
iVel    ampmidi ampdbfs(-6)
iRise   = 0.05
iDec    = 0.5
iAtDec  = 0.01

aOsc    oscil   iVel, iFreq, gisin
aEnv    linenr 1.0, iRise, iDec, iAtDec

aOsc = aOsc * aEnv

        outs    aOsc, aOsc
        endin

    """

    scoreDefault = """
;i10 0 1 8.00
;i10 1 1 8.04
;i10 2 1 8.07

f0 3600
    """


    def destroy(self, data=None):
        if self.csound != None:
            self.csound.shutdownServer()        
        activity.Activity.destroy(self)

    def launchServer(self, sr):
        print "Launching Local Server"                
        server = CsoundServer()
        fd = server.launch(sampleRate = sr)
        
        if fd != None:
            gobject.io_add_watch(fd.fileno(), gobject.IO_IN, self.serverMessages, fd)                 
            print "Successfully Launched Server"
        else:
            print "Problem launching..."


    def __init__(self, handle):        
        activity.Activity.__init__(self, handle)

        self.csound = None
        self.openedCSD = None
        self.openedFilename = None
        self.paused = False
        self.performing = False
        self.csound = None
        
        self.MIDIChannel = 1
                
        self.set_title('Front End Editor')
                
        toolbox = activity.ActivityToolbox(self)
        self.set_toolbox(toolbox)
        toolbox.show()
                
        widgets = self.setupWidgets()
        widgets.show_all()
                
        self.set_canvas(widgets)
        
        
    def setupWidgets(self):
        self.layout = gtk.Layout()

        # Create a new notebook, place the position of the tabs
        notebook = gtk.Notebook()
        notebook.set_tab_pos(gtk.POS_LEFT)
        notebook.set_size_request(1150, 650)

        buffer = gtk.TextBuffer()
        buffer.set_text(self.orchestraMIDIDefault)
        self.orcTextView = gtk.TextView(buffer)
        
        self.orcScrolledWindow = gtk.ScrolledWindow()
        self.orcScrolledWindow.add_with_viewport(self.orcTextView)
        self.orcScrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        
        buffer = gtk.TextBuffer()
        buffer.set_text(self.scoreDefault)
        self.scoTextView = gtk.TextView(buffer)
        
        self.scoScrolledWindow = gtk.ScrolledWindow()
        self.scoScrolledWindow.add_with_viewport(self.scoTextView)
        self.scoScrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
            
        self.midiLayout = self.createMIDILayout()
        
        self.setupLayout = self.createSetupLayout()        
                
        self.messagesTextView = gtk.TextView()
        
        self.messageScrolledWindow = gtk.ScrolledWindow()
        self.messageScrolledWindow.add_with_viewport(self.messagesTextView)
        self.messageScrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)


        self.serverTextView = gtk.TextView()        
        self.serverScrolledWindow = gtk.ScrolledWindow()
        self.serverScrolledWindow.add_with_viewport(self.serverTextView)
        self.serverScrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
                                    
        notebook.append_page(self.orcScrolledWindow, gtk.Label("Orchestra"))
        notebook.append_page(self.scoScrolledWindow, gtk.Label("Score"))
        notebook.append_page(self.midiLayout, gtk.Label("MIDI"))
        notebook.append_page(self.setupLayout, gtk.Label("Setup"))
        notebook.append_page(self.messageScrolledWindow, gtk.Label("Messages"))
        notebook.append_page(self.serverScrolledWindow, gtk.Label("Server"))
                                              
        self.layout.put(notebook, 10, 80)
        self.layout.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#C0C0C0"))
        
        self.playButton = gtk.Button("play")
        self.playButton.connect("clicked", self.play)
        self.playButton.set_size_request(100, 50)
        self.layout.put(self.playButton, 10, 10)

        self.pauseButton = gtk.Button("pause")
        self.pauseButton.connect("clicked", self.pause)
        self.pauseButton.set_size_request(100, 50)
        self.layout.put(self.pauseButton, 110, 10)

        self.stopButton = gtk.Button("stop")
        self.stopButton.connect("clicked", self.stop)
        self.stopButton.set_size_request(100, 50)
        self.layout.put(self.stopButton, 210, 10)

        self.rewindButton = gtk.Button("rewind")
        self.rewindButton.connect("clicked", self.rewind)
        self.rewindButton.set_size_request(100, 50)
        self.layout.put(self.rewindButton, 310, 10)


        self.menu = gtk.combo_box_new_text()
        self.menu.set_size_request(100, 50)
        self.menu.append_text('File:')
        self.menu.append_text('New')
        self.menu.append_text('Open')
        self.menu.append_text('Save')
        self.menu.connect('changed', self.changedCombo)

        self.layout.put(self.menu, 510, 10)
                                

        label = gtk.Label("Server Address")
        self.layout.put(label, 750, 20)
                
        self.serverAddress = gtk.Entry()
        self.serverAddress.set_text("localhost")
        self.serverAddress.set_size_request(200, 40)
        self.layout.put(self.serverAddress, 925, 10)


        # line event
        self.lineEvent = gtk.Entry()
        self.lineEvent.set_text("i1 0 1 8.00")
        self.lineEvent.set_size_request(800, 40)
        self.layout.put(self.lineEvent, 240, 750)
        self.layout.put(gtk.Label("Line Event"), 100, 750)
        
        self.lineEventButton = gtk.Button("send")
        self.lineEventButton.connect("clicked", self.sendEvent)
        self.lineEventButton.set_size_request(100, 40)
        self.layout.put(self.lineEventButton, 1050, 750)

        return self.layout
        
    def initializeInterface(self):
        try:
            initFile = os.path.join(activity.get_bundle_path(), 'editor-init.xml')
            print "init=", initFile
            file = open(initFile)
            fileData = file.read()
            file.close()

            xml = SimpleXMLParser()
            xml.parse(fileData)

            editorTag = xml.rootTag().tagWithName('EditorInit')
            defaultsTag = editorTag.tagWithName('Defaults')
            
            orcTag = defaultsTag.tagWithName('NewOrchestra')
            scoTag = defaultsTag.tagWithName('NewScore')
            srTag = defaultsTag.tagWithName('SampleRate')
            krTag = defaultsTag.tagWithName('ControlRate')
            bsTag = defaultsTag.tagWithName('BufferSize')
            mlTag = defaultsTag.tagWithName('MessageLevel')
            showTablesTag = defaultsTag.tagWithName('ShowTables')
            logMsgsTag = defaultsTag.tagWithName('LogMessages')

            if orcTag != None:
                self.setOrchestra(orcTag.data())
            if scoTag != None:
                self.setScore(scoTag.data())
            if srTag != None:
                self.sampleRate.set_text(srTag.data())
            if krTag != None:
                self.controlRate.set_text(krTag.data())
            if bsTag != None:
                self.bufferSize.set_text(bsTag.data())
            if mlTag != None:
                self.messageLevel.set_text(mlTag.data())
            if showTablesTag != None:
                self.showTables.set_active(showTablesTag.data())
            if logMsgsTag != None:
                self.logMsgsTag.set_active(logMsgsTag.data())
            
        except Exception, msg:
            print "Exception loading editor-init.xml"
            print msg            

        self.openedFilename = None

    def changedCombo(self, widget, data = None):
        print "changedCombo"
        
        model = widget.get_model()
        index = widget.get_active()
        if index:
            selectedItem = model[index][0]

            if selectedItem == 'New':
                self.initializeInterface()
            elif selectedItem == 'Open':
                self.open()
            elif selectedItem == 'Save':
                self.save()
        
        self.menu.set_active(0)
        
    def sendEvent(self, widget, data = None):
        if self.performing == True:
            self.csound.playEventAsString(self.lineEvent.get_text())
        
    def fileOpenOkay(self, widget, data = None):
        #print "%s" % self.fileOpen.get_filename()
        csd = CSDParser()

        try:
            csd.open(self.fileOpen.get_filename())
            self.openedFilename = self.fileOpen.get_filename()
            
            # save a ref
            self.openedCSD = csd

            self.setOrchestra( csd.orchestra() )
            self.setScore( csd.score() )
            
        except Exception, msg:
            print "Exception: ", msg
            
        self.fileOpen.destroy()

    def fileSaveOkay(self, widget, data = None):
        
        filename = self.fileSave.get_filename()
        print "filename", filename
        
        #dialog = gtk.Dialog('Could not write to file', None)
        #button = gtk.Button('OK')
        #dialog.action_area.pack_start(button, True, True, 0)
        #button.show()
        
        if self.openedCSD != None:
            # replace the sections
            self.openedCSD.setOrchestra( self.orchestra() )
            self.openedCSD.setScore( self.score() )

            self.orcTextView.get_buffer().set_modified(False)
            self.scoTextView.get_buffer().set_modified(False)
                        
            self.openedCSD.write(filename)
        else:
            rootTag = XMLTag('CsoundSynthesizer')
            rootTag.addChildTag(XMLTag('CsInstruments', self.orchestra()))
            rootTag.addChildTag(XMLTag('CsScore', self.score()))
            
            s = SimpleXMLParser()
            str = s.serialize(rootTag)
            
            try:
                file = open(filename, "w")
                file.write(str)
                file.close()
            except Exception, msg:
                print "error", msg
                                    
        self.fileSave.destroy()        

    def midiOpenOkay(self, widget, data = None):
        self.midiFilename.set_text(self.midiFileOpen.get_filename())
        self.midiFileOpen.destroy()
        
    def open(self):
        self.fileOpen = gtk.FileSelection("Open File")
        self.fileOpen.set_size_request(900, 800)
        self.fileOpen.ok_button.connect("clicked", self.fileOpenOkay)
        self.fileOpen.cancel_button.connect("clicked", lambda w: self.fileOpen.destroy())
        self.fileOpen.set_filename(activity.get_bundle_path()+'/')
        self.fileOpen.show()

    def openMIDIFile(self, widget, data = None):
        self.midiFileOpen = gtk.FileSelection("Choose MIDI File")
        self.midiFileOpen.set_size_request(900, 800)
        self.midiFileOpen.ok_button.connect("clicked", self.midiOpenOkay)
        self.midiFileOpen.cancel_button.connect("clicked", lambda w: self.midiFileOpen.destroy())
        self.midiFileOpen.set_filename(activity.get_bundle_path()+'/')
        self.midiFileOpen.show()

    def save(self):
        self.fileSave = gtk.FileSelection("Save File")
        self.fileSave.set_size_request(900, 800)
        self.fileSave.ok_button.connect("clicked", self.fileSaveOkay)
        self.fileSave.cancel_button.connect("clicked", lambda w: self.fileSave.destroy())
        if self.openedFilename == None:
            self.fileSave.set_filename(activity.get_bundle_path()+'/')
        else:
            self.fileSave.set_filename(self.openedFilename)        
        self.fileSave.show()


    def play(self, widget, data = None):
        if self.paused == True:
            self.csound.perform()
            self.paused = False
        else:
        
            if self.performing == True:
                self.csound.shutdown()
                self.performing = False
                time.sleep(1)

            sampleRate = int(self.sampleRate.get_text())
            self.launchServer( sampleRate )
            
            # Create our instance        
            self.csound = Csound("Editor", self.serverAddress.get_text())
            #self.csound.setServerSampleRate( int(self.sampleRate.get_text()) )
            self.csound.setMessageCallback(self.csoundMessages)
            self.csound.setupOrchestraAsString(self.orchestra())
            self.csound.setupScoreAsString(self.score())
            self.csound.addCallback(self.performanceDone, "/notification/done_performing")
            
            midFilename = self.midiFilename.get_text()
            
            if len(midFilename) > 0:
                self.csound.setupMIDIFile(midFilename, False)
                    
                if self.terminateMIDI.get_active():
                    print "Terminating when done"
                    self.csound.setupTerminateWhenMIDIFileDone()
            
            self.csound.setupMessageLevel( int(self.messageLevel.get_text()) )
            self.csound.setSampleRate( sampleRate )
            self.csound.setControlRate( int(self.controlRate.get_text()) )            
            self.csound.setupRealtimeMIDI()
            self.csound.initialize()

            if self.csound.isInitialized(45) == 1:
                print "YES - its initialized"
                self.performing = True
                self.csound.perform()
                self.paused = False

                
            
    def performanceDone(self, message):
        print "done"
        #self.csound.shutdown()
        
    def pause(self, widget, data = None):
        self.paused = True
        self.csound.pause()

    def stop(self, widget, data = None):
        self.paused = True
        self.csound.stop()

    def rewind(self, widget, data = None):
        self.csound.rewind()
        
    def orchestra(self):
        buffer = self.orcTextView.get_buffer()
        startiter, enditer = buffer.get_bounds()
        
        text = buffer.get_text(startiter, enditer)

        return text

    def score(self):
        buffer = self.scoTextView.get_buffer()
        startiter, enditer = buffer.get_bounds()
        
        text = buffer.get_text(startiter, enditer)

        return text
    
    def setOrchestra(self, orc):
        buffer = self.orcTextView.get_buffer()
        buffer.set_text(orc)

    def setScore(self, sco):
        buffer = self.scoTextView.get_buffer()
        buffer.set_text(sco)
        
    def createMIDILayout(self):
        layout = gtk.Layout()
        layout.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#C0C0C0"))
            
        label = gtk.Label("MIDI Filename")
        layout.put(label, 10, 10)
    
        self.midiFilename = gtk.Entry()
        self.midiFilename.set_text("")
        self.midiFilename.set_size_request(600, 40)
        layout.put(self.midiFilename, 10, 40)

        openMid = gtk.Button("Choose")
        openMid.connect("clicked", self.openMIDIFile)
        layout.put(openMid, 620, 40)

        self.terminateMIDI = gtk.CheckButton("Terminate on end of file")
        self.terminateMIDI.set_active(True)
        layout.put(self.terminateMIDI, 10, 80)


        self.minPitchBend = -1
        self.maxPitchBend = 1
        self.pitchBend = 0

        self.minModWheel = 0
        self.maxModWheel = 127
        self.modWheel = 0

        self.minChannel = 1
        self.maxChannel = 16
        
        self.MIDIprogram = 1
        self.minProgram = 0
        self.maxProgram = 127
        
        adj = gtk.Adjustment(self.MIDIChannel, self.minChannel, self.maxChannel, 1.0, 1.0, 1.0)
        self.channelSpin = gtk.SpinButton(adj, 0, 0)
        self.channelSpin.connect("value_changed", self.channelChanged)
        self.channelSpin.set_size_request(100, 40)
        self.channelSpin.set_wrap(True)
        self.channelSpin.set_value(self.MIDIChannel)
        layout.put(gtk.Label("Channel"), 120, 155)
        layout.put(self.channelSpin, 10, 150)

        adj = gtk.Adjustment(self.MIDIprogram, self.minProgram, self.maxProgram, 1.0, 1.0, 1.0)
        self.programSpin = gtk.SpinButton(adj, 0, 0)
        self.programSpin.connect("value_changed", self.programChanged)
        self.programSpin.set_size_request(100, 40)
        self.programSpin.set_wrap(True)
        self.programSpin.set_value(self.MIDIprogram)

        layout.put(gtk.Label("Program"), 410, 155)
        layout.put(self.programSpin, 300, 150)


        
        # value, lower, upper, step_increment, page_increment, page_size
        self.pitchBendSlider = gtk.VScale(gtk.Adjustment(self.pitchBend, self.minPitchBend, self.maxPitchBend, 0.01, 0.01, 0.01))
        self.pitchBendSlider.connect("value_changed", self.pitchBendChanged, self.pitchBendSlider)
        self.pitchBendSlider.set_update_policy(gtk.UPDATE_CONTINUOUS)
        self.pitchBendSlider.set_size_request(60, 160)
        layout.put(self.pitchBendSlider, 10, 230)

        # value, lower, upper, step_increment, page_increment, page_size
        self.modWheelSlider = gtk.VScale(gtk.Adjustment(self.modWheel, self.minModWheel, self.maxModWheel, 0.01, 0.01, 0.01))
        self.modWheelSlider.connect("value_changed", self.modWheelChanged, self.modWheelSlider)
        self.modWheelSlider.set_update_policy(gtk.UPDATE_CONTINUOUS)
        self.modWheelSlider.set_size_request(60, 160)
        layout.put(self.modWheelSlider, 60, 230)
            
        self.keyboardView = KeyboardView()
        self.keyboardView.setKeyRange(60 - 24, 48)                 
        self.keyboardView.set_size_request(800, 160)
        self.keyboardView.setNoteOnCallback(self.noteOn)
        self.keyboardView.setNoteOffCallback(self.noteOff)
        layout.put(self.keyboardView, 120, 230)

        self.control = 0
        self.minControl = 0
        self.maxControl = 127
        

        adj = gtk.Adjustment(0, self.minControl, self.maxControl, 1.0, 1.0, 1.0)
        self.control1Spin = gtk.SpinButton(adj, 0, 0)
        #self.control1Spin.connect("value_changed", self.controlChanged, 1)
        self.control1Spin.set_size_request(100, 40)
        self.control1Spin.set_wrap(True)
        self.control1Spin.set_value(1)
        layout.put(gtk.Label("Controller"), 10, 460)
        layout.put(self.control1Spin, 10, 490)

        # value, lower, upper, step_increment, page_increment, page_size
        self.control1Slider = gtk.HScale(gtk.Adjustment(self.control, self.minControl, self.maxControl, 1, 1, 1))
        self.control1Slider.connect("value_changed", self.controlValueChanged, 0)
        self.control1Slider.set_update_policy(gtk.UPDATE_CONTINUOUS)
        self.control1Slider.set_value_pos(gtk.POS_BOTTOM)
        self.control1Slider.set_size_request(250, 60)
        self.control1Slider.set_digits(0)
        layout.put(self.control1Slider, 120, 490)


        adj = gtk.Adjustment(0, self.minControl, self.maxControl, 1.0, 1.0, 1.0)
        self.control2Spin = gtk.SpinButton(adj, 0, 0)
        self.control2Spin.set_size_request(100, 40)
        self.control2Spin.set_wrap(True)
        self.control2Spin.set_value(2)
        layout.put(gtk.Label("Controller"), 410, 460)
        layout.put(self.control2Spin, 410, 490)

        # value, lower, upper, step_increment, page_increment, page_size
        self.control2Slider = gtk.HScale(gtk.Adjustment(self.control, self.minControl, self.maxControl, 1, 1, 1))
        self.control2Slider.connect("value_changed", self.controlValueChanged, 1)
        self.control2Slider.set_update_policy(gtk.UPDATE_CONTINUOUS)
        self.control2Slider.set_value_pos(gtk.POS_BOTTOM)
        self.control2Slider.set_size_request(250, 60)
        self.control2Slider.set_digits(0)
        layout.put(self.control2Slider, 520, 490)
        
        return layout


    def controlValueChanged(self, widget, data = None):
        if self.performing == True:
            if data == 0:
                self.csound.sendMIDIControl(int(self.control1Spin.get_value()), int(widget.get_value()), self.MIDIChannel)
            elif data == 1:
                self.csound.sendMIDIControl(int(self.control2Spin.get_value()), int(widget.get_value()), self.MIDIChannel)
                
    def programChanged(self, widget, data = None):
        if self.performing == True:
            self.MIDIprogram = int(widget.get_value())
            self.csound.sendMIDIProgramChange(self.MIDIprogram, self.MIDIChannel)
        
    def channelChanged(self, widget, data = None):
        self.MIDIChannel = int( widget.get_value() )

    def modWheelChanged(self, widget, data = None):
        if self.performing == True:
            self.csound.sendMIDIControl(1, int(widget.get_value()), self.MIDIChannel)

    def pitchBendChanged(self, widget, data = None):
        if self.performing == True:
            self.csound.sendMIDIPitchBend(int(widget.get_value()), self.MIDIChannel)
            
    def noteOn(self, note, vel, userData = None):
        if self.performing == True:
            self.csound.playMIDINoteOn(note, vel, self.MIDIChannel)

    def noteOff(self, note, vel, userData = None):
        if self.performing == True:
            self.csound.playMIDINoteOff(note, self.MIDIChannel)
                
    def createSetupLayout(self):
        layout = gtk.Layout()
        layout.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#C0C0C0"))
                    
        label = gtk.Label("Sample Rate")

        layout.put(label, 10, 15)
    
        self.sampleRate = gtk.Entry()
        self.sampleRate.set_text("16000")
        layout.put(self.sampleRate, 200, 10)

        label = gtk.Label("Control Rate")
        layout.put(label, 10, 75)
    
        self.controlRate = gtk.Entry()
        self.controlRate.set_text("160")
        layout.put(self.controlRate, 200, 70)

        label = gtk.Label("Buffer Size")
        layout.put(label, 10, 135)
    
        self.bufferSize = gtk.Entry()
        self.bufferSize.set_text("512")
        layout.put(self.bufferSize, 200, 130)

        label = gtk.Label("Message Level")
        layout.put(label, 10, 195)
    
        self.messageLevel = gtk.Entry()
        self.messageLevel.set_text("16")        
        layout.put(self.messageLevel, 200, 190)

        self.showTables = gtk.CheckButton("Show Tables")
        layout.put(self.showTables, 120, 250)

        self.logMessages = gtk.CheckButton("Log Messages")
        self.logMessages.set_active(True)
        layout.put(self.logMessages, 120, 300)

                
        return layout


    def serverMessages(self, source, condition, userData = None):
        if self.logMessages.get_active(): 
            buffer = self.serverTextView.get_buffer()
            enditer = buffer.get_end_iter()
            
            buffer.insert(enditer, userData.readline())
            enditer = buffer.get_end_iter()
            
            self.serverTextView.scroll_to_iter(enditer, 0.0)
                        
        return True

    def csoundMessages(self, message):
        if self.logMessages.get_active(): 
            buffer = self.messagesTextView.get_buffer()
            enditer = buffer.get_end_iter()
            
            buffer.insert(enditer, message.argument(0))
            enditer = buffer.get_end_iter()
            
            self.messagesTextView.scroll_to_iter(enditer, 0.0)
            