#
# Copyright 2016 Pixar
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the trade
#    names, trademarks, service marks, or product names of the Licensor
#    and its affiliates, except as required to comply with Section 4(c) of
#    the License and to reproduce the content of the NOTICE file.
#
# You may obtain a copy of the Apache License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#
# Sendmail Plugin
#
# This module provides plug-in facilities for usdview to send emails containing
# captured screenshots from usdview.
#
# Use the `SendMail` function as the callback function for a Usdview command
# plugin. Optionally, modify `_GetSenderAddress` to auto-fill the sender's email
# address.

from pxr.Usdviewq.qt import QtWidgets, QtCore

def _GetSenderAddress():
    '''Returns an email address used to auto-fill the 'From' field.'''
    return ""

def SendMail(usdviewApi):
    '''This method creates a local SMTP server, runs the dialog
       and upon successful completion, sends the info to the intended
       recipient.'''
    import smtplib
    import tempfile
    from email.mime.text import MIMEText
    from email.mime.image import MIMEImage
    from email.mime.multipart import MIMEMultipart

    # create the image to send as a temporary file
    with tempfile.NamedTemporaryFile(suffix=".jpeg") as tempimagefile:
        windowShot = usdviewApi.GrabWindowShot()
        viewportShot = usdviewApi.GrabViewportShot()

        # If we can't grab a shot of the viewport (probably if --norender is
        # used) then only allow window shots.
        allowScreenCapSel = (viewportShot is not None)

        # fetch the info from the launched sendmail dialog
        mailInfo = _GetSendMailInfo(usdviewApi, allowScreenCapSel)

        if not _ValidMailInfo(mailInfo):
            return False

        # set the image based on user config
        if mailInfo.imagetype == "Window":
            imagedata = windowShot
        else:
            imagedata = viewportShot

        # verify we created an image correctly and could safely save it
        if not imagedata or not imagedata.save(tempimagefile.name):
            return False

        # construct the message and convert the body to MIMEText
        msg = MIMEMultipart()
        msg['Subject'] = mailInfo.subject
        msg['From'] = (mailInfo.sender.strip() if
                       mailInfo.sender.strip() else None)
        msg['To'] = ','.join(x.strip() for x in mailInfo.sendee.split(',') if
                             x.strip())

        # Either the from or to category was unsatisfactory, bail
        if msg['From'] is None or len(msg['To'].split(',')) == 0:
            return False

        msg.attach(MIMEText(mailInfo.body))
        # grab the image data from the file, convert it to
        # MIME and attach it to the message
        msg.attach(MIMEImage(tempimagefile.read(), _subtype="jpeg"))

        # launch smtp server on local and send mail
        server = smtplib.SMTP('localhost')

        # Note that pythons stmplib requires the second arg as a list here
        # even though msg['To'] requires a string.
        server.sendmail(msg['From'],
                        msg['To'].split(','),
                        msg.as_string())
        server.quit()

class EmailInfo():
    '''A class to represent necessary fields in an email'''
    def __init__(self='', sender='', sendee='', subject='', body='',
                 imagetype=''):
        self.sender = sender
        self.sendee = sendee
        self.subject = subject
        self.body = body
        self.imagetype = imagetype

def _GenerateLayout(dialog, allowScreenCapSel):
    '''This method generates the full dialog, containing the text fields,
       buttons and combobox for choosing the type of screenshot'''
    (fields, textLayout) = _GenerateTextFields(dialog)
    buttonLayout = _GenerateButtons(dialog, fields, allowScreenCapSel)

    # compose layouts
    layout = QtWidgets.QVBoxLayout()
    layout.addLayout(textLayout)
    layout.addLayout(buttonLayout)

    return layout

