/*
  Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>

  Based on KMail code by various authors (kmmsgpartdlg).

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

  This library 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 Library General Public
  License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to the
  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  02110-1301, USA.
*/

#include "attachmentpropertiesdialog.h"

#include "attachmentfrommimecontentjob.h"
#include "ui_attachmentpropertiesdialog.h"

#include <boost/shared_ptr.hpp>

#include <KAboutData>
#include <KComponentData>
#include <KDebug>
#include <KGlobal>
#include <KMimeType>

#include <kmime/kmime_content.h>
#include <kmime/kmime_headers.h>
#include <kmime/kmime_util.h>

using namespace KPIM;

class KPIM::AttachmentPropertiesDialog::Private
{
  public:
    Private( AttachmentPropertiesDialog *qq )
      : q( qq )
      , readOnly( false )
    {
    }

    void init( AttachmentPart::Ptr part, bool readOnly );
    void polishUi();
    void mimeTypeChanged( const QString &type ); // slot
    void populateEncodings();
    void populateMimeTypes();
    void populateWhatsThis();
    void loadFromPart();
    void saveToPart();

    AttachmentPropertiesDialog *const q;
    bool readOnly;
    AttachmentPart::Ptr part;
    Ui::AttachmentPropertiesDialog ui;
};

void AttachmentPropertiesDialog::Private::init( AttachmentPart::Ptr part, bool readOnly )
{
  this->readOnly = readOnly;
  this->part = part;

  QWidget *widget = new QWidget( q );
  q->setMainWidget( widget );
  ui.setupUi( widget );
  polishUi();
  q->setModal( true );
  q->showButtonSeparator( true );
  q->setHelp( QString::fromLatin1( "attachments" ) );

  loadFromPart();
}

void AttachmentPropertiesDialog::Private::polishUi()
{
  // Update the icon when the selected mime type changes.
  connect( ui.mimeType, SIGNAL(editTextChanged(QString)),
      q, SLOT(mimeTypeChanged(QString)) );

  // Tweak the dialog, depending on whether it is read-only or not.
  if( readOnly ) {
    ui.mimeType->setEditable( false );
    ui.name->setReadOnly( true );
    ui.description->setReadOnly( true );
    ui.encoding->setEnabled( false );
    ui.autoDisplay->setEnabled( false );
    ui.encrypt->setEnabled( false );
    ui.sign->setEnabled( false );

    q->setButtons( Ok|Help );
  } else {
    populateEncodings();
    populateMimeTypes();

    q->setButtons( Ok|Cancel|Help );
  }

  q->setDefaultButton( Ok );
  populateWhatsThis();
}

void AttachmentPropertiesDialog::Private::mimeTypeChanged( const QString &type )
{
  KMimeType::Ptr mimeType = KMimeType::mimeType( type, KMimeType::ResolveAliases );
  if( !mimeType.isNull() ) {
    ui.mimeIcon->setPixmap( KIconLoader::global()->loadMimeTypeIcon( mimeType->iconName(),
                            KIconLoader::Desktop ) );
  } else {
    ui.mimeIcon->setPixmap( DesktopIcon( QLatin1String("unknown") ) );
  }
}

void AttachmentPropertiesDialog::Private::populateWhatsThis()
{
  QString msg;

  // FIXME These are such a mess... Make them straightforward and pretty.

  msg = i18n( "<p>The <em>MIME type</em> of the file:</p>"
              "<p>Normally, you do not need to touch this setting, since the "
              "type of the file is automatically checked; but, sometimes, %1 "
              "may not detect the type correctly -- here is where you can fix "
              "that.</p>", KGlobal::mainComponent().aboutData()->programName() );
  ui.mimeType->setWhatsThis( msg );

  msg = i18n( "<p>The estimated size of the attachment:</p>"
              "<p>Note that, in an email message, a binary file encoded with "
              "base64 will take up four thirds the actual size of the file.</p>" );
  ui.size->setWhatsThis( msg );

  msg = i18n( "<p>The file name of the part:</p>"
              "<p>Although this defaults to the name of the attached file, "
              "it does not specify the file to be attached; rather, it "
              "suggests a file name to be used by the recipient's mail agent "
              "when saving the part to disk.</p>" );
  ui.name->setWhatsThis( msg );

  msg = i18n( "<p>A description of the part:</p>"
              "<p>This is just an informational description of the part, "
              "much like the Subject is for the whole message; most "
              "mail agents will show this information in their message "
              "previews alongside the attachment's icon.</p>" );
  ui.description->setWhatsThis( msg );

  msg = i18n( "<p>The transport encoding of this part:</p>"
              "<p>Normally, you do not need to change this, since %1 will use "
              "a decent default encoding, depending on the MIME type; yet, "
              "sometimes, you can significantly reduce the size of the "
              "resulting message, e.g. if a PostScript file does not contain "
              "binary data, but consists of pure text -- in this case, choosing "
              "\"quoted-printable\" over the default \"base64\" will save up "
              "to 25% in resulting message size.</p>",
              KGlobal::mainComponent().aboutData()->programName() );
  ui.encoding->setWhatsThis( msg );

  msg = i18n( "<p>Check this option if you want to suggest to the "
              "recipient the automatic (inline) display of this part in the "
              "message preview, instead of the default icon view;</p>"
              "<p>Technically, this is carried out by setting this part's "
              "<em>Content-Disposition</em> header field to \"inline\" "
              "instead of the default \"attachment\".</p>" );
  ui.autoDisplay->setWhatsThis( msg );

  msg = i18n( "<p>Check this option if you want this message part to be "
              "signed.</p>"
              "<p>The signature will be made with the key that you associated "
              "with the currently-selected identity.</p>" );
  ui.sign->setWhatsThis( msg );

  msg = i18n( "<p>Check this option if you want this message part to be "
              "encrypted.</p>"
              "<p>The part will be encrypted for the recipients of this "
              "message.</p>" );
  ui.encrypt->setWhatsThis( msg );
}

