/***************************************************************************
                          kshowmail.cpp  -  description
                             -------------------
    begin                : Sat May  6 12:13:57 MEST 2000
    copyright            : (C) 2000-2002 by Eggert Ehmke
    email                : eggert.ehmke@berlin.de

    26 Sep 2002 - Allow for columns to be hidden. Allistar Melville
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "kshowmail.h"

using namespace TDEIO;

TDEAboutData* KShowMailApp::m_pAbout;

KShowMailApp::KShowMailApp()
{
  //set up actions
  initActions();

  // create GUI from ui resource file
  setXMLFile( "kshowmailui.rc", true );
  createGUI();

  //the main window settings will be saved automatically
  setAutoSaveSettings();

  //initiate GUI parts
  initStatusBar();
  initDocument();
  initView();

  //the application is idle at beginning
  m_state = idle;

  //the setup dialog will be created in slotSetup() at first use
  SetupDialog = NULL;

  m_pTimer = new TQTimer (this);
  m_pAlertDialog = NULL;
  m_pDockWindow = new KShowMailDock (this, m_actionRefresh);
  m_bForceClose = false;


  //read the setup
  m_ConfigList.refreshSetup( m_pView->m_pListAccounts );

  //load stored mails
  m_ConfigList.readStoredMails();

  //connect signals with slots

  connect (m_pDockWindow, SIGNAL (quitSelected ()), SLOT (slotForceClose ()));
  connect (m_pTimer, SIGNAL (timeout ()), SLOT (slotRefresh ()));
  connect (m_pView->m_pListAccounts, SIGNAL (currentChanged(TQListViewItem*)), SLOT(slotAccountActivated(TQListViewItem*)));
  connect (m_pView, SIGNAL (signalActiveChanged()), SLOT (slotRefreshView()));

  connect( &m_ConfigList, SIGNAL( sigDeleteReady() ), this, SLOT( slotDeletionReady() ) );
  connect( &m_ConfigList, SIGNAL( sigShowBodiesReady() ), this, SLOT( slotShowMessageReady() ) );
  connect( &m_ConfigList, SIGNAL( sigMessageWindowOpened() ), this, SLOT( slotNormalCursor() ) );
  connect( &m_ConfigList, SIGNAL( sigAllMessageWindowsClosed() ), this, SLOT( slotWaitingCursor() ) );
  connect( &m_ConfigList, SIGNAL( sigRefreshReady() ), this, SLOT( slotRefreshReady() ) );

  //show welcome message
  KMessageBox::information( NULL, i18n( "Thank You for using KShowmail.\nPlease use the feedback dialog to tell us your experience with this program." ), i18n("Welcome"), "WelcomeMessage" );

  // this starts the one-second-interval timer
  // this is a TQObject method
  startTimer (1000);

  //we are ready to take off! Lets initiate the first refresh (if desired)
  initFirstRefresh();
}

KShowMailApp::~KShowMailApp()
{
  kdDebug() << "cleaning up" << endl;
  delete m_pTimer;
  delete m_pDockWindow;
  kdDebug() << "cleanup finished" << endl;
}

/**
  * This is reimplemented from TQObject
  */
void KShowMailApp::timerEvent (TQTimerEvent *)
{
  if (m_nSecondsToGo)
  {
    TQTime time;
    time = time.addSecs (m_nSecondsToGo--);
    TQCString msg (i18n("Autorefresh: %1").arg(time.toString()).utf8());
    statusBar()->changeItem( msg, STATUSBAR_FIELD_NEXT_REFRESH );
  }
  else
    statusBar()->changeItem( "", STATUSBAR_FIELD_NEXT_REFRESH );

  // if busy, animate the traybar
  if ( m_state == refreshing || m_state == deleting )
  {
    static int color = 0;
    color = (color+1)%2;
    switch (color)
    {
      case 0:
        m_pDockWindow->clear ();
        break;
      default:
        m_pDockWindow->drawText ("?", TQt::red);
    }
  }
}