def _GenerateTextFields(dialog):
    '''This method generates the layout containing the text fields to
        be filled by the user, such as sender, sendee and message.
        Note that this takes a reference to the dialog because the buttons
        need to create a signal handler which uses the text field info'''
    # construct text layout
    senderLabel = QtWidgets.QLabel("From: ")
    sendeeLabel = QtWidgets.QLabel("To: ")
    subjectLabel = QtWidgets.QLabel("Subject: ")
    bodyLabel   = QtWidgets.QLabel("Message: ")

    senderFld = QtWidgets.QLineEdit(dialog.emailInfo.sender)
    sendeeFld = QtWidgets.QLineEdit(dialog.emailInfo.sendee)
    subjectFld = QtWidgets.QLineEdit(dialog.emailInfo.subject)
    bodyFld   = QtWidgets.QTextEdit()
    bodyFld.setText(dialog.emailInfo.body)

    senderLayout = QtWidgets.QHBoxLayout()
    senderLayout.addWidget(senderFld)

    sendeeLayout = QtWidgets.QHBoxLayout()
    sendeeLayout.addWidget(sendeeFld)

    subjectLayout = QtWidgets.QHBoxLayout()
    subjectLayout.addWidget(subjectFld)

    bodyLayout = QtWidgets.QHBoxLayout()
    bodyLayout.addWidget(bodyFld)

    fieldsLayout = QtWidgets.QVBoxLayout()
    fieldsLayout.addWidget(senderLabel)
    fieldsLayout.addLayout(senderLayout)
    fieldsLayout.addWidget(sendeeLabel)
    fieldsLayout.addLayout(sendeeLayout)
    fieldsLayout.addWidget(subjectLabel)
    fieldsLayout.addLayout(subjectLayout)
    fieldsLayout.addWidget(bodyLabel)
    fieldsLayout.addLayout(bodyLayout)

    return ({"sender" : senderFld,
            "sendee" : sendeeFld,
            "subject" :subjectFld,
            "body":    bodyFld},
            fieldsLayout)

def _GenerateButtons(dialog, fields, allowScreenCapSel):
    '''This method generates the buttons used in our sendmail
       dialog. This includes creating signal handlers which batch
       the inputted info for sending across the wire.'''

    # construct buttons
    cancelBt = QtWidgets.QPushButton("Cancel")
    acceptBt = QtWidgets.QPushButton("Send")

    # construct screen cap selector if there are multiple options available
    screenCapSel = None
    if allowScreenCapSel:
        screenCapSel = QtWidgets.QComboBox()
        screenCapSel.setSizeAdjustPolicy(
                QtWidgets.QComboBox.AdjustToContents)
        screenCapSel.addItem("Window")
        screenCapSel.addItem("Viewport")

    buttonLayout = QtWidgets.QHBoxLayout()
    buttonLayout.addStretch(0.5)
    if allowScreenCapSel:
        buttonLayout.addWidget(screenCapSel)
    buttonLayout.addWidget(cancelBt)
    buttonLayout.addWidget(acceptBt)

    # add a signal handler for the accept and close buttons
    # upon cancelling, simply close the dialog
    cancelBt.clicked.connect(lambda : dialog.close())

    # upon accepting, fill the data and close the dialog
    acceptBt.clicked.connect(lambda : _FillDataFromDialog(
         dialog,
         fields,
         screenCapSel.currentText() if allowScreenCapSel else "Window").close())
    return buttonLayout

def _GetSendMailInfo(usdviewApi, allowScreenCapSel):
    '''This method takes the generated dialog, runs it and returns the
       inputted information upon the dialogs completion'''
    window = usdviewApi.qMainWindow
    dialog = QtWidgets.QDialog(window)
    dialog.setWindowTitle("Send Screenshot...")

    # set field defaults
    dialog.emailInfo = _GenerateDefaultInfo(usdviewApi, dialog)
    dialog.setMinimumWidth((window.size().width()/2))

    # add layout to dialog and launch
    dialog.setLayout(_GenerateLayout(dialog, allowScreenCapSel))
    dialog.exec_()

    return dialog.emailInfo

def _GenerateDefaultInfo(usdviewApi, dialog):
    '''This method generates default information for the sender and
       bodyfields of the sendmail dialog. For the sender, it calls the
       _GetSenderAddress function. For the body, it generates some
       information based on the current UsdStage.'''
    from time import strftime, gmtime
    import os

    currtime = strftime("%b %d, %Y, %H:%M:%S", gmtime())

    # if a user is defined, we'll use that
    sender = _GetSenderAddress()

    mainWindow = usdviewApi.qMainWindow
    camera = usdviewApi.currentGfCamera
    sendee = ""
    subject = 'Usdview Screenshot'
    body = str("Usdview screenshot, taken " + currtime + "\n" +
               "----------------------------------------" + "\n" +
               "File: "
                  + str(usdviewApi.stageIdentifier) + "\n" +
               "Selected Prim Paths: "
                  + ", ".join(map(str, usdviewApi.selectedPrims)) + "\n" +
               "Current Frame: "
                  + str(usdviewApi.frame) + "\n" +
               "Complexity: "
                  + str(usdviewApi.dataModel.viewSettings.complexity) + "\n" +
               "Camera Info:\n" +str(camera) + "\n" +
               "----------------------------------------" + "\n")

    return EmailInfo(sender, sendee, subject, body)

