/*
  This file is part of KTnef.

  Copyright (C) 2002 Michael Goffioul <kdeprint@swing.be>
  Copyright (c) 2012 Allen Winter <winter@kde.org>

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

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

#include "ktnefmain.h"
#include "attachpropertydialog.h"
#include "ktnefview.h"
#include "messagepropertydialog.h"

#include <KTNEF/KTNEFAttach>
#include <KTNEF/KTNEFMessage>
#include <KTNEF/KTNEFParser>
#include <KTNEF/KTNEFProperty>

#include <KAction>
#include <KActionCollection>
#include <KApplication>
#include <KDebug>
#include <KDialog>
#include <KEditToolBar>
#include <KFileDialog>
#include <KGlobal>
#include <KIconLoader>
#include <KLocalizedString>
#include <KMenu>
#include <KMessageBox>
#include <KProcess>
#include <KRun>
#include <KShortcutsDialog>
#include <KStandardAction>
#include <KStandardDirs>
#include <KStatusBar>
#include <KTemporaryFile>
#include <KRecentFilesAction>

#include <QContextMenuEvent>
#include <QDir>
#include <QDrag>
#include <QMimeData>
#include <QPixmap>

KTNEFMain::KTNEFMain( QWidget *parent )
  : KXmlGuiWindow( parent )
{
  setupActions();
  setupStatusbar();

  setupTNEF();

  KConfigGroup config( KGlobal::config(), "Settings" );
  mDefaultDir = config.readPathEntry( "defaultdir", QLatin1String("/tmp/") );

  config = KConfigGroup( KGlobal::config(), "Recent Files" );
  mOpenRecentFileAction->loadEntries(config);

  mLastDir = mDefaultDir;

  // create personal temp extract dir
  KStandardDirs::makeDir( KGlobal::dirs()->localkdedir() + QLatin1String("/share/apps/ktnef/tmp") );

  resize( 430, 350 );

  setStandardToolBarMenuEnabled( true );

  createStandardStatusBarAction();

  setupGUI( Keys | Save | Create, QLatin1String("ktnefui.rc") );

  setAutoSaveSettings();
}

KTNEFMain::~KTNEFMain()
{
  delete mParser;
  cleanup();
}

void KTNEFMain::setupActions()
{
  KStandardAction::quit( this, SLOT(close()), actionCollection() );

  KAction *action =
    KStandardAction::keyBindings( this, SLOT(slotConfigureKeys()), actionCollection() );
  action->setWhatsThis(
    i18nc( "@info:whatsthis",
           "You will be presented with a dialog where you can configure "
           "the application-wide shortcuts." ) );

  KStandardAction::configureToolbars( this, SLOT(slotEditToolbars()), actionCollection() );

  // File menu
  KStandardAction::open( this, SLOT(openFile()), actionCollection() );

  mOpenRecentFileAction = KStandardAction::openRecent(this, SLOT(openRecentFile(KUrl)), actionCollection());

  // Action menu
  KAction *openAction = actionCollection()->addAction( QLatin1String("view_file") );
  openAction->setText( i18nc( "@action:inmenu", "View" ) );
  openAction->setIcon( KIcon( QLatin1String("document-open") ) );
  connect( openAction, SIGNAL(triggered()), this, SLOT(viewFile()) );

  KAction *openAsAction = actionCollection()->addAction( QLatin1String("view_file_as") );
  openAsAction->setText( i18nc( "@action:inmenu", "View With..." ) );
  connect( openAsAction, SIGNAL(triggered()), this, SLOT(viewFileAs()) );

  KAction *extractAction = actionCollection()->addAction( QLatin1String("extract_file") );
  extractAction->setText( i18nc( "@action:inmenu", "Extract" ) );
  connect( extractAction, SIGNAL(triggered()), this, SLOT(extractFile()) );

  KAction *extractToAction = actionCollection()->addAction( QLatin1String("extract_file_to") );
  extractToAction->setText( i18nc( "@action:inmenu", "Extract To..." ) );
  extractToAction->setIcon( KIcon( QLatin1String("archive-extract") ) );
  connect( extractToAction, SIGNAL(triggered()), this, SLOT(extractFileTo()) );

  KAction *extractAllToAction = actionCollection()->addAction( QLatin1String("extract_all_files") );
  extractAllToAction->setText( i18nc( "@action:inmenu", "Extract All To..." ) );
  extractAllToAction->setIcon( KIcon( QLatin1String("archive-extract") ) );
  connect( extractAllToAction, SIGNAL(triggered()), this, SLOT(extractAllFiles()) );

  KAction *filePropsAction = actionCollection()->addAction( QLatin1String("properties_file") );
  filePropsAction->setText( i18nc( "@action:inmenu", "Properties" ) );
  filePropsAction->setIcon( KIcon( QLatin1String("document-properties") ) );
  connect( filePropsAction, SIGNAL(triggered()), this, SLOT(propertiesFile()));

  KAction *messPropsAction = actionCollection()->addAction( QLatin1String("msg_properties") );
  messPropsAction->setText( i18nc( "@action:inmenu", "Message Properties" ) );
  connect( messPropsAction, SIGNAL(triggered()), this, SLOT(slotShowMessageProperties()) );

  KAction *messShowAction = actionCollection()->addAction( QLatin1String("msg_text") );
  messShowAction->setText( i18nc( "@action:inmenu", "Show Message Text" ) );
  messShowAction->setIcon( KIcon( QLatin1String("document-preview-archive") ) );
  connect( messShowAction, SIGNAL(triggered()), this, SLOT(slotShowMessageText()) );

  KAction *messSaveAction = actionCollection()->addAction( QLatin1String("msg_save") );
  messSaveAction->setText( i18nc( "@action:inmenu", "Save Message Text As..." ) );
  messSaveAction->setIcon( KIcon( QLatin1String("document-save") ) );
  connect( messSaveAction, SIGNAL(triggered()), this, SLOT(slotSaveMessageText()) );

  actionCollection()->action( QLatin1String("view_file") )->setEnabled( false );
  actionCollection()->action( QLatin1String("view_file_as") )->setEnabled( false );
  actionCollection()->action( QLatin1String("extract_file") )->setEnabled( false );
  actionCollection()->action( QLatin1String("extract_file_to") )->setEnabled( false );
  actionCollection()->action( QLatin1String("extract_all_files") )->setEnabled( false );
  actionCollection()->action( QLatin1String("properties_file") )->setEnabled( false );

  // Options menu
  KAction *defFolderAction = actionCollection()->addAction( QLatin1String("options_default_dir") );
  defFolderAction->setText( i18nc( "@action:inmenu", "Default Folder..." ) );
  defFolderAction->setIcon( KIcon( QLatin1String("folder-open") ) );
  connect( defFolderAction, SIGNAL(triggered()), this, SLOT(optionDefaultDir()) );

}

void KTNEFMain::slotConfigureKeys()
{
  KShortcutsDialog::configure( actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this );
}

void KTNEFMain::setupStatusbar()
{
  statusBar()->insertItem( i18nc( "@info:status", "100 attachments found" ), 0 );
  statusBar()->changeItem( i18nc( "@info:status", "No file loaded" ), 0 );
}

void KTNEFMain::setupTNEF()
{
  mView = new KTNEFView( this );
  mView->setAllColumnsShowFocus( true );
  mParser = new KTNEFParser;

  setCentralWidget(mView);

  connect( mView, SIGNAL(itemSelectionChanged()),
           SLOT(viewSelectionChanged()) );

  connect( mView, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
           SLOT(viewDoubleClicked(QTreeWidgetItem*)) );

//PORTME:  connect( mView, SIGNAL(dragRequested(QList<KTNEFAttach*>)),
//PORTME:           SLOT(viewDragRequested(QList<KTNEFAttach*>)) );
}

void KTNEFMain::loadFile( const QString &filename )
{
  mFilename = filename;
  setCaption(mFilename);
  if ( !mParser->openFile( filename ) ) {
    KMessageBox::error(
      this,
      i18nc( "@info",
             "Unable to open file \"%1\".", filename ) );
    mView->setAttachments( QList<KTNEFAttach *>() );
    enableExtractAll( false );
  } else {
    addRecentFile(KUrl(filename));
    QList<KTNEFAttach *> list = mParser->message()->attachmentList();
    QString msg;
    msg = i18ncp( "@info:status",
                  "%1 attachment found", "%1 attachments found", list.count() );
    statusBar()->changeItem( msg, 0 );
    mView->setAttachments( list );
    enableExtractAll( ( list.count() > 0 ) );
    enableSingleAction( false );
  }
}

void KTNEFMain::openFile()
{
  QString filename =
    KFileDialog::getOpenFileName(
      KUrl(),
      QString(),
      this,
      i18nc( "@title:window", "Open TNEF File" ) );
  if ( !filename.isEmpty() ) {
    loadFile( filename );
  }
}

void KTNEFMain::openRecentFile(const KUrl &url)
{
    loadFile(url.path());
}

void KTNEFMain::addRecentFile(const KUrl &url)
{
    mOpenRecentFileAction->addUrl(url);
    KConfigGroup config( KGlobal::config(), "Recent Files" );
    mOpenRecentFileAction->saveEntries(config);
    config.sync();
}

void KTNEFMain::viewFile()
{
  if ( !mView->getSelection().isEmpty() ) {
    KTNEFAttach *attach = mView->getSelection().first();
    KUrl url( QLatin1String("file:") + extractTemp( attach ) );
    QString mimename( attach->mimeTag() );

    if ( mimename.isEmpty() || mimename == QLatin1String("application/octet-stream") ) {
      kDebug() << "No mime type found in attachment object, trying to guess...";
      mimename = KMimeType::findByUrl( url, 0, true )->name();
      kDebug() << "Detected mime type: " << mimename;
    } else {
      kDebug() << "Mime type from attachment object: " << mimename;
    }

    KRun::runUrl( url, mimename, this, true );
  } else {
    KMessageBox::information(
      this,
      i18nc( "@info",
             "There is no file selected. Please select a file an try again." ) );
  }

}

QString KTNEFMain::extractTemp( KTNEFAttach *att )
{
  QString dir = KGlobal::dirs()->localkdedir() + QLatin1String("/share/apps/ktnef/tmp/");
  mParser->extractFileTo( att->name(), dir );
  QString filename = att->fileName();
  // falling back to internal TNEF attachement name if no filename is given for the attached file
  // this follows the logic of KTNEFParser::extractFileTo(...)
  if ( filename.isEmpty() ) {
    filename = att->name();
  }
  dir.append( filename );
  return dir;
}

void KTNEFMain::viewFileAs()
{
  if ( !mView->getSelection().isEmpty() ) {
    KUrl::List list;
    list.append( KUrl( extractTemp( mView->getSelection().first() ) ) );

    if ( !list.isEmpty() ) {
      KRun::displayOpenWithDialog( list, this );
    }
  } else {
    KMessageBox::information(
      this,
      i18nc( "@info",
             "There is no file selected. Please select a file an try again." ) );
  }
}

void KTNEFMain::extractFile()
{
  extractTo( mDefaultDir );
}

void KTNEFMain::extractFileTo()
{
  QString dir = KFileDialog::getExistingDirectory( mLastDir, this );
  if ( !dir.isEmpty() ) {
    extractTo( dir );
    mLastDir = dir;
  }
}

void KTNEFMain::extractAllFiles()
{
  QString dir = KFileDialog::getExistingDirectory( mLastDir, this );
  if ( !dir.isEmpty() ) {
    mLastDir = dir;
    dir.append( QLatin1String("/") );
    QList<KTNEFAttach *> list = mParser->message()->attachmentList();
    QList<KTNEFAttach *>::ConstIterator it;
    QList<KTNEFAttach *>::ConstIterator end( list.constEnd() );
    for ( it = list.constBegin(); it != end; ++it ) {
      if ( !mParser->extractFileTo( (*it)->name(), dir ) ) {
        KMessageBox::error(
          this,
          i18nc( "@info",
                 "Unable to extract file \"%1\".", (*it)->name() ) );
        return;
      }
    }
  }
}

void KTNEFMain::propertiesFile()
{
  KTNEFAttach *attach = mView->getSelection().first();
  AttachPropertyDialog dlg( this );
  dlg.setAttachment( attach );
  dlg.exec();
}

void KTNEFMain::optionDefaultDir()
{
  const QString dirname = KFileDialog::getExistingDirectory( mDefaultDir, this );
  if ( !dirname.isEmpty() ) {
    mDefaultDir = dirname;

    KConfigGroup config( KGlobal::config(), "Settings" );
    config.writePathEntry( "defaultdir", mDefaultDir );
  }
}

void KTNEFMain::viewSelectionChanged()
{
  const QList<KTNEFAttach *> list = mView->getSelection();
  const int nbItem = list.count();
  const bool on1 = ( nbItem == 1 );
  const bool on2 = ( nbItem > 0 );

  actionCollection()->action( QLatin1String("view_file") )->setEnabled( on1 );
  actionCollection()->action( QLatin1String("view_file_as") )->setEnabled( on1 );
  actionCollection()->action( QLatin1String("properties_file") )->setEnabled( on1 );

  actionCollection()->action( QLatin1String("extract_file") )->setEnabled( on2 );
  actionCollection()->action( QLatin1String("extract_file_to") )->setEnabled( on2 );
}

void KTNEFMain::enableExtractAll( bool on )
{
  if ( !on ) {
    enableSingleAction( false );
  }

  actionCollection()->action( QLatin1String("extract_all_files") )->setEnabled( on );
}

void KTNEFMain::enableSingleAction( bool on )
{
  actionCollection()->action( QLatin1String("extract_file") )->setEnabled( on );
  actionCollection()->action( QLatin1String("extract_file_to") )->setEnabled( on );
  actionCollection()->action( QLatin1String("view_file") )->setEnabled( on );
  actionCollection()->action( QLatin1String("view_file_as") )->setEnabled( on );
  actionCollection()->action( QLatin1String("properties_file") )->setEnabled( on );
}

void KTNEFMain::cleanup()
{
  QDir d( KGlobal::dirs()->localkdedir() + QLatin1String("/share/apps/ktnef/tmp/") );
  QFileInfoList list = d.entryInfoList( QDir::Files | QDir::Hidden, QDir::Unsorted );
  QFileInfoList::const_iterator it;
  QFileInfoList::const_iterator end(list.constEnd());
  for ( it = list.constBegin(); it != end; ++it ) {
    d.remove( it->absoluteFilePath() );
  }
}

void KTNEFMain::extractTo( const QString &dirname )
{
  QString dir = dirname;
  if ( dir.right(1) != QLatin1String("/") ) {
    dir.append( QLatin1String("/") );
  }
  QList<KTNEFAttach *>list = mView->getSelection();
  QList<KTNEFAttach *>::ConstIterator it;
  QList<KTNEFAttach *>::ConstIterator end( list.constEnd() );
  for ( it = list.constBegin(); it != end; ++it ) {
    if ( !mParser->extractFileTo( (*it)->name(), dir ) ) {
      KMessageBox::error(
        this,
        i18nc( "@info",
               "Unable to extract file \"%1\".", (*it)->name() ) );
      return;
    }
  }
}

/* This breaks the saveMainWindowSettings stuff....
  void KTNEFMain::closeEvent(QCloseEvent *e)
{
  e->accept();
}*/

