#   Copyright (C) 2002-2003 Yannick Gingras <ygingras@ygingras.net>
#   Copyright (C) 2002-2003 Vincent Barbin <vbarbin@openbeatbox.org>

#   This file is part of Open Beat Box.

#   Open Beat Box is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.

#   Open Beat Box is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.

#   You should have received a copy of the GNU General Public License
#   along with Open Beat Box; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

from os import path
from qt import *
from soundPath import *
from OBBCSoundTorturer import *
from OBBSongDocument import *
from xml.dom.minidom import *

from OBBUtils.OBBTimer import OBBTimer

import config
import copy

import OBBUtils.OBBSignals as sigs


class OBBSongCreator(QObject):
    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.hitPerCycle = config.HIT_PER_CYCLE
        self.instrNumber = config.INSTR_NUMBER
        self.hitPerRecorderCall = 2
        self.recorder = OBBCSoundTorturer()
        self.hitTimer = OBBTimer(config.DEF_BPM)
        self.doc = None

    def newSongDocument(self,
                        bpm=240,
                        filename="",
                        sampleMap=None ):
        if not filename:
            filename = path.join(getPatDir(), config.DEF_PAT_FILE)
            userSelected = 0
        else:
            userSelected = 1
        
        if self.doc:
            self.cleanUpTemporaryFiles()
        
        self.doc = OBBSongDocument(bpm)
        self.emit(sigs.clearHits, ())
        self.setFileName(filename, userSelected)
        self.setTempo(bpm)

        if not sampleMap:
            sampleMap = self.initSampleMap()
        
        for instrId in range(self.instrNumber):
            self.setInstrument( instrId, sampleMap[instrId] )
            self.setPanning(instrId, 0)
            
                                    
    def setHitPerCycle(self, hitPerCycle):
        self.hitPerCycle = hitPerCycle

    def setInstrument(self, samplePattern, sampleName):
        self.doc.songOrchestra.set(samplePattern, sampleName)
        self.emit(sigs.instrFileChanged, (samplePattern, sampleName))

    def removeInstrument(self):
        self.doc.songOrchestra.delete(samplePattern)

    def getInstrument(self, samplePattern):
        return self.doc.getInstrument(samplePattern).getPlayFilePath()
        
    def setPanning(self, samplePattern, panValue):
        self.doc.getInstrument(samplePattern).setPanning(panValue)
        self.emit(sigs.instrBalChanged, (samplePattern, panValue))

    def getPanning(self, samplePattern):
        self.doc.getInstrument(samplePattern).getPanning()

    def addHit(self, hitPattern, hitPosition):
        self.doc.songScore.Add( hitPattern,
                                hitPosition )


    # userSelected : is it the user that picked up this name ?
    def setFileName(self, fileName, userSelected=0):
        self.doc.setFileName(fileName)
        self.emit(sigs.patFileChanged, (fileName, userSelected))

    def getFileName(self):
        return self.doc.getFileName()

    def removeHit(self, hitPattern, hitPosition):
        self.doc.songScore.Delete( hitPattern,
                                   hitPosition )

    def toggleHit(self, hitPattern, hitPosition):
        if self.doc.songScore.isIn( hitPattern,
                                    hitPosition ):
            self.removeHit(hitPattern, hitPosition)
        else:
            self.addHit(hitPattern, hitPosition)
        self.emit(sigs.hitToggled, (hitPattern, hitPosition))
        self.doc.songScore.printScore()


    def setTempo(self, newValue):
        self.doc.setTempo(newValue)
        self.emit(sigs.tempoChanged, (newValue,))


    def getTempo(self):
        return self.doc.getTempo()


    def makeSample(self, hitId = -1, length = 16):
        #Sould always validate used synthetisor
        #before proceeding with advanced sample
        #creation
        #                             --VBarbin
        if not self.doc.songScore.isEmpty():
            self.recorder.createOrchestraFile(self.doc)
            if self.recorder.createScoreFile( self.doc,
                                              (hitId + 1) % self.hitPerCycle,
                                              length ):
                self.doc.setSample(self.recorder.createWaveFile())
                self.emit(sigs.sampleCreated, (self.doc,))
                return self.doc.samplePath