def _FillDataFromDialog(dialog, fields, imagetype):
    '''This method simply fills members of the dialog object'''
    dialog.emailInfo.sender = fields["sender"].text()
    dialog.emailInfo.sendee = fields["sendee"].text()
    dialog.emailInfo.subject = fields["subject"].text()
    dialog.emailInfo.body = fields["body"].toPlainText()
    dialog.emailInfo.imagetype = imagetype
    return dialog

def _ValidMailInfo(mailInfo):
    '''This method asserts that the fields are non empty.'''
    return (mailInfo.sender  and \
            mailInfo.sendee  and \
            mailInfo.subject and \
            mailInfo.body    )

#
# Copyright 2016 Pixar
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the trade
#    names, trademarks, service marks, or product names of the Licensor
#    and its affiliates, except as required to comply with Section 4(c) of
#    the License and to reproduce the content of the NOTICE file.
#
# You may obtain a copy of the Apache License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#
# Sendmail Plugin
#
# This module provides plug-in facilities for usdview to send emails containing
# captured screenshots from usdview.
#
# Use the `SendMail` function as the callback function for a Usdview command
# plugin. Optionally, modify `_GetSenderAddress` to auto-fill the sender's email
# address.

from pxr.Usdviewq.qt import QtWidgets, QtCore

def _GetSenderAddress():
    '''Returns an email address used to auto-fill the 'From' field.'''
    return ""

def SendMail(usdviewApi):
    '''This method creates a local SMTP server, runs the dialog
       and upon successful completion, sends the info to the intended
       recipient.'''
    import smtplib
    import tempfile
    from email.mime.text import MIMEText
    from email.mime.image import MIMEImage
    from email.mime.multipart import MIMEMultipart

    # create the image to send as a temporary file
    with tempfile.NamedTemporaryFile(suffix=".jpeg") as tempimagefile:
        windowShot = usdviewApi.GrabWindowShot()
        viewportShot = usdviewApi.GrabViewportShot()

        # If we can't grab a shot of the viewport (probably if --norender is
        # used) then only allow window shots.
        allowScreenCapSel = (viewportShot is not None)

        # fetch the info from the launched sendmail dialog
        mailInfo = _GetSendMailInfo(usdviewApi, allowScreenCapSel)

        if not _ValidMailInfo(mailInfo):
            return False

        # set the image based on user config
        if mailInfo.imagetype == "Window":
            imagedata = windowShot
        else:
            imagedata = viewportShot

        # verify we created an image correctly and could safely save it
        if not imagedata or not imagedata.save(tempimagefile.name):
            return False

        # construct the message and convert the body to MIMEText
        msg = MIMEMultipart()
        msg['Subject'] = mailInfo.subject
        msg['From'] = (mailInfo.sender.strip() if
                       mailInfo.sender.strip() else None)
        msg['To'] = ','.join(x.strip() for x in mailInfo.sendee.split(',') if
                             x.strip())

        # Either the from or to category was unsatisfactory, bail
        if msg['From'] is None or len(msg['To'].split(',')) == 0:
            return False

        msg.attach(MIMEText(mailInfo.body))
        # grab the image data from the file, convert it to
        # MIME and attach it to the message
        msg.attach(MIMEImage(tempimagefile.read(), _subtype="jpeg"))

        # launch smtp server on local and send mail
        server = smtplib.SMTP('localhost')

        # Note that pythons stmplib requires the second arg as a list here
        # even though msg['To'] requires a string.
        server.sendmail(msg['From'],
                        msg['To'].split(','),
                        msg.as_string())
        server.quit()

class EmailInfo():
    '''A class to represent necessary fields in an email'''
    def __init__(self='', sender='', sendee='', subject='', body='',
                 imagetype=''):
        self.sender = sender
        self.sendee = sendee
        self.subject = subject
        self.body = body
        self.imagetype = imagetype

def _GenerateLayout(dialog, allowScreenCapSel):
    '''This method generates the full dialog, containing the text fields,
       buttons and combobox for choosing the type of screenshot'''
    (fields, textLayout) = _GenerateTextFields(dialog)
    buttonLayout = _GenerateButtons(dialog, fields, allowScreenCapSel)

    # compose layouts
    layout = QtWidgets.QVBoxLayout()
    layout.addLayout(textLayout)
    layout.addLayout(buttonLayout)

    return layout