void KTNEFMain::contextMenuEvent( QContextMenuEvent *event )
{
  QList<KTNEFAttach *> list = mView->getSelection();
  if ( !list.count() ) {
    return;
  }

  QAction *view = 0;
  QAction *viewWith = 0;
  QAction *prop = 0;
  KMenu *menu = new KMenu();
  if ( list.count() == 1 ) {
    view = menu->addAction( KIcon( QLatin1String("document-open") ),
                            i18nc( "@action:inmenu", "View" ) );
    viewWith = menu->addAction( i18nc( "@action:inmenu", "View With..." ) );
    menu->addSeparator();
  }
  QAction *extract = menu->addAction( i18nc( "@action:inmenu", "Extract" ) );
  QAction *extractTo = menu->addAction( KIcon( QLatin1String("archive-extract") ),
                                        i18nc( "@action:inmenu", "Extract To..." ) );
  if ( list.count() == 1 ) {
    menu->addSeparator();
   prop = menu->addAction( KIcon( QLatin1String("document-properties") ),
                           i18nc( "@action:inmenu", "Properties" ) );
  }

  QAction *a = menu->exec( event->globalPos(), 0 );
  if ( a ) {
    if ( a == view ) {
      viewFile();
    } else if ( a == viewWith ) {
      viewFileAs();
    } else if ( a == extract ) {
      extractFile();
    } else if ( a == extractTo ) {
      extractFileTo();
    } else if ( a == prop ) {
      propertiesFile();
    }
  }
  delete menu;
}