void KShowMailApp::initActions ()
{
  // file menu
  KStdAction::quit( this, SLOT( slotFileQuit() ), actionCollection() );

  // action menu
  m_actionRefresh = new TDEAction( i18n( "&Refresh messages" ), "reload", Key_F5, this, SLOT( slotRefresh() ), actionCollection(), "refresh" );
  new TDEAction( i18n( "Show &header of highlighted messages" ), "letter-closed", Key_F2, this, SLOT( slotShowHeader() ), actionCollection(), "show_header" );
  new TDEAction( i18n( "Show &complete highlighted messages" ), "letter-open", Key_F3, this, SLOT( slotShowMessage() ), actionCollection(), "show_complete" );
  new TDEAction( i18n( "&Delete highlighted messages" ), "eraser", Key_Delete, this, SLOT( slotDelete() ), actionCollection(), "delete" );
  new TDEAction( i18n( "S&top current transfer" ), "process-stop", 0, this, SLOT( slotStop() ), actionCollection(), "stop" );
  new TDEAction( i18n( "Show Filter Log" ), "text-x-log", Key_F4, this, SLOT( slotShowFilterLog() ), actionCollection(), "show_filterlog" );
  new TDEAction( i18n( "Add sender to whitelist" ), "add_user", 0, this, SLOT( slotAddToWhitelist() ), actionCollection(), "addWhitelist" );
  new TDEAction( i18n( "Add sender to blacklist" ), "delete_user", 0, this, SLOT( slotAddToBlacklist() ), actionCollection(), "addBlacklist" );

  // setup menu
  KStdAction::preferences( this, SLOT( slotSetup() ), actionCollection() );
  KStdAction::configureToolbars( this, SLOT( slotEditToolbars() ), actionCollection() );

  //create menu items for toggle tool and status bar
  setStandardToolBarMenuEnabled( true );
  createStandardStatusBarAction();

  //help menu
  new TDEAction( i18n( "Send &Feedback Mail" ), "email", 0, this, SLOT( slotSendFeedbackMail() ),actionCollection(), "sendFeedback" );

  //for account context menu
  new TDEAction(i18n( "Setup &account" ), "tool", 0, this, SLOT( slotSetupAccount() ), actionCollection(), "setupAccount" );
}


void KShowMailApp::initStatusBar()
{
  statusBar()->insertItem( i18n( "Ready." ), STATUSBAR_FIELD_STATE );
  statusBar()->insertItem( "", STATUSBAR_FIELD_FILTER, 1 );
  statusBar()->setItemAlignment( STATUSBAR_FIELD_FILTER, AlignLeft | AlignVCenter );
  statusBar()->insertItem( "", STATUSBAR_FIELD_NEXT_REFRESH );
  statusBar()->insertItem( "", STATUSBAR_FIELD_LAST_REFRESH );

   TQToolTip::add( statusBar(), i18n( "Shows the number of deleted, moved or ignored mails by the filter.\nThe positions denotes:\nby last refresh / since application start / listed by the log" ) );

  refreshFilterStatusBar();
}

void KShowMailApp::initDocument()
{
  m_pDoc = new KshowmailDoc(this);
  m_pDoc->newDocument();
}

void KShowMailApp::initView()
{
  ////////////////////////////////////////////////////////////////////
  // create the main widget here that is managed by TDEMainWindow's view-region and
  // connect the widget to your document to display document contents.

  m_pView = new KshowmailView(this);
  m_pDoc->addView(m_pView);
  setCentralWidget(m_pView);
}

void KShowMailApp::slotSaveOptions()
{
  TDEConfig* config = TDEGlobal::config();

  //order application parts to save their setup
  m_pView->saveOptions( config );
  m_ConfigList.saveOptions ();
  fLog.save();

  //save config to file
  config->sync ();

}

bool KShowMailApp::queryClose()
{
    bool result;
    kdDebug() << "KShowMailApp::queryClose()" << endl;

    if (m_bForceClose) {
        // We are forced to close app - so just do it
        kdDebug() << "forced exit" << endl;
        result = true;
    } else if( m_ConfigList.closeToTray() ) {
        // Don't close - close button must minimize the window
        result = false;
        kdDebug() << "ignoring close request and minimizing the window" << endl;
        TDEMainWindow* w = memberList->first();
        w->hide();
    } else {
        // Need to ask user first
        result = askCloseConfirmation();
    }
    return result;
}

