/*  -*- c++ -*-
    Copyright 2008 Thomas McGuire <Thomas.McGuire@gmx.net>
    Copyright 2008 Edwin Schepers <yez@familieschepers.nl>
    Copyright 2004 Marc Mutz <mutz@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/


#include "signatureconfigurator.h"
#include "identity.h"

#include <kactioncollection.h>
#include <klocale.h>
#include <kdebug.h>
#include <kdialog.h>
#include <klineedit.h>
#include <kurlrequester.h>
#include <kshellcompletion.h>
#include <ktoolbar.h>
#include <krun.h>
#include <KComboBox>
#include <KStandardDirs>

#include <kpimtextedit/textedit.h>

#include <QCheckBox>
#include <QDir>
#include <QFileInfo>
#include <QLabel>
#include <QLayout>
#include <QMimeData>
#include <QTextEdit>

#include <QStackedWidget>

#include <QVBoxLayout>
#include <QHBoxLayout>

#include <assert.h>

using namespace KPIMIdentities;

namespace KPIMIdentities {

/**
   Private class that helps to provide binary compatibility between releases.
   @internal
  */
//@cond PRIVATE
class SignatureConfigurator::Private
{
  public:
    Private( SignatureConfigurator *parent );
    void init();

    SignatureConfigurator *q;
    bool inlinedHtml;
    QString imageLocation;
};
//@endcond

SignatureConfigurator::Private::Private( SignatureConfigurator *parent )
  :q( parent )
{
}