#     def handleSongChange(self, patternId, hitId):
#         self.toggleHit(patternId, hitId)


    def handleHit(self, hitId):
        if hitId % self.hitPerRecorderCall == self.hitPerRecorderCall - 1:
            self.makeSample(hitId, self.hitPerRecorderCall)
            
    def handleInstrumentVolume(self, paternId, volume):
        self.doc.getInstrument(paternId).setVolume(volume)
        self.doc.getInstrument(paternId).refreshSample()

    def handleInstrumentEffects():
        pass

    def cleanUpTemporaryFiles(self):
        self.doc.removeSample()
        for instr in self.doc.songOrchestra.get():
            self.doc.getInstrument(instr).deleteTempSampleFile()


    def save(self):
        domDoc = getDOMImplementation().createDocument("", "Song", None)
        domDoc.documentElement.setAttribute("Tempo", "%d" % self.doc.getTempo())
        domPattern = domDoc.createElement('Pattern')
        domOrchestra = domDoc.createElement('Orchestra')
        domScore = domDoc.createElement('Score')
        for i in self.doc.songOrchestra.get():
            domNode = domDoc.createElement('Instrument')
            domNode.setAttribute("Id", "%d" % i)
            domNode.setAttribute("File", self.doc.getInstrument(i).getFilePath())
            domNode.setAttribute("Volume", "%d" % self.doc.getInstrument(i).getVolume())
            domNode.setAttribute("Panning", "%d" % self.doc.getInstrument(i).panning)
            domNode.setAttribute("Reverb", "%d" % self.doc.getInstrument(i).reverb)
            domOrchestra.appendChild(domNode)
         
        for instr in self.doc.songScore.hitList:
            for step in self.doc.songScore.hitList[instr]:
                domNode = domDoc.createElement('Hit')
                domNode.setAttribute("Instr", "%d" % instr)
                domNode.setAttribute("Position", "%d" % step)
                domScore.appendChild(domNode)
            
        domPattern.appendChild(domOrchestra)
        domPattern.appendChild(domScore)
        domDoc.documentElement.appendChild(domPattern)
        file(self.doc.fileName, 'w+').write(domDoc.toprettyxml())
        domDoc.unlink()
        self.doc.changed = 0
            
    def loadFile(self, fileName):
        if access(fileName, R_OK):
            domDoc = parse(fileName)
            
            tempo = int(domDoc.documentElement.getAttribute("Tempo"))
            self.newSongDocument(tempo)
            for pattern in domDoc.getElementsByTagName("Pattern"):
                for orchestra in pattern.getElementsByTagName("Orchestra"):
                    for instr in orchestra.getElementsByTagName("Instrument"):
                        self.setInstrument(int(instr.getAttribute("Id")), 
                                           str(instr.getAttribute("File")))
                                            
                        self.setPanning(int(instr.getAttribute("Id")), 
                                        int(instr.getAttribute("Panning")))

                for score in pattern.getElementsByTagName("Score"):
                    for hit in score.getElementsByTagName("Hit"):
                        self.toggleHit(int(hit.getAttribute("Instr")),
                                       int(hit.getAttribute("Position")))
#                         self.emit(PYSIGNAL("hitStateLoaded(int, int)"), 
#                                             (int(hit.getAttribute("Position")),
#                                             int(hit.getAttribute("Instr")),))
                        
            self.doc.changed = 0

        self.setFileName(fileName, 1)
        

    def initSampleMap(self):
        # Those are all reasonable defaults but it would be so simple
        # to have some sound packs and would make the mix of cSound
        # instruments and samples really transparent.  You should
        # not assume that I know what I'm talking about...
        #                                        -- YGingras
        sampleMap = { 0:os.path.join( getSndDir(), "hat03.wav"   ),
                      1:os.path.join( getSndDir(), "hat07.wav"   ),
                      2:os.path.join( getSndDir(), "kick01.wav"  ),
                      3:os.path.join( getSndDir(), "snr02.wav"   ),
                      4:os.path.join( getSndDir(), "bongo02.wav" ),
                      5:os.path.join( getSndDir(), "tom01.wav"   ),
                      6:os.path.join( getSndDir(), "snr04.wav"   ),
                      7:os.path.join( getSndDir(), "tabla01.wav" ),}
        return sampleMap