bool KShowMailApp::queryExit()
{
  kdDebug() << "KShowMailApp::queryExit()" << endl;
  slotSaveOptions ();

  return true;
}

/////////////////////////////////////////////////////////////////////
// SLOT IMPLEMENTATION
/////////////////////////////////////////////////////////////////////

void KShowMailApp::slotFileQuit()
{
  kdDebug () << "KShowMailApp::slotFileQuit()" << endl;
  if(memberList && askCloseConfirmation())
  {
    m_bForceClose = true;   // Forcing the closing
    TDEMainWindow* w = memberList->first();
    w->close();
  }
}


void KShowMailApp::slotSetupAccount()
{
  //just open the dialog, if the app is idle
  if (m_state != idle)
  {
    kapp->beep();
    return;
  }

  //set application state
  m_state = configure;

  //stop the refresh timer
  stopRefreshTimer();

  //get account to configure
  ConfigElem* pConfig = m_ConfigList.getSelectedAccount();

  if( pConfig != NULL )
  {
    //open dialog
    //the dialog sets the new options directly in the account
    ServerDialog dlg( this, pConfig );

    if( dlg.exec() == TQDialog::Accepted )
    {
      //if OK was clicked, refresh the account list view
      slotConfChanged();
    }
  }

  //we are ready, set application state to idle and initiate next refresh
  m_state = idle;
  initNextRefresh();
}

void KShowMailApp::slotAccountActivated (TQListViewItem* pItem)
{
  TQPtrListIterator<ConfigElem> it(m_ConfigList);
  // this is = not == !!!
  while (ConfigElem* pConfig = it())
  {
    if (pConfig->getListViewItem() == pItem)
    {
      setCaption (pConfig->getAccountName());
      return;
    }
  }
}

void KShowMailApp::slotAlertDestroyed()
{
  m_pAlertDialog = NULL;
}

void KShowMailApp::slotAlertOk ()
{
  //switch main window to normal view
  showNormal();
  raise();
}

void KShowMailApp::slotRefresh()
{
  //return, if the app is not idle
  if ( m_state != idle )
  {
    kapp->beep();
    initNextRefresh();
    return;
  }

  //stop the refresh timer
  stopRefreshTimer();

  //set the state
  m_state = refreshing;

  //show status message
  slotStatusMsg( i18n( "Refreshing ..." ) );

  //set waiting cursor
  TQApplication::setOverrideCursor( TQt::waitCursor );

  //order the account list to refresh its mail lists
  m_ConfigList.refreshMailLists( &fLog );

  return;
}

void KShowMailApp::slotStop ()
{
  if (m_state == idle)
  {
    kapp->beep ();
    return;
  }
  kdDebug() << "KShowMailApp::slotStop" << endl;

  //kill all running pop3 jobs
  m_ConfigList.killPOP3Jobs();

  slotRefreshView ();

  // make sure the view is refreshed before the state gets idle again
  // bug #853651

  //set state to idle
  m_state = idle;

  //set normal cursor
  //might be more than one override cursor ...
  while (TQApplication::overrideCursor())
    TQApplication::restoreOverrideCursor ();

  //print status message
  slotStatusMsg(i18n("Job was stopped"));
}

void KShowMailApp::slotRefreshView ()
{

  //clear mail list view
  m_pView->clearMailListView();

  //fill mail list view
  m_ConfigList.fillMailListView( m_pView );

  //refresh account list
  m_ConfigList.refreshAccountList();

  //get some informations
  int numberMails = m_ConfigList.getNumberMails();
  bool hasNewMails = m_ConfigList.getNumberNewMails() > 0;

  //draw number of mails into dock
  if( hasNewMails )
    //draw red number, because we have new mails
     m_pDockWindow->drawNumber( numberMails, TQt::red );
  else
    //we haven't new mails, draw black
    m_pDockWindow->drawNumber( numberMails, TQt::black );

  //show message in status bar
  long totalSize = m_ConfigList.getTotalSize();
  slotStatusMsg( i18n( "%1 message(s) with a total of %2 bytes are waiting" ).arg( numberMails ).arg( totalSize ) );

  //refresh filter view in the status bar
  refreshFilterStatusBar();
}

