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

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

"""
Module implementing a dialog to send bug reports.
"""

import sys
import os
import MimeWriter
import mimetypes
import StringIO
import smtplib
import socket

from qt import *
from qtext import QSCINTILLA_VERSION_STR

from EmailForm import EmailForm
from Info import Program, Version, BugAddress
import Preferences
import Utilities
if Utilities.getPythonVersion() >= 0x202:
    from Refactoring.Refactoring import Refactoring, BRM_VERSION_STR
else:
    BRM_VERSION_STR = 'not available'

# the following was taken from Python 2.3 email.Encoders
import base64
# Helpers
try:
    from quopri import encodestring as _encodestring

    def _qencode(s):
        enc = _encodestring(s, quotetabs=1)
        # Must encode spaces, which quopri.encodestring() doesn't do
        return enc.replace(' ', '=20')
except ImportError:
    # Python 2.1 doesn't have quopri.encodestring()
    from cStringIO import StringIO
    import quopri as _quopri

    def _qencode(s):
        if not s:
            return s
        hasnewline = (s[-1] == '\n')
        infp = StringIO(s)
        outfp = StringIO()
        _quopri.encode(infp, outfp, quotetabs=1)
        # Python 2.x's encode() doesn't encode spaces even when quotetabs==1
        value = outfp.getvalue().replace(' ', '=20')
        if not hasnewline and value[-1] == '\n':
            return value[:-1]
        return value


def _bencode(s):
    # We can't quite use base64.encodestring() since it tacks on a "courtesy
    # newline".  Blech!
    if not s:
        return s
    hasnewline = (s[-1] == '\n')
    value = base64.encodestring(s)
    if not hasnewline and value[-1] == '\n':
        return value[:-1]
    return value
# up to here

class EmailDialog(EmailForm):
    """
    Class implementing a dialog to send bug reports.
    """
    def __init__(self, parent=None):
        """
        Constructor
        
        Arguments
        
            parent -- parent widget of this dialog (QWidget)
        """
        EmailForm.__init__(self, parent)
        sig = Preferences.getUser("Signature")
        if sig:
            self.message.setText(sig)
            self.message.setCursorPosition(0, 0)
        
    def handleSend(self):
        """
        Private slot to send the email message.
        """
        if self.attachments.childCount():
            msg = self.createMultipartMail()
        else:
            msg = self.createSimpleMail()
            
        self.sendmail(msg)
        
        self.accept()
        
    def createSimpleMail(self):
        """
        Private method to create a simple mail message.
        
        Returns
        
            string containing the mail message
        """
        sio = StringIO.StringIO()
        msg = MimeWriter.MimeWriter(sio)
        msg.addheader("From", str(Preferences.getUser("Email")))
        msg.addheader("To", BugAddress)
        msg.addheader("Subject", str(self.subject.text()))
        msg.addheader("Content-Transfer-Encoding", "quoted-printable")
        sio = msg.startbody("text/plain")
        msgtext = ("%s\r\n----\r\nVersion Numbers:\r\n  Python %s\r\n  Qt %s\r\n" + \
            "  PyQt %s\r\n  QScintilla %s\r\n  %s %s\r\n  Bicycle Repair Man %s\r\n" + \
            "\r\nPlatform: %s\r\n") % (str(self.message.text()),
            sys.version.split()[0], str(QT_VERSION_STR),
            str(PYQT_VERSION_STR), str(QSCINTILLA_VERSION_STR),
            Program, Version, BRM_VERSION_STR, sys.platform)
        sio.write(_qencode(msgtext))
        msg = sio.getvalue()
        sio.close()
        return msg
        
    def createMultipartMail(self):
        """
        Private method to create a multipart mail message.
        
        Returns
        
            string containing the mail message
        """
        sio = StringIO.StringIO()
        msg = MimeWriter.MimeWriter(sio)
        msg.addheader("From", str(Preferences.getUser("Email")))
        msg.addheader("To", BugAddress)
        msg.addheader("Subject", str(self.subject.text()))
        
        # first part of multipart mail explains format
        p1 = msg.startmultipartbody("mixed")
        p1.write("This is a MIME-encoded message with attachments. "
            "If you see this message, your mail client is not "
            "capable of displaying the attachments.")
            
        # second part is intended to be read
        p2 = msg.nextpart()
        p2.addheader("Content-Transfer-Encoding", "quoted-printable")
        sio = p2.startbody("text/plain")
        msgtext = ("%s\r\n----\r\nVersion Numbers:\r\n  Python %s\r\n  Qt %s\r\n" + \
            "  PyQt %s\r\n  QScintilla %s\r\n  %s %s\r\n  Bicycle Repair Man %s\r\n" + \
            "\r\nPlatform: %s\r\n") % (str(self.message.text()),
            sys.version.split()[0], str(QT_VERSION_STR),
            str(PYQT_VERSION_STR), str(QSCINTILLA_VERSION_STR),
            Program, Version, BRM_VERSION_STR, sys.platform)
        sio.write(_qencode(msgtext))
        
        # next parts contain the attachments
        itm = self.attachments.firstChild()
        while itm:
            type = str(itm.text(1))
            fname = str(itm.text(0))
            name = os.path.basename(fname)
            istext = type.startswith("text/")
            
            att = msg.nextpart()
            if istext:
                att.addheader("Content-Transfer-Encoding", "quoted-printable")
            else:
                att.addheader("Content-Transfer-Encoding", "base64")
            sio = att.startbody(type, [["Name", name]])
            if istext:
                attencoded = _qencode(open(fname, "rb").read())
            else:
                attencoded = _bencode(open(fname, "rb").read())
            sio.write(attencoded)
            
            itm = itm.nextSibling()
            
        # never forget to call lastpart
        msg.lastpart()
        
        msg = sio.getvalue()
        sio.close()
        return msg

    def sendmail(self, msg):
        """
        Private method to actually send the message.
        
        Arguments
        
            msg -- the message to be sent (string)
        """
        try:
            server = smtplib.SMTP(str(Preferences.getUser("MailServer")))
            result = server.sendmail(str(Preferences.getUser("Email")),
                BugAddress, msg)
            server.quit()
        except (smtplib.SMTPException, socket.error), e:
            res = QMessageBox.critical(self,
                self.trUtf8("Send bug report"),
                self.trUtf8("""Message could not be sent.<br>Reason: %1""")
                    .arg(str(e)),
                self.trUtf8("&Retry"),
                self.trUtf8("&Abort"),
                None,
                0, -1)
            if res == 0:
                self.sendmail(msg)
                
    def handleAdd(self):
        """
        Private slot to handle the Add... button.
        """
        fname = QFileDialog.getOpenFileName(\
            None,
            None,
            None, None,
            self.trUtf8("Attach file"),
            None, 1)
        if not fname.isEmpty():
            fname = str(fname)
            type = mimetypes.guess_type(fname)[0]
            if not type:
                type = "application/octet-stream"
            itm = QListViewItem(self.attachments, fname, type)
            
    def handleDelete(self):
        """
        Private slot to handle the Delete button.
        """
        itm = self.attachments.selectedItem()
        if itm is not None:
            self.attachments.takeItem(itm)
            del itm