void KTNEFMain::viewDoubleClicked( QTreeWidgetItem *item )
{
  if ( item && item->isSelected() ) {
    viewFile();
  }
}

void KTNEFMain::viewDragRequested( const QList<KTNEFAttach *>& list )
{
  KUrl::List urlList;
  QList<KTNEFAttach *>::ConstIterator end( list.constEnd() );
  for ( QList<KTNEFAttach *>::ConstIterator it = list.constBegin();
        it != end; ++it ) {
    urlList << KUrl( extractTemp( *it ) );
  }

  if ( !list.isEmpty() ) {
    QMimeData *mimeData = new QMimeData;
    urlList.populateMimeData( mimeData );

    QDrag *drag = new QDrag( this );
    drag->setMimeData( mimeData );
  }
}

void KTNEFMain::slotEditToolbars()
{
  saveMainWindowSettings( KGlobal::config()->group( "MainWindow" ) );

  KEditToolBar dlg( factory() );
  connect( &dlg, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()) );
  dlg.exec();
}

void KTNEFMain::slotNewToolbarConfig()
{
  createGUI( QLatin1String("ktnefui.rc") );
  applyMainWindowSettings( KGlobal::config()->group( "MainWindow" ) );
}

void KTNEFMain::slotShowMessageProperties()
{
  MessagePropertyDialog dlg( this, mParser->message() );
  dlg.exec();
}