void KShowMailApp::slotDelete()
{
  //just delete, if no operation is running
  if( m_state != idle )
  {
    kapp->beep();
    return;
  }

  //return, if no mails are selected
  if( !m_ConfigList.hasSelectedMails() )
    return;

  //confirm deletion if required
  if( m_ConfigList.confirmDeletion() )
  {
    //get subjects off all selected mails
    TQStringList subjects = m_ConfigList.getSelectedSubjects();

    //show question
    int answer = KMessageBox::questionYesNoList( this, i18n( "Do you want to delete these mails?"), subjects, i18n( "Delete?" ) );

    if( answer == KMessageBox::No )
      return;
  }

  //set the state
  m_state = deleting;

  //show status message
  slotStatusMsg( i18n( "Deleting Mail(s) ..." ) );

  //set waiting cursor
  TQApplication::setOverrideCursor( TQt::waitCursor );

  //order the account list to delete the selected mails
  //test!
  m_ConfigList.deleteSelectedMails();
}

void KShowMailApp::slotShowHeader ()
{
  //only show headers, if the app is idle
  if( m_state != idle )
  {
    kapp->beep ();
    return;
  }

  //show the headers
  m_ConfigList.showSelectedHeaders();

}

void KShowMailApp::slotShowMessage ()
{
  //return, if application is not idle
  if( m_state != idle )
  {
    kapp->beep ();
    return;
  }

  //return, if no mails are selected
  if( !m_ConfigList.hasSelectedMails() )
    return;

  //set the state
  m_state = showing;

  //show status message
  slotStatusMsg( i18n( "Downloading ..." ) );

  //set waiting cursor
  TQApplication::setOverrideCursor( TQt::waitCursor );

  //order the account list to show the selected mails
  m_ConfigList.showSelectedMails();
}

void KShowMailApp::slotStatusMsg(const TQString& text)
{
  //get current time
  // added by Gustavo Zamorano to include time
  TQString sTime = TQTime::currentTime ().toString ();

  //clear status bar
  statusBar()->clear();

  //set given text
  statusBar()->changeItem( text, STATUSBAR_FIELD_STATE );

  //set current time
  statusBar()->changeItem( i18n( "Last Refresh: %1" ).arg( sTime ), STATUSBAR_FIELD_LAST_REFRESH );
}

void KShowMailApp::slotStatusHelpMsg(const TQString &text)
{
  statusBar()->message(text, 2000);
}




bool KShowMailApp::event(TQEvent *e)
{
  // to hide the taskbar button; any better solution ??
  if ((e->type() == TQEvent::Hide) && m_ConfigList.minimizesToTray() ) {
    hide ();
  }

  return(TDEMainWindow::event(e));
}

void KShowMailApp::slotForceClose()
{
    m_bForceClose = true;
}

bool KShowMailApp::askCloseConfirmation()
{
    bool result;

    if( m_ConfigList.confirmClose() ) {
        // Need to ask user
        result = (KMessageBox::questionYesNo (this, i18n ("KShowmail will be closed.\n"
                                    "Are you sure?")) == KMessageBox::Yes);
    } else {
        // User doesn't want to be asked - just close it
        result = true;
    }

    return result;
}

void KShowMailApp::slotDeletionReady( )
{
  //set state to idle
  m_state = idle;

  //set normal cursor
  while( TQApplication::overrideCursor() )
    TQApplication::restoreOverrideCursor();

  //show status message
  slotStatusMsg( i18n( "Ready." ) );

  //refresh mail list
  slotRefresh();
}

void KShowMailApp::slotShowMessageReady( )
{
  //set state to idle
  m_state = idle;

  //set normal cursor
  while( TQApplication::overrideCursor() )
    TQApplication::restoreOverrideCursor();

  //show status message
  slotStatusMsg( i18n( "Ready." ) );


}

void KShowMailApp::slotNormalCursor( )
{
  //set normal cursor
  while( TQApplication::overrideCursor() )
    TQApplication::restoreOverrideCursor();

}

void KShowMailApp::slotWaitingCursor( )
{
  //set waiting cursor
  TQApplication::setOverrideCursor( TQt::waitCursor );
}