def _GenerateTextFields(dialog):
    '''This method generates the layout containing the text fields to
        be filled by the user, such as sender, sendee and message.
        Note that this takes a reference to the dialog because the buttons
        need to create a signal handler which uses the text field info'''
    # construct text layout
    senderLabel = QtWidgets.QLabel("From: ")
    sendeeLabel = QtWidgets.QLabel("To: ")
    subjectLabel = QtWidgets.QLabel("Subject: ")
    bodyLabel   = QtWidgets.QLabel("Message: ")

    senderFld = QtWidgets.QLineEdit(dialog.emailInfo.sender)
    sendeeFld = QtWidgets.QLineEdit(dialog.emailInfo.sendee)
    subjectFld = QtWidgets.QLineEdit(dialog.emailInfo.subject)
    bodyFld   = QtWidgets.QTextEdit()
    bodyFld.setText(dialog.emailInfo.body)

    senderLayout = QtWidgets.QHBoxLayout()
    senderLayout.addWidget(senderFld)

    sendeeLayout = QtWidgets.QHBoxLayout()
    sendeeLayout.addWidget(sendeeFld)

    subjectLayout = QtWidgets.QHBoxLayout()
    subjectLayout.addWidget(subjectFld)

    bodyLayout = QtWidgets.QHBoxLayout()
    bodyLayout.addWidget(bodyFld)

    fieldsLayout = QtWidgets.QVBoxLayout()
    fieldsLayout.addWidget(senderLabel)
    fieldsLayout.addLayout(senderLayout)
    fieldsLayout.addWidget(sendeeLabel)
    fieldsLayout.addLayout(sendeeLayout)
    fieldsLayout.addWidget(subjectLabel)
    fieldsLayout.addLayout(subjectLayout)
    fieldsLayout.addWidget(bodyLabel)
    fieldsLayout.addLayout(bodyLayout)

    return ({"sender" : senderFld,
            "sendee" : sendeeFld,
            "subject" :subjectFld,
            "body":    bodyFld},
            fieldsLayout)

def _GenerateButtons(dialog, fields, allowScreenCapSel):
    '''This method generates the buttons used in our sendmail
       dialog. This includes creating signal handlers which batch
       the inputted info for sending across the wire.'''

    # construct buttons
    cancelBt = QtWidgets.QPushButton("Cancel")
    acceptBt = QtWidgets.QPushButton("Send")

    # construct screen cap selector if there are multiple options available
    screenCapSel = None
    if allowScreenCapSel:
        screenCapSel = QtWidgets.QComboBox()
        screenCapSel.setSizeAdjustPolicy(
                QtWidgets.QComboBox.AdjustToContents)
        screenCapSel.addItem("Window")
        screenCapSel.addItem("Viewport")

    buttonLayout = QtWidgets.QHBoxLayout()
    buttonLayout.addStretch(0.5)
    if allowScreenCapSel:
        buttonLayout.addWidget(screenCapSel)
    buttonLayout.addWidget(cancelBt)
    buttonLayout.addWidget(acceptBt)

    # add a signal handler for the accept and close buttons
    # upon cancelling, simply close the dialog
    cancelBt.clicked.connect(lambda : dialog.close())

    # upon accepting, fill the data and close the dialog
    acceptBt.clicked.connect(lambda : _FillDataFromDialog(
         dialog,
         fields,
         screenCapSel.currentText() if allowScreenCapSel else "Window").close())
    return buttonLayout

def _GetSendMailInfo(usdviewApi, allowScreenCapSel):
    '''This method takes the generated dialog, runs it and returns the
       inputted information upon the dialogs completion'''
    window = usdviewApi.qMainWindow
    dialog = QtWidgets.QDialog(window)
    dialog.setWindowTitle("Send Screenshot...")

    # set field defaults
    dialog.emailInfo = _GenerateDefaultInfo(usdviewApi, dialog)
    dialog.setMinimumWidth((window.size().width()/2))

    # add layout to dialog and launch
    dialog.setLayout(_GenerateLayout(dialog, allowScreenCapSel))
    dialog.exec_()

    return dialog.emailInfo

