# -*- coding: utf-8 -*-

# Copyright (c) 2005 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a dialog to show the results of the PyLint run.
"""

import sys
import os

from qt import *

from KdeQt import KQMessageBox, KQFileDialog

from PyLintExecForm import PyLintExecForm

import Preferences
import Utilities

class PyLintExecDialog(PyLintExecForm):
    """
    Class implementing a dialog to show the results of the PyLint run.
    
    This class starts a QProcess and displays a dialog that
    shows the results of the PyLint command process.
    """
    def __init__(self, parent = None):
        """
        Constructor
        
        @param parent parent widget of this dialog (QWidget)
        """
        PyLintExecForm.__init__(self, parent)
        
        self.messageList.setAllColumnsShowFocus(1)
        
        self.process = None
        self.noResults = 1
        self.htmlOutput = 0
        self.parsedOutput = 0
        
        self.typeDict = {
            'C' : self.trUtf8('Convention'),
            'R' : self.trUtf8('Refactor'),
            'W' : self.trUtf8('Warning'),
            'E' : self.trUtf8('Error'),
            'F' : self.trUtf8('Fatal'),
        }
        
    def start(self, args, fn, reportFile, ppath):
        """
        Public slot to start PyLint.
        
        @param args commandline arguments for documentation programPyLint (QStringList)
        @param fn filename or dirname to be processed by PyLint
        @param reportFile filename of file to write the report to (string or QString)
        @param ppath project path (string or QString)
        @return flag indicating the successful start of the process
        """
        self.reportFile = reportFile
        self.ppath = ppath
        
        self.filename = unicode(fn)
        dname = os.path.dirname(self.filename)
        fname = os.path.basename(self.filename)
        
        self.contents.clear()
        self.errors.clear()
        self.saveButton.setEnabled(0)
        
        self.process = QProcess()
        self.process.setArguments(args)
        self.process.addArgument(fname)
        self.process.setWorkingDirectory(QDir(dname))
        
        self.connect(self.process, SIGNAL('readyReadStderr()'),
            self.handleReadStderr)
        self.connect(self.process, SIGNAL('processExited()'),
            self.finish)
            
        if args.contains("--parseable=y"):
            self.reportFile = None
            self.contents.hide()
            self.connect(self.process, SIGNAL('readyReadStdout()'),
                self.handleReadParseStdout)
            
            self.messageList.setSorting(-1)
            self.messageList.setColumnAlignment(1, Qt.AlignRight)
            self.messageList.setColumnAlignment(2, Qt.AlignHCenter)
            self.parsedOutput = 1
        else:
            self.connect(self.process, SIGNAL('readyReadStdout()'),
                self.handleReadStdout)
            self.messageList.hide()
            if args.contains("--html=y"):
                self.contents.setTextFormat(QTextBrowser.RichText)
                self.contents.setText('<b>Processing your request...</b>')
                self.htmlOutput = 1
            else:
                self.contents.setTextFormat(QTextBrowser.PlainText)
                self.contents.setFont(Preferences.getEditorOtherFonts("MonospacedFont"))
                self.htmlOutput = 0
            self.parsedOutput = 0
        self.noResults = 1
        
        self.buf = QString("")
        
        procStarted = self.process.start()
        if not procStarted:
            arg0 = self.process.arguments()[0]
            KQMessageBox.critical(None,
                self.trUtf8('Process Generation Error'),
                self.trUtf8(
                    'The process %1 could not be started.'
                    'Ensure, that it is in the search path.'
                ).arg(arg0),
                self.trUtf8('&OK'))
        else:
            self.setCursor(Qt.waitCursor)
        return procStarted
        
    def buttonPressed(self):
        """
        Private slot connected to the button clicked signal.
        """
        if self.process is None:
            self.accept()
        else:
            self.finish()
            
    def finish(self):
        """
        Private slot called when the process finished.
        
        It is called when the process finished or
        the user pressed the button.
        """
        self.unsetCursor()
        
        if self.htmlOutput:
            self.contents.setText(self.buf)
        
        if self.process is not None:
            self.process.tryTerminate()
            QTimer.singleShot(2000, self.process, SLOT('kill()'))
            
        self.button.setText(self.trUtf8('&Close'))
        self.button.setDefault(1)
        self.messageList.setSorting(0)
        
        self.process = None
        
        if self.reportFile:
            self.writeReport()
        elif not self.parsedOutput:
            self.saveButton.setEnabled(1)
        
        if self.noResults:
            itm = QListViewItem(self.messageList, self.trUtf8("No weaknesses found"))
        
    def handleReadStdout(self):
        """
        Private slot to handle the readyReadStdout signal. 
        
        It reads the output of the process, formats it and inserts it into
        the contents pane.
        """
        while self.process and self.process.canReadLineStdout():
            s = self.process.readLineStdout()
            self.buf.append(s).append(os.linesep)
            if not self.htmlOutput:
                self.contents.append(s)
        
    def handleReadParseStdout(self):
        """
        Private slot to handle the readyReadStdout signal for parseable output. 
        
        It reads the output of the process, formats it and inserts it into
        the message list pane.
        """
        while self.process and self.process.canReadLineStdout():
            s = unicode(self.process.readLineStdout())
            if s:
                try:
                    if sys.platform == "win32":
                        drive, s = os.path.splitdrive(s)
                        fname, lineno, fullmessage = s.split(':')
                        fname = drive + fname
                    else:
                        fname, lineno, fullmessage = s.split(':')
                    type_, message = fullmessage.strip().split(']', 1)
                    type_ = type_.strip()[1]
                    message = message.strip()
                    itm = QListViewItem(self.messageList, fname, lineno, 
                                                   self.typeDict[type_], message)
                    self.noResults = 0
                except ValueError:
                    continue
        
    def handleReadStderr(self):
        """
        Private slot to handle the readyReadStderr signal. 
        
        It reads the error output of the process and inserts it into the
        error pane.
        """
        while self.process and self.process.canReadLineStderr():
            s = self.process.readLineStderr()
            self.errors.append(s)
        
    def openFile(self, itm):
        """
        Private slot to handle the doubleClicked signal of the result list.
        
        @param itm The listview item that was double clicked.
        """
        if self.noResults:
            return
            
        fn = Utilities.normabspath(unicode(itm.text(0)))
        lineno = int(str(itm.text(1)))
        
        qApp.mainWidget().getViewManager().handlePythonFile(fn, lineno)
        
    def writeReport(self):
        """
        Private slot to write the report to a report file.
        """
        self.reportFile = unicode(self.reportFile)
        if os.path.exists(self.reportFile):
            res = KQMessageBox.warning(self,
                self.trUtf8("PyLint Report"),
                self.trUtf8("""<p>The PyLint report file <b>%1</b> already exists.</p>""")\
                    .arg(self.reportFile),
                self.trUtf8("&Overwrite"),
                self.trUtf8("&Cancel"),
                QString.null,
                1, -1)
            if res == 1:
                return
        
        try:
            f = open(self.reportFile, 'wb')
            f.write(unicode(self.buf))
            f.close()
        except IOError, why:
            KQMessageBox.critical(self, self.trUtf8('PyLint Report'),
                self.trUtf8('<p>The PyLint report file <b>%1</b> could not be written.'
                            '<br>Reason: %2</p>')
                    .arg(self.reportFile).arg(str(why)))
        
    def handleSave(self):
        """
        Private slot to save the report to a file.
        """
        if self.htmlOutput:
            filter = self.trUtf8("HTML Files (*.html);;All Files (*)")
        else:
            filter = self.trUtf8("Text Files (*.txt);;All Files (*)")
        
        self.reportFile = KQFileDialog.getSaveFileName(\
            self.ppath,
            filter,
            self, None,
            self.trUtf8("PyLint Report"),
            None, 1)
        if not self.reportFile.isEmpty():
            self.writeReport()