void AttachmentPropertiesDialog::Private::populateEncodings()
{
  using namespace KMime;
  using namespace KMime::Headers;

  ui.encoding->clear();
  ui.encoding->addItem( nameForEncoding( CE7Bit ), int( CE7Bit ) );
  ui.encoding->addItem( nameForEncoding( CE8Bit ), int( CE8Bit ) );
  ui.encoding->addItem( nameForEncoding( CEquPr ), int( CEquPr ) );
  ui.encoding->addItem( nameForEncoding( CEbase64 ), int( CEbase64 ) );

  // TODO 8bit should be disabled if it is disabled in Settings.
  // Also, if it's a message/* part, base64 and qp should be disabled.
  // But since this is a dialog for power users anyway, let them shoot
  // themselves in the foot.  (The AttachmentJob will fail when they
  // try to compose the message.)
}

void AttachmentPropertiesDialog::Private::populateMimeTypes()
{
  // TODO these are what the old KMMsgPartDialog used.
  // Should we use KMimeType::allMimeTypes()?

  QStringList list;
  list << QString::fromLatin1( "text/html" )
       << QString::fromLatin1( "text/plain" )
       << QString::fromLatin1( "image/gif" )
       << QString::fromLatin1( "image/jpeg" )
       << QString::fromLatin1( "image/png" )
       << QString::fromLatin1( "application/octet-stream" )
       << QString::fromLatin1( "application/x-gunzip" )
       << QString::fromLatin1( "application/zip" );
  ui.mimeType->addItems( list );
}

void AttachmentPropertiesDialog::Private::loadFromPart()
{
  Q_ASSERT( part );

  ui.mimeType->setCurrentItem( QLatin1String(part->mimeType()), true );
  ui.size->setText( KGlobal::locale()->formatByteSize( part->size() ) );
  ui.name->setText( part->name() );
  ui.description->setText( part->description() );
  ui.encoding->setCurrentIndex( int( part->encoding() ) );
  ui.autoDisplay->setChecked( part->isInline() );
  ui.encrypt->setChecked( part->isEncrypted() );
  ui.sign->setChecked( part->isSigned() );
}

void AttachmentPropertiesDialog::Private::saveToPart()
{
  Q_ASSERT( part );
  Q_ASSERT( !readOnly );

  part->setMimeType( ui.mimeType->currentText().toLatin1() );
  part->setName( ui.name->text() );
  // TODO what about fileName? Extra field??
  part->setDescription( ui.description->text() );
  part->setInline( ui.autoDisplay->isChecked() );
  part->setSigned( ui.sign->isChecked() );
  part->setEncrypted( ui.encrypt->isChecked() );

  if( ui.mimeType->currentText().startsWith( QLatin1String( "message" ) ) &&
      ui.encoding->itemData( ui.encoding->currentIndex() ) != KMime::Headers::CE7Bit &&
      ui.encoding->itemData( ui.encoding->currentIndex() ) != KMime::Headers::CE8Bit ) {
    kWarning() << "Encoding on message/rfc822 must be \"7bit\" or \"8bit\".";
  }
  part->setEncoding( KMime::Headers::contentEncoding(
                       ui.encoding->itemData( ui.encoding->currentIndex() ).toInt() ) );
}

AttachmentPropertiesDialog::AttachmentPropertiesDialog( AttachmentPart::Ptr part,
                                                        QWidget *parent, bool readOnly )
  : KDialog( parent )
  , d( new Private( this ) )
{
  d->init( part, readOnly );
}

AttachmentPropertiesDialog::AttachmentPropertiesDialog( const KMime::Content *content,
                                                        QWidget *parent, bool readOnly )
  : KDialog( parent )
  , d( new Private( this ) )
{
  if( !readOnly ) {
    kFatal() << "Dialog must be read-only if loading from a KMime::Content.";
  }

  AttachmentFromMimeContentJob *ajob = new AttachmentFromMimeContentJob( content, this );
  ajob->exec();
  if( ajob->error() ) {
    kError() << "AttachmentFromMimeContentJob failed.";
  }
  AttachmentPart::Ptr part = ajob->attachmentPart();
  d->init( part, readOnly );
}

AttachmentPropertiesDialog::~AttachmentPropertiesDialog()
{
  delete d;
}

KPIM::AttachmentPart::Ptr AttachmentPropertiesDialog::attachmentPart() const
{
  return d->part;
}

bool AttachmentPropertiesDialog::isEncryptEnabled() const
{
  return d->ui.encrypt->isEnabled();
}

void AttachmentPropertiesDialog::setEncryptEnabled( bool enabled )
{
  return d->ui.encrypt->setEnabled( enabled );
}

bool AttachmentPropertiesDialog::isSignEnabled() const
{
  return d->ui.sign->isEnabled();
}

void AttachmentPropertiesDialog::setSignEnabled( bool enabled )
{
  return d->ui.sign->setEnabled( enabled );
}

void AttachmentPropertiesDialog::accept()
{
  if( !d->readOnly ) {
    d->saveToPart();
  }
  KDialog::accept();
}

#include "attachmentpropertiesdialog.moc"