def _GenerateDefaultInfo(usdviewApi, dialog):
    '''This method generates default information for the sender and
       bodyfields of the sendmail dialog. For the sender, it calls the
       _GetSenderAddress function. For the body, it generates some
       information based on the current UsdStage.'''
    from time import strftime, gmtime
    import os

    currtime = strftime("%b %d, %Y, %H:%M:%S", gmtime())

    # if a user is defined, we'll use that
    sender = _GetSenderAddress()

    mainWindow = usdviewApi.qMainWindow
    camera = usdviewApi.currentGfCamera
    sendee = ""
    subject = 'Usdview Screenshot'
    body = str("Usdview screenshot, taken " + currtime + "\n" +
               "----------------------------------------" + "\n" +
               "File: "
                  + str(usdviewApi.stageIdentifier) + "\n" +
               "Selected Prim Paths: "
                  + ", ".join(map(str, usdviewApi.selectedPrims)) + "\n" +
               "Current Frame: "
                  + str(usdviewApi.frame) + "\n" +
               "Complexity: "
                  + str(usdviewApi.dataModel.viewSettings.complexity) + "\n" +
               "Camera Info:\n" +str(camera) + "\n" +
               "----------------------------------------" + "\n")

    return EmailInfo(sender, sendee, subject, body)

def _FillDataFromDialog(dialog, fields, imagetype):
    '''This method simply fills members of the dialog object'''
    dialog.emailInfo.sender = fields["sender"].text()
    dialog.emailInfo.sendee = fields["sendee"].text()
    dialog.emailInfo.subject = fields["subject"].text()
    dialog.emailInfo.body = fields["body"].toPlainText()
    dialog.emailInfo.imagetype = imagetype
    return dialog

def _ValidMailInfo(mailInfo):
    '''This method asserts that the fields are non empty.'''
    return (mailInfo.sender  and \
            mailInfo.sendee  and \
            mailInfo.subject and \
            mailInfo.body    )

# # Copyright 2016 Pixar # # Licensed under the Apache License, Version 2.0 (the "Apache License") # with the following modification; you may not use this file except in # compliance with the Apache License and the following modification to it: # Section 6. Trademarks. is deleted and replaced with: # # 6. Trademarks. This License does not grant permission to use the trade #    names, trademarks, service marks, or product names of the Licensor #    and its affiliates, except as required to comply with Section 4(c) of #    the License and to reproduce the content of the NOTICE file. # # You may obtain a copy of the Apache License at # #     http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the Apache License with the above modification is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the Apache License for the specific # language governing permissions and limitations under the Apache License. # # Sendmail Plugin # # This module provides plug-in facilities for usdview to send emails containing # captured screenshots from usdview. # # Use the `SendMail` function as the callback function for a Usdview command # plugin. Optionally, modify `_GetSenderAddress` to auto-fill the sender's email # address. Returns an email address used to auto-fill the 'From' field. This method creates a local SMTP server, runs the dialog        and upon successful completion, sends the info to the intended        recipient. # create the image to send as a temporary file # If we can't grab a shot of the viewport (probably if --norender is # used) then only allow window shots. # fetch the info from the launched sendmail dialog # set the image based on user config # verify we created an image correctly and could safely save it # construct the message and convert the body to MIMEText # Either the from or to category was unsatisfactory, bail # grab the image data from the file, convert it to # MIME and attach it to the message # launch smtp server on local and send mail # Note that pythons stmplib requires the second arg as a list here # even though msg['To'] requires a string. A class to represent necessary fields in an email This method generates the full dialog, containing the text fields,        buttons and combobox for choosing the type of screenshot # compose layouts This method generates the layout containing the text fields to         be filled by the user, such as sender, sendee and message.         Note that this takes a reference to the dialog because the buttons         need to create a signal handler which uses the text field info # construct text layout This method generates the buttons used in our sendmail        dialog. This includes creating signal handlers which batch        the inputted info for sending across the wire. # construct buttons # construct screen cap selector if there are multiple options available # add a signal handler for the accept and close buttons # upon cancelling, simply close the dialog # upon accepting, fill the data and close the dialog This method takes the generated dialog, runs it and returns the        inputted information upon the dialogs completion # set field defaults # add layout to dialog and launch This method generates default information for the sender and        bodyfields of the sendmail dialog. For the sender, it calls the        _GetSenderAddress function. For the body, it generates some        information based on the current UsdStage. # if a user is defined, we'll use that This method simply fills members of the dialog object This method asserts that the fields are non empty.