void KTNEFMain::slotShowMessageText()
{
  if ( !mParser->message() ) {
    return;
  }

  QString rtf = mParser->message()->rtfString();
  if ( !rtf.isEmpty() ) {
    KTemporaryFile *tmpFile = new KTemporaryFile();
    tmpFile->setPrefix( KGlobal::dirs()->localkdedir() + QLatin1String("/share/apps/ktnef/tmp/") );
    tmpFile->setSuffix( QLatin1String( ".rtf" ) );
    tmpFile->open();
    tmpFile->setPermissions( QFile::ReadUser );
    tmpFile->write( rtf.toLocal8Bit() );
    tmpFile->close();

    KRun::runUrl( KUrl( tmpFile->fileName() ), QLatin1String("text/rtf"), this, true );
    delete tmpFile;
  } else {
      KMessageBox::error(
        this,
        i18nc( "@info",
               "The message does not contain any Rich Text data." ) );
  }
}

void KTNEFMain::slotSaveMessageText()
{
  if ( !mParser->message() ) {
    return;
  }

  QString rtf = mParser->message()->rtfString();
  QString filename = KFileDialog::getSaveFileName( QString(), QString(), this, QString(), KFileDialog::ConfirmOverwrite );
  if ( !filename.isEmpty() ) {
    QFile f( filename );
    if ( f.open( QIODevice::WriteOnly ) ) {
      QTextStream t( &f );
      t << rtf;
    } else {
      KMessageBox::error(
        this,
        i18nc( "@info",
               "Unable to open file \"%1\" for writing, check file permissions.", filename ) );
    }
  }
}

