from qt import *
from qttable import *
from releaseWizardBA import ReleaseWizardBA 
from constants import *
import logging
import os
from ReleaseDataVO import ReleaseDataVO
from FileVO import FileVO
from messageQueue import progressMessageQueue, ftpProgressQueue
from workerThread import WorkerThread
from help import Help
import time
from types import TupleType
import re
from stat import ST_SIZE
from utf8 import from_utf8, to_utf8

try:
    # python2.4
    set = set
except:
    # python2.3 compatibility
    from sets import Set
    set = Set

debug = logging.getLogger("releaseWizard").debug
info  = logging.getLogger("releaseWizard").info
error = logging.getLogger("releaseWizard").error

### file table constants

COL_BASENAME = 0
COL_CPU = 1
COL_FILE_TYPE = 2
COL_FILENAME = 3
COL_FILEID = 4


### edit release listview constants

COL_RELEASENAME = 0
COL_RELEASEID = 1
COL_RELEASESTATUS = 2

###

# hack for calculating w/ of qcombotableitems
SCROLLBAR_WIDTH = 25 

###

RX_CPU_DELIMITER =  re.compile("\.|-")  # split on . and -

class ReleaseWizard(ReleaseWizardBA):
    def __init__(self, parent, settings, sf_comm, projectVO, packageVO, create=True):
        ReleaseWizardBA.__init__(self)
            
        self.parent = parent
        self.settings = settings
        self.sf_comm = sf_comm
        self.projectVO = projectVO
        self.packageVO = packageVO
        self.create = create
        self.reguessFrame.hide()

        if not self.create:
            self.releaseNotifyCheckBox.setChecked(False)
            caption = "Edit Existing %s Release" % (packageVO.getPackageName().capitalize())
        else:
            caption = "Create New %s Release" % (packageVO.getPackageName().capitalize())
            
        self.setCaption(caption)
            
        self.group_id = projectVO.getGroupId()
        self.package_id = packageVO.getPackageId()
        self.release_id = None

        self.timer = QTimer(self)
        self.connect(self.timer, SIGNAL("timeout()"),
                     self.getProgressMsg)

        self.ftp_timer = QTimer(self)
        self.connect(self.ftp_timer, SIGNAL("timeout()"),
                     self.update_ftp_progress)
        
        self.connect(self, SIGNAL("selected(const QString &)"), self.pageChanged)
        self.releaseNameEdited(self.newReleaseNameLineEdit.text())

        self.release_notes_path = ""
        self.change_log_path = ""
        self.release_files_path = ""
        self.selected_files = set()

        self.fetch_prior_release = False;

        self.show_file_progress(False)
        self.releaseFilesTable.setColumnReadOnly(COL_BASENAME, True)
        self.releaseFilesTable.setColumnReadOnly(COL_FILENAME, True)
        self.releaseFilesTable.setLeftMargin(0)
        self.populateProcessorCombo()
        self.releaseFilesTable.hideColumn(COL_FILENAME)
        self.releaseFilesTable.hideColumn(COL_FILEID)
        
        self.table_font_metrics = QFontMetrics(self.releaseFilesTable.font())
        
        self.connect(self.releaseFilesTable,
                     SIGNAL("contextMenuRequested(int,int,const QPoint&)"),
                     self.fileMenu)

        self.thread = None
        self.eventDict = {EVENT_GET_RELEASES: self.populateReleases,
                          EVENT_RELEASE_FETCHED: self.releaseFetched,
                          EVENT_CREATE_RELEASE: self.getNextEvent,
                          EVENT_DELETE_FILES: self.getNextEvent,
                          EVENT_UPLOAD_FILES: self.getNextEvent,
                          EVENT_EDIT_RELEASE_1: self.getNextEvent,
                          EVENT_EDIT_RELEASE_2: self.getNextEvent,
                          EVENT_EDIT_RELEASE_3: self.getNextEvent,
                          EVENT_EDIT_RELEASE_4: self.getNextEvent}


        self.releaseVO = None
        self.editReleaseVO = None
        self.successful_completion = False
        self.cancelled = False
        self.deleted_files = None
        
        self.step2_warned_filenames = None
        
        self.__resizeComboTableItems()


        if create:
            self.next()
            self.newReleaseNameLineEdit.setFocus()
            self.setBackEnabled(self.page(WIZARD_PAGE_RELEASE_NAME), False)
        else:
            self.getReleases()

        self.fetch_prior_release = False


    def customEvent(self, event):
        data = event.data()
        etype = event.type()
        #print etype
        event_action = self.eventDict.get(etype)
        if event_action: event_action(data)

            

    def getNextEvent(self, data=None):
        event_action = self.eventQueue.pop(0)
        event_action(data)

        
    def setEventQueue(self):
        self.eventQueue = []
            
        if self.create:
            # creating a new release
            self.eventQueue.append(self.create_release)
            if self.uploadFileVOs:
                self.eventQueue.append(self.upload_files)

            self.eventQueue.append(self.edit_event_step1)
        else:
            # editing a release
            if self.uploadFileVOs:
                self.eventQueue.append(self.upload_files)

            if self.releaseVO.getReleaseNotes() != self.editReleaseVO.getReleaseNotes() or \
               self.releaseVO.getChangeLog() != self.editReleaseVO.getChangeLog() or \
               self.releaseVO.getStatus() != self.editReleaseVO.getStatus() or \
               self.releaseVO.getName() != self.editReleaseVO.getName():
                self.eventQueue.append(self.edit_event_step1)

            if self.deleted_files:
                self.eventQueue.append(self.delete_files)
                
        # creating or editing a release
        if self.uploadFileVOs:
            self.eventQueue.append(self.edit_event_step2)
            
        if self.attribFileVOs:
            self.eventQueue.append(self.edit_event_step3)

        if self.releaseVO.getNotifyUsers():
            self.eventQueue.append(self.edit_event_step4)

        self.eventQueue.append(self.complete)


    def __wait(self):
        if not self.thread: return
        while self.thread.running():
            debug("waiting for thread to complete...")
            time.sleep(0.25)

    def show_file_progress(self, show):
        debug("show_file_progress: %s", show)
        if show:
            self.ftp_timer.start(50, False)
            self.fileTextLabel.show()
            self.fileProgressBar.setProgress(-1)
            self.fileProgressBar.show()
        else:
            self.ftp_timer.stop()
            self.fileTextLabel.hide()
            self.fileProgressBar.hide()


    def next(self):
        ReleaseWizardBA.next(self)
        self.set_finish_button("Submit")
        
    def back(self):
        ReleaseWizardBA.back(self)
        self.set_finish_button("Submit")
        
    def set_finish_button(self, text="Finish"):
        self.finishButton().setText(text)

        
    def releaseNameEdited(self, text):
        releasePage = self.page(WIZARD_PAGE_RELEASE_NAME)
        if text.isEmpty():
            self.setNextEnabled(releasePage, False)
        else:
            self.setNextEnabled(releasePage, True)


    def help(self):
        #debug("help for page #: %d", self.indexOf(self.currentPage()))
        page_num = self.indexOf(self.currentPage())
        help_file_tuple = WIZARD_HELP_PAGES[page_num]
        if self.create: help_file = help_file_tuple[0]
        else: help_file = help_file_tuple[1]
        
        self._help = Help(self, help_file)
        

    def __importFile(self, filename, widget):
        try:
            fp = open(filename, "r")
            data = fp.read()
            fp.close()
            try:
                widget.setWordWrap(0)
            except:
                pass
            
            widget.setText(data)
            return 1
        except Exception, e:
            QMessageBox.warning(self,
                                "File read failed",
                                "Could not read from input file\n%s" % str(e))
            return 0
        
                                
    def importChangeLog(self):
        self.changeLogPushButton.setOn(False)
        self.changeLogPushButton.setDown(False)

        fn = QFileDialog.getOpenFileName(self.change_log_path or self.release_notes_path,
                                         "",
                                         self,
                                         "Import Change Log",
                                         "Import Change Log")
        fn = str(fn)
        if not fn: return
        if self.__importFile(fn, self.changeLogTextEdit):
            self.change_log_path = fn
                    
                    
    
    def importReleaseNotes(self):
        self.releaseNotesPushButton.setOn(False)
        self.releaseNotesPushButton.setDown(False)

        fn = QFileDialog.getOpenFileName(self.release_notes_path or self.change_log_path,
                                         "",
                                         self,
                                         "Import Release Notes",
                                         "Import Release Notes")
        fn = str(fn)
        if not fn: return
        if self.__importFile(fn, self.releaseNotesTextEdit):
            self.release_notes_path = fn


    def populateProcessorCombo(self):
        data = self.__getProcessorComboData()
        self.processorComboBox.insertStringList(data)


    def __getFileTypeWidth(self):
        return self.__getComboWidth(FILE_TYPE.keys())


    def __getProcessorWidth(self):
        return self.__getComboWidth(CPU.keys())


    def __getComboWidth(self, slist):
        maxwidth = 0
        for s in slist:
            maxwidth = max(self.table_font_metrics.width(s), maxwidth)
        maxwidth += SCROLLBAR_WIDTH
        return maxwidth


    def __getProcessorComboData(self):
        keys = CPU.keys()
        keys.sort()
        strlist = QStringList()
        #strlist.append("")
        for key in keys:
            strlist.append(key)
        return strlist
                           

    def __getFileTypeComboData(self):
        keys = FILE_TYPE.keys()
        keys.sort()
        strlist = QStringList()
        #strlist.append("")
        maxwidth = 0
        for key in keys:
            strlist.append(key)
            
        return strlist
    

    def __guessFileType(self, filename):
        parts = filename.split(".")
        lparts = len(parts)
        if lparts == 1:
            exts = []
        elif lparts == 2:
            exts = [".%s" % parts[1]]
        else:
            exts = [".%s.%s" % (parts[-2], parts[-1]), ".%s" % parts[-1]]

        guess = None
        for ext in exts:
            guess = FILE_TYPE_GUESS.get(ext)
            if guess: break
        if not guess: guess = "Other"
        debug("guess: %s is type: %s", filename, guess)
        return guess
    

    def __guessProcessorType(self, filename):
        guess = 'Any'
        try:
            parts = RX_CPU_DELIMITER.split(filename)[1:-1]
        except:
            # ignore the leading and trailing filename parts
            # eg. foo.alpha.tgz
            return 'Any'

        for part in parts:
            part = part.lower()
            guess = CPU_GUESS.get(part)
            if guess: break
        
        debug("guess: %s cpu is: %s", filename, guess or "Any")
        return guess or "Any"

    