void KShowMailApp::slotRefreshReady( )
{
  //set state to idle
  m_state = idle;

  //set normal cursor
  while( TQApplication::overrideCursor() )
    TQApplication::restoreOverrideCursor();

  //refresh mail list view
  slotRefreshView();

  //inform the user about new mails
  bool hasNewMails = m_ConfigList.getNumberNewMails() > 0;
  if ( hasNewMails )
  {
    //show main window if desired
    if( m_ConfigList.showMainWindowForNewMails() )
    {
      showNormal();
      raise();
    }

    //get out some sounds
    m_ConfigList.beep();
    m_ConfigList.playSound();

    //execute a command
    m_ConfigList.executeNewMailCommand();

    //show alert message if desired
    if( m_ConfigList.showAlertMessageForNewMails() )
    {
      //have we created one already?
      if( m_pAlertDialog != NULL )
      {
        //yes, we have one
        m_pAlertDialog->show();
        m_pAlertDialog->raise();
      }
      else
      {
        //create a message dialog
        m_pAlertDialog = new AlertDialog( this );
        connect( m_pAlertDialog, SIGNAL( destroyed() ), SLOT( slotAlertDestroyed() ) );
        connect( m_pAlertDialog, SIGNAL( signalOk() ), SLOT( slotAlertOk() ) );
      }
    }

  }
  else
  {
    //no new mails was received
    //perform configured actions

    //terminate app if desired
    if( m_ConfigList.quitNoNewMails() )
      slotFileQuit();
    else if( m_ConfigList.minimizeMainWindowNoNewMails() )
    {
      showMinimized();
      hide();
    }
  }

  //show status message
  slotStatusMsg( i18n( "Ready." ) );


  //prepare next refresh cycle
  initNextRefresh();
}

void KShowMailApp::initFirstRefresh( )
{
  //is auto refresh on? if not, we do nothing
  if( m_ConfigList.AutoRefreshOn() )
  {
    //is an initial time configured? if not, we refresh immediately
    if( m_ConfigList.hasInitTime() )
    {
      //starts refresh timer with the configured initial time to first refresh
      m_pTimer->start( m_ConfigList.getInitTime() * 1000, true );
      m_nSecondsToGo = m_ConfigList.getInitTime();
    }
    else
    {
      //just to set a defined state
      m_pTimer->stop();
      m_nSecondsToGo = 0;

      //start refresh cycle
      slotRefresh();
    }
  }
  else
  {
    //set a defined state
    m_pTimer->stop();
    m_nSecondsToGo = 0;
  }
}

void KShowMailApp::stopRefreshTimer( )
{
  m_pTimer->stop();
  m_nSecondsToGo = 0;
}

void KShowMailApp::initNextRefresh( )
{
  if( m_ConfigList.AutoRefreshOn() )
  {
    //auto refresh is activated
    //restart the refresh timer
    m_pTimer->start( m_ConfigList.getRefreshTimeInterval() * 60000, true );
    m_nSecondsToGo = m_ConfigList.getRefreshTimeInterval() * 60;
  }
  else
  {
    //auto refresh is not activated
    //set a defined state of the refresh timer
    m_pTimer->stop();
    m_nSecondsToGo = 0;
  }
}

void KShowMailApp::delayNextRefresh( )
{
  const unsigned long DELAY_TIME = 60;

  //delay only if the timer is active and the remaining time is less than one minute
  if( m_pTimer->isActive() )
  {
    if( m_nSecondsToGo < DELAY_TIME )
    {
      m_pTimer->start( DELAY_TIME * 1000, true );
      m_nSecondsToGo = DELAY_TIME;
    }
  }
}

void KShowMailApp::slotEditToolbars()
{
  //create the toolbar config dialog
  KEditToolbar *dlg = new KEditToolbar(guiFactory());
  if( dlg->exec() )
  {
    //if dialog closed by OK, create the GUI new
    //the dialog has changed the kshowmailui.rc file
    createGUI();
  }

  //destruct dialog
  delete dlg;
}