void SignatureConfigurator::Private::init()
{
  // tmp. vars:
  QLabel * label;
  QWidget * page;
  QHBoxLayout * hlay;
  QVBoxLayout * vlay;
  QVBoxLayout * page_vlay;

  vlay = new QVBoxLayout( q );
  vlay->setObjectName( "main layout" );
  vlay->setMargin( 0 );

  // "enable signatue" checkbox:
  q->mEnableCheck = new QCheckBox( i18n("&Enable signature"), q );
  q->mEnableCheck->setWhatsThis(
      i18n("Check this box if you want KMail to append a signature to mails "
            "written with this identity."));
  vlay->addWidget( q->mEnableCheck );

  // "obtain signature text from" combo and label:
  hlay = new QHBoxLayout(); // inherits spacing
  vlay->addLayout( hlay );
  q->mSourceCombo = new KComboBox( q );
  q->mSourceCombo->setEditable( false );
  q->mSourceCombo->setWhatsThis(
      i18n("Click on the widgets below to obtain help on the input methods."));
  q->mSourceCombo->setEnabled( false ); // since !mEnableCheck->isChecked()
  q->mSourceCombo->addItems( QStringList()
                  << i18nc("continuation of \"obtain signature text from\"",
                          "Input Field Below")
                  << i18nc("continuation of \"obtain signature text from\"",
                          "File")
                  << i18nc("continuation of \"obtain signature text from\"",
                          "Output of Command")
                  );
  label = new QLabel( i18n("Obtain signature &text from:"), q );
  label->setBuddy( q->mSourceCombo );
  label->setEnabled( false ); // since !mEnableCheck->isChecked()
  hlay->addWidget( label );
  hlay->addWidget( q->mSourceCombo, 1 );

  // widget stack that is controlled by the source combo:
  QStackedWidget * widgetStack = new QStackedWidget( q );
  widgetStack->setEnabled( false ); // since !mEnableCheck->isChecked()
  vlay->addWidget( widgetStack, 1 );
  q->connect( q->mSourceCombo, SIGNAL(currentIndexChanged(int)),
              widgetStack, SLOT(setCurrentIndex(int)) );
  q->connect( q->mSourceCombo, SIGNAL(highlighted(int)),
              widgetStack, SLOT(setCurrentIndex(int)) );
  // connects for the enabling of the widgets depending on
  // signatureEnabled:
  q->connect( q->mEnableCheck, SIGNAL(toggled(bool)),
              q->mSourceCombo, SLOT(setEnabled(bool)) );
  q->connect( q->mEnableCheck, SIGNAL(toggled(bool)),
              widgetStack, SLOT(setEnabled(bool)) );
  q->connect( q->mEnableCheck, SIGNAL(toggled(bool)),
              label, SLOT(setEnabled(bool)) );
  // The focus might be still in the widget that is disabled
  q->connect( q->mEnableCheck, SIGNAL(clicked()),
              q->mEnableCheck, SLOT(setFocus()) );

  int pageno = 0;
  // page 0: input field for direct entering:
  page = new QWidget( widgetStack );
  widgetStack->insertWidget( pageno, page );
  page_vlay = new QVBoxLayout( page );

#ifndef QT_NO_TOOLBAR
  q->mEditToolBar = new KToolBar( q );
  q->mEditToolBar->setToolButtonStyle( Qt::ToolButtonIconOnly );
  page_vlay->addWidget( q->mEditToolBar, 0 );

  q->mFormatToolBar = new KToolBar( q );
  q->mFormatToolBar->setToolButtonStyle( Qt::ToolButtonIconOnly );
  page_vlay->addWidget( q->mFormatToolBar, 1 );
#endif

  q->mTextEdit = new KPIMTextEdit::TextEdit( q );
  static_cast<KPIMTextEdit::TextEdit*>( q->mTextEdit )->enableImageActions();
  page_vlay->addWidget( q->mTextEdit, 2 );
  q->mTextEdit->setWhatsThis( i18n("Use this field to enter an arbitrary static signature."));
  // exclude SupportToPlainText.
  q->mTextEdit->setRichTextSupport( KRichTextWidget::FullTextFormattingSupport |
      KRichTextWidget::FullListSupport |
      KRichTextWidget::SupportAlignment |
      KRichTextWidget::SupportRuleLine |
      KRichTextWidget::SupportHyperlinks |
      KRichTextWidget::SupportFormatPainting );

  // Fill the toolbars.
  KActionCollection *actionCollection = new KActionCollection( q );
  q->mTextEdit->createActions( actionCollection );
#ifndef QT_NO_TOOLBAR
  q->mEditToolBar->addAction( actionCollection->action( "format_text_bold" ) );
  q->mEditToolBar->addAction( actionCollection->action( "format_text_italic" ) );
  q->mEditToolBar->addAction( actionCollection->action( "format_text_underline" ) );
  q->mEditToolBar->addAction( actionCollection->action( "format_text_strikeout" ) );
  q->mEditToolBar->addAction( actionCollection->action( "format_text_foreground_color" ) );
  q->mEditToolBar->addAction( actionCollection->action( "format_text_background_color" ) );
  q->mEditToolBar->addAction( actionCollection->action( "format_font_family" ) );
  q->mEditToolBar->addAction( actionCollection->action( "format_font_size" ) );

  q->mFormatToolBar->addAction( actionCollection->action( "format_list_style" ) );
  q->mFormatToolBar->addAction( actionCollection->action( "format_list_indent_more" ) );
  q->mFormatToolBar->addAction( actionCollection->action( "format_list_indent_less" ) );
  q->mFormatToolBar->addAction( actionCollection->action( "format_list_indent_less" ) );
  q->mFormatToolBar->addSeparator();

  q->mFormatToolBar->addAction( actionCollection->action( "format_align_left" ) );
  q->mFormatToolBar->addAction( actionCollection->action( "format_align_center" ) );
  q->mFormatToolBar->addAction( actionCollection->action( "format_align_right" ) );
  q->mFormatToolBar->addAction( actionCollection->action( "format_align_justify" ) );
  q->mFormatToolBar->addSeparator();

  q->mFormatToolBar->addAction( actionCollection->action( "insert_horizontal_rule" ) );
  q->mFormatToolBar->addAction( actionCollection->action( "manage_link" ) );
  q->mFormatToolBar->addAction( actionCollection->action( "format_painter" ) );

  q->mFormatToolBar->addSeparator();
  q->mFormatToolBar->addAction( actionCollection->action( "add_image" ) );
#endif

  hlay = new QHBoxLayout(); // inherits spacing
  page_vlay->addLayout( hlay );
  q->mHtmlCheck = new QCheckBox( i18n("&Use HTML"), page );
  q->connect( q->mHtmlCheck, SIGNAL(clicked()),
              q, SLOT(slotSetHtml()) );
  hlay->addWidget( q->mHtmlCheck );
  inlinedHtml = true;

  widgetStack->setCurrentIndex( 0 ); // since mSourceCombo->currentItem() == 0

  // page 1: "signature file" requester, label, "edit file" button:
  ++pageno;
  page = new QWidget( widgetStack );
  widgetStack->insertWidget( pageno, page ); // force sequential numbers (play safe)
  page_vlay = new QVBoxLayout( page );
  page_vlay->setMargin( 0 );
  hlay = new QHBoxLayout(); // inherits spacing
  page_vlay->addLayout( hlay );
  q->mFileRequester = new KUrlRequester( page );
  q->mFileRequester->setWhatsThis(
      i18n("Use this requester to specify a text file that contains your "
            "signature. It will be read every time you create a new mail or "
            "append a new signature."));
  label = new QLabel( i18n("S&pecify file:"), page );
  label->setBuddy( q->mFileRequester );
  hlay->addWidget( label );
  hlay->addWidget( q->mFileRequester, 1 );
  q->mFileRequester->button()->setAutoDefault( false );
  q->connect( q->mFileRequester, SIGNAL(textChanged(QString)),
              q, SLOT(slotEnableEditButton(QString)) );
  q->mEditButton = new QPushButton( i18n("Edit &File"), page );
  q->mEditButton->setWhatsThis( i18n("Opens the specified file in a text editor."));
  q->connect( q->mEditButton, SIGNAL(clicked()),
              q, SLOT(slotEdit()) );
  q->mEditButton->setAutoDefault( false );
  q->mEditButton->setEnabled( false ); // initially nothing to edit
  hlay->addWidget( q->mEditButton );
  page_vlay->addStretch( 1 ); // spacer

  // page 2: "signature command" requester and label:
  ++pageno;
  page = new QWidget( widgetStack );
  widgetStack->insertWidget( pageno,page );
  page_vlay = new QVBoxLayout( page  );
  page_vlay->setMargin( 0 );
  hlay = new QHBoxLayout(); // inherits spacing
  page_vlay->addLayout( hlay );
  q->mCommandEdit = new KLineEdit( page );
  q->mCommandEdit->setCompletionObject( new KShellCompletion() );
  q->mCommandEdit->setAutoDeleteCompletionObject( true );
  q->mCommandEdit->setWhatsThis(
      i18n("You can add an arbitrary command here, either with or without path "
            "depending on whether or not the command is in your Path. For every "
            "new mail, KMail will execute the command and use what it outputs (to "
            "standard output) as a signature. Usual commands for use with this "
            "mechanism are \"fortune\" or \"ksig -random\"."));
  label = new QLabel( i18n("S&pecify command:"), page );
  label->setBuddy( q->mCommandEdit );
  hlay->addWidget( label );
  hlay->addWidget( q->mCommandEdit, 1 );
  page_vlay->addStretch( 1 ); // spacer
}

  SignatureConfigurator::SignatureConfigurator( QWidget * parent )
    : QWidget( parent ), d( new Private( this ) )
  {
    d->init();
  }

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

  bool SignatureConfigurator::isSignatureEnabled() const
  {
    return mEnableCheck->isChecked();
  }

  void SignatureConfigurator::setSignatureEnabled( bool enable )
  {
    mEnableCheck->setChecked( enable );
  }

  Signature::Type SignatureConfigurator::signatureType() const
  {
    switch ( mSourceCombo->currentIndex() ) {
    case 0:  return Signature::Inlined;
    case 1:  return Signature::FromFile;
    case 2:  return Signature::FromCommand;
    default: return Signature::Disabled;
    }
  }

  void SignatureConfigurator::setSignatureType( Signature::Type type )
  {
    int idx = 0;
    switch( type ) {
    case Signature::Inlined:     idx = 0; break;
    case Signature::FromFile:    idx = 1; break;
    case Signature::FromCommand: idx = 2; break;
    default:                     idx = 0; break;
    };

    mSourceCombo->setCurrentIndex( idx );
  }

  void SignatureConfigurator::setInlineText( const QString & text )
  {
    mTextEdit->setTextOrHtml( text );
  }

  QString SignatureConfigurator::fileURL() const
  {
    QString file = mFileRequester->url().path();

    // Force the filename to be relative to ~ instead of $PWD depending
    // on the rest of the code (KRun::run in Edit and KFileItem on save)
    if ( !file.isEmpty() && QFileInfo( file ).isRelative() )
        file = QDir::home().absolutePath() + QDir::separator() + file;

    return file;
  }

  void SignatureConfigurator::setFileURL( const QString & url )
  {
    mFileRequester->setUrl( url );
  }

  QString SignatureConfigurator::commandURL() const
  {
    return mCommandEdit->text();
  }

  void SignatureConfigurator::setCommandURL( const QString & url )
  {
    mCommandEdit->setText( url );
  }


  Signature SignatureConfigurator::signature() const
  {
    Signature sig;
    const Signature::Type sigType = signatureType();
    switch ( sigType ) {
    case Signature::Inlined:
      sig.setInlinedHtml( d->inlinedHtml );
      sig.setText( d->inlinedHtml ? asCleanedHTML() : mTextEdit->textOrHtml() );
      if ( d->inlinedHtml ) {
        if ( !d->imageLocation.isEmpty() )
          sig.setImageLocation( d->imageLocation );
        KPIMTextEdit::ImageWithNameList images = static_cast< KPIMTextEdit::TextEdit*>( mTextEdit )->imagesWithName();
        foreach( const KPIMTextEdit::ImageWithNamePtr &image, images ) {
          sig.addImage( image->image, image->name );
        }
      }
      break;
    case Signature::FromCommand:
      sig.setUrl( commandURL(), true );
      break;
    case Signature::FromFile:
      sig.setUrl( fileURL(), false );
      break;
    case Signature::Disabled:
      /* do nothing */
      break;
    }
    sig.setEnabledSignature(isSignatureEnabled());
    sig.setType( sigType );
    return sig;
  }

  void SignatureConfigurator::setSignature( const Signature & sig )
  {
    setSignatureType( sig.type() );
    setSignatureEnabled( sig.isEnabledSignature() );

    if ( sig.isInlinedHtml() )
      mHtmlCheck->setCheckState( Qt::Checked );
    else
      mHtmlCheck->setCheckState( Qt::Unchecked );
    slotSetHtml();

    // Let insertIntoTextEdit() handle setting the text, as that function also adds the images.
    mTextEdit->clear();
    KPIMTextEdit::TextEdit * const pimEdit = static_cast<KPIMTextEdit::TextEdit*>( mTextEdit );
    sig.insertIntoTextEdit( KPIMIdentities::Signature::Start, KPIMIdentities::Signature::AddNothing,
                            pimEdit,true );
    if ( sig.type() == Signature::FromFile )
      setFileURL( sig.url() );
    else
      setFileURL( QString() );

    if ( sig.type() == Signature::FromCommand )
      setCommandURL( sig.url() );
    else
      setCommandURL( QString() );
  }

  void SignatureConfigurator::slotEnableEditButton( const QString & url )
  {
    mEditButton->setDisabled( url.trimmed().isEmpty() );
  }

  void SignatureConfigurator::slotEdit()
  {
    QString url = fileURL();
    // slotEnableEditButton should prevent this assert from being hit:
    assert( !url.isEmpty() );

    (void)KRun::runUrl( KUrl( url ), QString::fromLatin1("text/plain"), this );
  }

  QString SignatureConfigurator::asCleanedHTML() const
  {
    QString text = mTextEdit->toHtml();

    // Beautiful little hack to find the html headers produced by Qt.
    QTextDocument textDocument;
    QString html = textDocument.toHtml();

    // Now remove each line from the text, the result is clean html.
    foreach( const QString& line, html.split( '\n' ) ){
        text.remove( line + '\n' );
    }
    return text;
  }

  // "use HTML"-checkbox (un)checked
  void SignatureConfigurator::slotSetHtml()
  {
    if ( mHtmlCheck->checkState() == Qt::Unchecked ) {
      mHtmlCheck->setText( i18n("&Use HTML") );
#ifndef QT_NO_TOOLBAR
      mEditToolBar->setVisible( false );
      mEditToolBar->setEnabled( false );
      mFormatToolBar->setVisible( false );
      mFormatToolBar->setEnabled( false );
#endif
      mTextEdit->switchToPlainText();
      d->inlinedHtml = false;
    }
    else {
      mHtmlCheck->setText( i18n("&Use HTML (disabling removes formatting)") );
      d->inlinedHtml = true;
#ifndef QT_NO_TOOLBAR
      mEditToolBar->setVisible( true );
      mEditToolBar->setEnabled( true );
      mFormatToolBar->setVisible( true );
      mFormatToolBar->setEnabled( true );
#endif
      mTextEdit->enableRichTextMode();
    }
  }

  void SignatureConfigurator::setImageLocation ( const QString& path )
  {
    d->imageLocation = path;
  }

  void SignatureConfigurator::setImageLocation( const Identity &identity )
  {
    const QString dir = QString( "emailidentities/%1/" ).arg(
        QString::number( identity.uoid() ) );
    setImageLocation( KStandardDirs::locateLocal( "data", dir ) );
  }

}

#include "signatureconfigurator.moc"