##    def __addApplyAllRow(self):
##        rownum = 0
##        self.releaseFilesTable.setNumRows(rownum+1)
##        cpuData = self.__getProcessorComboData()
##        cpuData.prepend("Apply To All")
##        cpuCombo = QComboTableItem(self.releaseFilesTable,
##                                   cpuData)
##        self.releaseFilesTable.setItem(rownum, COL_CPU, cpuCombo)

##        for colnum in range(self.releaseFilesTable.numCols()):
##            rect = self.releaseFilesTable.cellRect(rownum, colnum);
##            pix = QPixmap(rect.width() * 5, rect.height())
        
##            color = Qt.gray
##            pix.fill(color)
##            self.releaseFilesTable.setPixmap(rownum, colnum, pix)
##            self.releaseFilesTable.setPixmap(rownum+1, colnum, pix)
        

    def updateProcessor(self, name):
        sname = str(name)
        debug("updateProcessor(): %s", sname)

        if sname in ('Choose...', ''): return

        rownum = self.releaseFilesTable.numRows()
        for i in range(rownum):
            cpuCombo = self.releaseFilesTable.item(i, COL_CPU)
            cpuCombo.setCurrentItem(name or 0) 
        if self.__validateFiles(rownum):
            self.setNextEnabled(self.page(WIZARD_PAGE_FILE_SELECTION), True)


    def reguessFileAttributes(self):
        rownum = self.releaseFilesTable.numRows()
        for i in range(rownum):
            filename = str(self.releaseFilesTable.text(i, COL_BASENAME))
            cpu = self.__guessProcessorType(filename)
            filetype = self.__guessFileType(filename)
           
            cpuCombo = self.releaseFilesTable.item(i, COL_CPU)
            currcpu = str(cpuCombo.currentText())
            if currcpu != cpu:
                cpuCombo.setCurrentItem(cpu)
            
            fileTypeCombo = self.releaseFilesTable.item(i, COL_FILE_TYPE)
            currfiletype = str(fileTypeCombo.currentText())
            if currfiletype != filetype:
                fileTypeCombo.setCurrentItem(filetype or 0)

            
            
            
    def addFiles(self):
        self.addFilesPushButton.setOn(False)
        self.addFilesPushButton.setDown(False)
        files = QFileDialog.getOpenFileNames("",
                                             self.release_files_path,
                                             self,
                                             "Add Files",
                                             "Add Files")

        if files:
            self.processorComboBox.setEnabled(True)
            self.reguessFrame.show()
       
        for f in files:
            fstr = str(f)

            filename = os.path.basename(fstr)

            self.__addFile(filename,
                           self.__guessProcessorType(filename),
                           self.__guessFileType(filename),
                           fstr)

        self.__resizeBasenameColumn()
        if self.releaseFilesTable.numRows() > 0:
            self.setNextEnabled(self.page(WIZARD_PAGE_FILE_SELECTION), True)



    def __addFile(self, filename, cpu, filetype, fullpath="", fileid=""):
        if filename in self.selected_files: return
        else: self.selected_files.add(filename)
        
        rownum = self.releaseFilesTable.numRows()
        self.releaseFilesTable.setNumRows(rownum + 1)

        self.releaseFilesTable.setText(rownum, COL_BASENAME, filename)
            
        cpuData = self.__getProcessorComboData()
        cpuCombo = QComboTableItem(self.releaseFilesTable,
                                   cpuData)

        cpuCombo.setCurrentItem(cpu)
        
        self.releaseFilesTable.setItem(rownum, COL_CPU, cpuCombo)

        fileTypeData = self.__getFileTypeComboData()
        fileTypeCombo = QComboTableItem(self.releaseFilesTable,
                                        fileTypeData)
        fileTypeCombo.setCurrentItem(filetype or 0)
        self.releaseFilesTable.setItem(rownum, COL_FILE_TYPE, fileTypeCombo)
            
        self.releaseFilesTable.setText(rownum, COL_FILENAME, fullpath)
        self.releaseFilesTable.setText(rownum, COL_FILEID, fileid)


    def __validateFiles(self, rownum=None):
        if not rownum: rownum = self.releaseFilesTable.numRows()
        
        ok = True
        for i in range(rownum):
            cpuCombo = self.releaseFilesTable.item(i, COL_CPU)
            cpuStr = str(cpuCombo.currentText())

            if cpuStr in (None, ''): return False

            typeCombo = self.releaseFilesTable.item(i, COL_FILE_TYPE)
            typeStr = str(typeCombo.currentText())

            if typeStr in (None, ''): return False

        return True


    def __resizeComboTableItems(self):
        max_file_type_width = self.__getFileTypeWidth()
        self.releaseFilesTable.setColumnWidth(COL_FILE_TYPE,
                                              max_file_type_width)
        
        max_cpu_width = self.__getProcessorWidth()
        self.releaseFilesTable.setColumnWidth(COL_CPU,
                                              max_cpu_width)

        
    def __resizeBasenameColumn(self):
        self.releaseFilesTable.adjustColumn(COL_BASENAME)
        width = self.releaseFilesTable.columnWidth(COL_BASENAME) + 5
        self.releaseFilesTable.setColumnWidth(COL_BASENAME, width)

        
    def fileMenu(self, row, col, qpt):
        self.file_menu = QPopupMenu(self)
        self.file_menu.insertItem("Remove files",
                                  self.removeFiles)
        self.file_menu.popup(QCursor.pos())
        

    def removeFiles(self):
        # removes selected files from the fileselection table
        numsel = self.releaseFilesTable.numSelections()
        rows = []
        for i in range(numsel):
            tsel = self.releaseFilesTable.selection(i)
            for rownum in range(tsel.topRow(), tsel.bottomRow()+1):
                rows.append(rownum)
                filename = str(self.releaseFilesTable.text(rownum, COL_BASENAME))
                self.selected_files.remove(filename)

                
        # delete rows in reverse sorted order
        rows.sort()
        rows.reverse()
        for rownum in rows:
            fileid = self.releaseFilesTable.text(rownum, COL_FILEID)
            if fileid and not self.create: self.deleted_files.add(str(fileid))
                
            self.releaseFilesTable.removeRow(rownum)

        if self.releaseFilesTable.numRows() == 0:
            self.processorComboBox.setEnabled(False)
            self.reguessFrame.hide()
            if self.create:
                self.setNextEnabled(self.page(WIZARD_PAGE_FILE_SELECTION), False)


    def getReleaseData(self):
        files = []
        for rownum in range(self.releaseFilesTable.numRows()):
            fullpath = str(self.releaseFilesTable.text(rownum, COL_FILENAME))
            filename = str(self.releaseFilesTable.text(rownum, COL_BASENAME))
            processor = str(self.releaseFilesTable.text(rownum, COL_CPU))
            filetype = str(self.releaseFilesTable.text(rownum, COL_FILE_TYPE))
            fileid = str(self.releaseFilesTable.text(rownum, COL_FILEID))
            files.append(FileVO(fullpath,
                                filename,
                                processor,
                                CPU.get(processor),
                                filetype,
                                FILE_TYPE.get(filetype),
                                fileid))
        
        releaseVO = ReleaseDataVO(self.projectVO,
                                  self.packageVO)
        releaseVO.setName(to_utf8(self.newReleaseNameLineEdit.text()))
        releaseVO.setChangeLog(to_utf8(self.changeLogTextEdit.text()))
        releaseVO.setReleaseNotes(to_utf8(self.releaseNotesTextEdit.text()))
        releaseVO.setNotifyUsers(self.releaseNotifyCheckBox.isChecked())
        releaseVO.setStatus(str(self.statusComboBox.currentText()))
        releaseVO.setFiles(files)
        return releaseVO


    def __getTotalSteps(self):
        num = 1
        if self.create:
            num += len(self.uploadFileVOs) * 2 # file u/l & step 3
            num += 1 # create release
            num += 1 # step 1
            num += 1 # step 2
            num += 1 # step 4
        else:
            num += len(self.uploadFileVOs) * 2
            num += len(self.attribFileVOs)
            num += len(self.deleted_files)
        
        return num + 1



    def __computeTotalFileSize(self):
        total_size = 0
        for vo in self.uploadFileVOs:
            fname = vo.getFullPath()
            total_size += os.stat(fname)[ST_SIZE]
        return total_size


    def getReleases(self):
        self.setNextEnabled(self.page(WIZARD_PAGE_EDIT_RELEASE), False)

        self.setCursor(Qt.waitCursor)
        self.thread = WorkerThread(self,
                                   EVENT_GET_RELEASES,
                                   self.sf_comm.get_releases,
                                   self.group_id,
                                   self.package_id)        
        self.thread.start()


    def populateReleases(self, releases):
        if not releases:
            QMessageBox.warning(self,
                                "Releases not found",
                                "There are no current releases\nfor the selected package")
            self.reject()

        # disable auto-sorting
        self.editReleasesListView.setSorting(self.editReleasesListView.columns() +1,
                                             True)
        for release in releases:
            release_name = release[0].strip()

            item = QListViewItem(self.editReleasesListView)
            item.setText(COL_RELEASENAME, from_utf8(release_name))
            item.setText(COL_RELEASEID, release[1])
            item.setText(COL_RELEASESTATUS, release[2])
        self.setCursor(Qt.arrowCursor)
        self.processorComboBox.setEnabled(True)


    def releaseSelected(self, item):
        self.setNextEnabled(self.page(WIZARD_PAGE_EDIT_RELEASE), True)
        self.newReleaseNameLineEdit.setText(item.text(COL_RELEASENAME))
        self.release_id = str(item.text(COL_RELEASEID))
        self.statusComboBox.setCurrentText(item.text(COL_RELEASESTATUS))
        self.fetch_prior_release = True
        #self.setNextEnabled(self.page(WIZARD_PAGE_RELEASE_NAME), False)
        

    def releaseSelectedNextPage(self, item):
        self.releaseSelected(item)
        self.next()
        
        
    def getRelease(self):
        self.setCursor(Qt.waitCursor)
        self.thread = WorkerThread(self,
                                   EVENT_RELEASE_FETCHED,
                                   self.sf_comm.get_release,
                                   self.group_id,
                                   self.package_id,
                                   self.release_id)        
        self.thread.start()


    def releaseFetched(self, data_tuple):
        release_notes = data_tuple[0]
        change_log = data_tuple[1]
        files = data_tuple[2]
              
        self.releaseNotesTextEdit.setText(from_utf8(release_notes))
        self.changeLogTextEdit.setText(from_utf8(change_log))
        self.setNextEnabled(self.page(WIZARD_PAGE_RELEASE_NAME), False)

        releaseVO = ReleaseDataVO(self.projectVO,
                                  self.packageVO)
        
        releaseVO.setName(to_utf8(self.newReleaseNameLineEdit.text()))
        releaseVO.setNotifyUsers(self.releaseNotifyCheckBox.isChecked())
        releaseVO.setChangeLog(to_utf8(change_log)),
        releaseVO.setReleaseNotes(to_utf8(release_notes)),
        release_item = self.editReleasesListView.currentItem()
        releaseVO.setStatus(str(release_item.text(COL_RELEASESTATUS)))

        
        self.selected_files = set()
        self.deleted_files = set()
        self.releaseFilesTable.setNumRows(0)
        
        rownum = 0
        fileVOs = []

        for f in files:
            fileid = f[0]
            filename = f[1]
            cpu = f[2]
            filetype = f[3]
            fileVOs.append(FileVO(None,
                                  filename,
                                  cpu,
                                  CPU.get(cpu),
                                  filetype,
                                  FILE_TYPE.get(filetype),
                                  fileid))

            self.__addFile(filename,
                           cpu,
                           filetype,
                           "",
                           fileid)

        if files: self.reguessFrame.show()

        debug("selected_files: %s", self.selected_files)
        releaseVO.setFiles(fileVOs)
        self.editReleaseVO = releaseVO
        self.setCursor(Qt.arrowCursor)

        self.__resizeBasenameColumn()
        # populate widgets...
        self.pageChanged(None)
        self.setNextEnabled(self.page(WIZARD_PAGE_RELEASE_NAME), True)
    

    def pageChanged(self, pageName):
        currPage = self.currentPage()
        pageNum = self.indexOf(currPage)
        if pageNum == WIZARD_PAGE_EDIT_RELEASE:
            # if release selected, enable next
            pass
        elif pageNum == WIZARD_PAGE_RELEASE_NAME:
            if self.fetch_prior_release:
                self.fetch_prior_release = False
                self.getRelease()
                
            if self.create and not self.newReleaseNameLineEdit.text().isEmpty(): enable = True
            elif not self.create: enable = True
            else: enable = False
            self.setNextEnabled(currPage, enable)
        elif pageNum == WIZARD_PAGE_FILE_SELECTION:
            if self.create:
                self.setNextEnabled(currPage, self.releaseFilesTable.numRows() != 0)
        elif pageNum == WIZARD_PAGE_PROGRESS:
            self.progressBar.setProgress(0)

            if self.create:
                msg = "create"
                self.deleteFilesTextLabel.hide()
            else:
                msg = "edit"
                self.createReleaseTextLabel.hide()
            
            self.progressTextLabel.setText("Press the 'Submit' button to %s your SourceForge release" % msg)
            self.setFinishEnabled(currPage, True)





    ####################################################################################
    # Progress-related code 
    ####################################################################################

    def getProgressMsg(self):
        # read the message queue.  If an item exists,
        # populate the progress label with it.
        msg = progressMessageQueue.get()
        if msg:
            self.progressTextLabel.setText(msg)            
            self.incrementProgressBar()
                

    def incrementProgressBar(self, increment=1):
        self.progressBar.setProgress(self.progressBar.progress() + increment)


    def isCancelled(self):
        return self.cancelled
    

    def accept(self):
        if self.successful_completion: ReleaseWizardBA.accept(self)
        else:
            page = self.page(WIZARD_PAGE_PROGRESS)
            self.releaseVO = self.getReleaseData()
            self.initializeProgessPage()
            
            self.setBackEnabled(page, False)
            self.setFinishEnabled(page, False)
            self.set_finish_button()

            self.cancelled = False

            fileVOs = self.releaseVO.getFiles()
            if self.create:
                self.uploadFileVOs = fileVOs
                self.attribFileVOs = fileVOs
            else:
                self.uploadFileVOs = []
                self.attribFileVOs = []

                existingHash = {}
                for fileVO in self.editReleaseVO.getFiles():
                    existingHash[fileVO.getFileId()] = fileVO
                    #print fileVO                    
                for fileVO in fileVOs:
                    if fileVO.getFullPath():
                        self.uploadFileVOs.append(fileVO)
                        self.attribFileVOs.append(fileVO)
                        continue

                    existingFileVO = existingHash.get(fileVO.getFileId())
                    
                    
                    if existingFileVO:
##                        print "----"
##                        print existingFileVO.getFilename()
##                        print existingFileVO.getProcessor()
##                        print fileVO.getProcessor()
##                        print "----"
##                        print existingFileVO.getFileType()
##                        print fileVO.getFileType()
##                        print "----"
                        if existingFileVO.getProcessor() != fileVO.getProcessor() or \
                           existingFileVO.getFileType() != fileVO.getFileType():
                            self.attribFileVOs.append(fileVO)
                    

            debug("uploadFileVOs: %s", self.uploadFileVOs)
            debug("attribFileVOs: %s", self.attribFileVOs)
            debug("deleted fileids: %s", self.deleted_files)

            self.progressBar.setTotalSteps(self.__getTotalSteps())
            self.setEventQueue()
            debug("event queue: %s",  self.eventQueue)

            self.step2_warned_filenames = None
            if self.create:
                self.start()
                #self.create_release()
            else:
                if self.eventQueue[0] in (self.edit_event_step4, self.complete):
                    QMessageBox.warning(self, "Nothing to do", "You have not made any changes to the release.\n  Please go 'back' and edit the release if necessary.  Otherwise, press 'cancel' to exit the wizard")
                    self.setBackEnabled(page, True)
                    return
                else:
                    self.start()


    def start(self):
        debug("Starting release process...")
        self.timer.start(500, False)
        self.getNextEvent("start")
        
        
    def success():
        ReleaseWizardBA.accept(self)


    def reject(self):
        debug("Operation cancelled by user")
        # perhaps hide the release?
        self.cancelled = True
        ReleaseWizardBA.reject(self)
        

    def create_release(self, data=None):
        self.createReleaseTextLabel.setEnabled(True)
        self.__wait()
        if self.cancelled: return
        self.thread = WorkerThread(self,
                                   EVENT_CREATE_RELEASE,
                                   self.sf_comm.add_release,
                                   self.group_id,
                                   self.package_id,
                                   self.releaseVO.getName())
        
        self.thread.start()


    def delete_files(self, data=None):
        self.deleteFilesTextLabel.setEnabled(True)
        self.__wait()
        if self.cancelled: return
        
        if not self.deleted_files:
            self.getNextEvent(True)
            return
            #self.upload_files("edit")
            #return
        
        self.thread = WorkerThread(self,
                                   EVENT_DELETE_FILES,
                                   self.sf_comm.delete_files,
                                   self.group_id,
                                   self.package_id,
                                   self.release_id,
                                   self.deleted_files)
        
        self.thread.start()


    def upload_files(self, data):
        self.uploadFilesTextLabel.setEnabled(True)
        debug("now entering upload_files: %s", data)
        if not data:
            self.error("Could not create release")
            return

        if self.create:
            self.release_id = data


        if not self.uploadFileVOs:
            # should never be the case ?
            self.getNextEvent(True)
            return
        
        self.__wait()
        if self.cancelled: return
        self.show_file_progress(True)
        self.thread = WorkerThread(self,
                                   EVENT_UPLOAD_FILES,
                                   self.sf_comm.upload_files,
                                   self,
                                   self.uploadFileVOs)

        self.thread.start()
            

    def edit_event_step1(self, data):
        self.changeLogTextLabel.setEnabled(True)
        self.releaseNotesTextLabel.setEnabled(True)
        debug("now entering edit_event_step1: %s", data)
        release_notes = self.releaseVO.getReleaseNotes()
        change_log = self.releaseVO.getChangeLog()
        status = self.releaseVO.getStatus()

        if self.create and not release_notes and not change_log:
            self.getNextEvent(True)
            #self.edit_event_step2(True)
            return

        self.__wait()
        if self.cancelled: return
        self.thread = WorkerThread(self,
                                   EVENT_EDIT_RELEASE_1,
                                   self.sf_comm.edit_release_step1,
                                   self.group_id,
                                   self.package_id,
                                   self.release_id,
                                   self.releaseVO.getName(),
                                   release_notes,
                                   change_log,
                                   status)

        self.thread.start()


    def edit_event_step2(self, ok):
        self.selectFilesTextLabel.setEnabled(True)
        debug("now entering edit_event_step2: %s", ok)
        self.__wait()
        if self.cancelled: return
        if not ok:
            self.error("Unable to add release notes/change log")
            return
        
        self.thread = WorkerThread(self,
                                   EVENT_EDIT_RELEASE_2,
                                   self.sf_comm.edit_release_step2,
                                   self.group_id,
                                   self.package_id,
                                   self.release_id,
                                   self.uploadFileVOs)

        self.thread.start()


    def __getFilenameToIdHash(self):
        files = self.editReleaseVO.getFiles()
        d = {}
        for f in files:
            d[f.getFilename()] = f.getFileId()
        return d
    

    def edit_event_step3(self, data_tuple):
        self.fileAttribTextLabel.setEnabled(True)
        warned_filenames = []
        fileVOs = self.releaseVO.getFiles()
        
        #for f in fileVOs:
        #    debug("fileVO: %s", f)
            
        if type(data_tuple) is TupleType:
            # create
            debug("creating lookup_dict: %s - %s - %s", data_tuple[0], data_tuple[1], data_tuple[2])
            lookup_dict = data_tuple[0]
            warned_filenames = data_tuple[1]
            ok = data_tuple[2]
            if warned_filenames:
                for vo in fileVOs:
                    if vo.getFilename() in warned_filenames:
                        fileVOs.remove(vo)
        else:
            # edit
            ok = data_tuple
            lookup_dict = self.__getFilenameToIdHash()


        if warned_filenames:
            self.step2_warned_filenames = warned_filenames

        if not ok:
            self.error("Unable to select file(s) for inclusion")
            return
        
        debug("edit_event_step3: %s - %s - %s", lookup_dict, warned_filenames, ok)
        self.__wait()
        if self.cancelled: return
        


        self.thread = WorkerThread(self,
                                   EVENT_EDIT_RELEASE_3,
                                   self.sf_comm.edit_release_step3,
                                   self.group_id,
                                   self.package_id,
                                   self.release_id,
                                   fileVOs,
                                   lookup_dict)

        self.thread.start()

                                   
    def edit_event_step4(self, ok):
        self.notifyTextLabel.setEnabled(True)
        debug("edit_event_step4: %s", ok)
        self.__wait()
        if self.cancelled: return
        if not ok:
            self.error("Unable to update file attributes")
            return
        
        if not self.releaseVO.getNotifyUsers(): self.complete(None)
        
        self.thread = WorkerThread(self,
                                   EVENT_EDIT_RELEASE_4,
                                   self.sf_comm.edit_release_step4,
                                   self.group_id,
                                   self.package_id,
                                   self.release_id)

        self.thread.start()


    def error(self, msg):
        self.timer.stop()
        self.ftp_timer.stop()
        page = self.page(WIZARD_PAGE_PROGRESS)
        self.progressTextLabel.setText(msg)
        self.setFinishEnabled(page, False)
        self.setBackEnabled(page, True)
        self.successful_completion = False
        self.displayStep2Warning()


    def complete(self, data):
        self.timer.stop()
        self.ftp_timer.stop()
        page = self.page(WIZARD_PAGE_PROGRESS)
        if self.create:
            self.progressTextLabel.setText("Successfully created new release")
        else:
            self.progressTextLabel.setText("Successfully edited an existing release")
        self.progressBar.setProgress(self.progressBar.totalSteps())
        self.setFinishEnabled(page, True)
        self.setBackEnabled(page, False)
        self.successful_completion = True

        self.displayStep2Warning()
        

    def displayStep2Warning(self):
        if self.step2_warned_filenames:
            fstr = ""
            for f in self.step2_warned_filenames:
                fstr += "   %s\n" % f

            print fstr
            QMessageBox.warning(self,
                                "Filename uniqueness violation",
                                SF_WARNING % fstr)
                                
        

    def initializeProgessPage(self):
        if self.releaseVO.getNotifyUsers():
            self.notifyTextLabel.show()
        else:
            self.notifyTextLabel.hide()

        self.createReleaseTextLabel.setEnabled(False)
        self.uploadFilesTextLabel.setEnabled(False)
        self.changeLogTextLabel.setEnabled(False)
        self.releaseNotesTextLabel.setEnabled(False)
        self.selectFilesTextLabel.setEnabled(False)
        self.fileAttribTextLabel.setEnabled(False)
        self.notifyTextLabel.setEnabled(False)


    def update_ftp_progress(self):
        # read the message queue.  If an item exists,
        # populate the progress label with it.
        progress_tuple = ftpProgressQueue.get()
        #debug("update_ftp_progress: %s",  progress_tuple)
        if progress_tuple:
            if progress_tuple == (True, True):
                self.show_file_progress(False)
                return 
            else:
                try:
                    percent_complete = float(progress_tuple[0]) / float(progress_tuple[1]) * 100.0
                except:
                    percent_complete = 0.0

            int_percent = int(percent_complete)
            #debug("pct: %f", percent_complete) 
            self.fileProgressBar.setProgress(int_percent)