void KShowMailApp::slotSetup( )
{
  //just open the dialog, if the app is idle
  if (m_state != idle)
  {
    kapp->beep();
    return;
  }

  //set application state
  m_state = configure;

  //stop the refresh timer
  stopRefreshTimer();

  if ( SetupDialog == NULL )
  {
    //if no preferences dialog was created as yet, create one
    SetupDialog = new KCMultiDialog( KCMultiDialog::IconList, i18n( "Options" ), this, "SetupDialog" );
    SetupDialog->addModule( "kshowmailconfigaccounts.desktop" );
    SetupDialog->addModule( "kshowmailconfiggeneral.desktop" );
    SetupDialog->addModule( "kshowmailconfigdisplay.desktop" );
    SetupDialog->addModule( "kshowmailconfigactions.desktop" );
    SetupDialog->addModule( "kshowmailconfigfilter.desktop" );
    SetupDialog->addModule( "kshowmailconfigspamcheck.desktop" );
    SetupDialog->addModule( "kshowmailconfiglog.desktop" );

    //If the configuration was changed, it will call slotConfChanged
    connect( SetupDialog, SIGNAL( configCommitted() ), this, SLOT( slotConfChanged() ) );
  }

  //execute preferences dialog
  SetupDialog->exec();

  //we are ready, set application state to idle and initiate next refresh
  m_state = idle;
  initNextRefresh();

}

void KShowMailApp::slotConfChanged( )
{
  //read the new setup
  m_ConfigList.refreshSetup( m_pView->m_pListAccounts );
  m_pView->refreshSetup();
  fLog.loadSetup();

}

void KShowMailApp::slotShowFilterLog( )
{
  //open dialog
  FilterLogView view( this, &fLog );
  view.exec();

  refreshFilterStatusBar();
}

void KShowMailApp::refreshFilterStatusBar( )
{
  int numberDeletedLastRefresh = m_ConfigList.numberDeletedMailsLastRefresh();
  int numberDeletedSinceStart = m_ConfigList.numberDeletedMailsStart();
  int numberDeletedLog = fLog.numberDeletedMails();

  int numberMovedLastRefresh = m_ConfigList.numberMovedMailsLastRefresh();
  int numberMovedSinceStart = m_ConfigList.numberMovedMailsStart();
  int numberMovedLog = fLog.numberMovedMails();

  int numberIgnored = m_ConfigList.numberIgnoredMails();

  statusBar()->changeItem( i18n( "Filter: Deleted: %1/%2/%3; Moved: %4/%5/%6; Ignored: %7" ).arg( numberDeletedLastRefresh ).arg( numberDeletedSinceStart ).arg( numberDeletedLog ).arg( numberMovedLastRefresh ).arg( numberMovedSinceStart ).arg( numberMovedLog ).arg( numberIgnored ), STATUSBAR_FIELD_FILTER );
}

void KShowMailApp::slotAddToWhitelist( )
{
  //get new entries
  TQStringList newEntries = m_ConfigList.getSelectedSenders();

  //get previous whitelist from config
  TDEConfig* config = TDEApplication::kApplication()->config();
  config->setGroup( CONFIG_GROUP_FILTER );
  TQStringList whiteList = config->readListEntry( CONFIG_ENTRY_FILTER_WHITELIST );

  //append new entries
  for( TQStringList::Iterator it = newEntries.begin(); it != newEntries.end(); ++it )
  {
    if( whiteList.contains( *it ) == 0 )
    {
      whiteList.append( *it );
    }
  }

  //write extended whitelist back
  config->writeEntry( CONFIG_ENTRY_FILTER_WHITELIST, whiteList );
  config->sync();

  //load new config
  m_ConfigList.refreshFilterSetup();
}

void KShowMailApp::slotAddToBlacklist( )
{
  //get new entries
  TQStringList newEntries = m_ConfigList.getSelectedSenders();

  //get previous blacklist from config
  TDEConfig* config = TDEApplication::kApplication()->config();
  config->setGroup( CONFIG_GROUP_FILTER );
  TQStringList blackList = config->readListEntry( CONFIG_ENTRY_FILTER_BLACKLIST );

  //append new entries
  for( TQStringList::Iterator it = newEntries.begin(); it != newEntries.end(); ++it )
  {
    if( blackList.contains( *it ) == 0 )
    {
      blackList.append( *it );
    }
  }

  //write extended whitelist back
  config->writeEntry( CONFIG_ENTRY_FILTER_BLACKLIST, blackList );
  config->sync();

  //load new config
  m_ConfigList.refreshFilterSetup();
}

#include "kshowmail.moc"
