/* This file implements kabīs toplevel widget..
 *
 * the KDE addressbook
 * copyright:  (C) Mirko Sucker, 1998, 1999, 2000
 * mail to:    Mirko Sucker <mirko@kde.org>
 * requires:   recent C++-compiler, at least Qt 2.0
 
 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., 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.
 
 * $Revision: 1.21 $
 */

#include "kab_topwidget.h"
#include "kab_kab1importer.h"
#include <kabapi.h>
#include "look_basic.h"
#include "look_businesscard.h"
#include "look_edit.h"
#include "widget_datanavigator.h"
#include <kmenubar.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kapp.h>
#include <kstdaccel.h>
#include <kstddirs.h>
#include <kaboutdialog.h>
#include <qtimer.h>
#include <qframe.h>
#include <qstringlist.h>
#include <qlayout.h>
#include <stdio.h>
#include <kdebug.h>

TopLevelWidget::TopLevelWidget()
  : KTMainWindow(),
    view(0),
    currentView(NoView),
    current(0),
    timer(new QTimer(this)),
    messages(new QStringList),
    modified(false),
    closingdown(false)
{
  bool GUARD; GUARD=true;
  kDebugInfo(GUARD, 0, "TopLevelWidget ctor: called.");
  // -----
  // WORK_TO_DO: remember last selected view
  createView(BusinessCard);
  api=new KabAPI(this);
  if(api==0)
    {
      KMessageBox::sorry
	(this, i18n("Out of memory."),
	 i18n("General failure"));
      ::exit(-1);
    }
  // -----
  makeMenu();
  makeToolbar();
  makeStatusbar();
  // -----
  connect(nav, SIGNAL(itemSelected(int)), SLOT(entrySelected(int)));
  connect(api, SIGNAL(setStatus(const QString&)),
	  SLOT(setStatus(const QString&)));
  connect(timer, SIGNAL(timeout()), SLOT(statusbarTimeOut()));
  // -----
  if(!initializeInterface())
    {
      KMessageBox::sorry
	(this, i18n("Could not connect to the address database."),
	 i18n("Database error"));
      ::exit(-1);
    }
  connect(this, SIGNAL(databaseChanged()),
	  api->addressbook(), SLOT(externalChange()));
  // -----
  entriesChanged();
  // -----
  setStatus(i18n("Welcome to kab 2.0"));
  kDebugInfo(GUARD, 0, "TopLevelWidget ctor: done.");
}

void TopLevelWidget::makeMenu()
{
  // ----- the file menu:
  file=new QPopupMenu;
  CHECK_PTR(file);
  // ----- the import (sub-) menu (more should follow): 
  import=new QPopupMenu;
  CHECK_PTR(import);
  import->insertItem(i18n("&KDE 1 address book"), this, 
		     SLOT(importKab1Addressbook()));
  file->insertItem(i18n("&Create new database"), this,
		   SLOT(createNew()), KStdAccel::openNew());
  file->insertItem(i18n("&Open"), this,
		   SLOT(loadDatabaseFile()), KStdAccel::open());
  file->insertItem(i18n("Open default &database"), this,
		   SLOT(loadDefaultDatabase()));
  file->insertItem(i18n("&Save"), this,
		   SLOT(save()), KStdAccel::save());
  file->insertSeparator();
  file->insertItem(i18n("&Import..."), import);
  file->insertSeparator();
  file->insertItem(i18n("&Quit"), this, SLOT(quit()), KStdAccel::quit());
  // ----- the edit menu:
  edit=new QPopupMenu;
  edit->insertItem(i18n("&Add entry"), this, SLOT(add()), CTRL+Key_A);
  edit->insertItem(i18n("&Edit entry"), this, SLOT(editEntry()), CTRL+Key_E);
  edit->insertItem(i18n("&Remove entry"), this, SLOT(remove()), CTRL+Key_R);
  edit->insertSeparator();
  idEditMail=edit->insertItem(i18n("Send an e&mail"), this, SLOT(mail()), CTRL+Key_M);
  idEditBrowse=edit->insertItem(i18n("&Browse"), this, SLOT(browse()), CTRL+Key_B);
  edit->insertSeparator();
  edit->insertItem(i18n("Enable all &messages"), this,
		   SLOT(enableAllMessages()));
  edit->insertItem(i18n("&Configure this file"), this,
		   SLOT(configureFile()));
  // ----- the view menu:
  menuview=new QPopupMenu;
  idViewBC=menuview->insertItem(i18n("&Business card"), this,
				SLOT(selectViewBC()));
  idViewEdit=menuview->insertItem(i18n("&Editing"), this,
				  SLOT(selectViewEdit()));
  menuview->setCheckable(true);
  selectViewBC();
  // -----
  menuBar()->insertItem(i18n("&File"), file);
  menuBar()->insertItem(i18n("&Edit"), edit);
  menuBar()->insertItem(i18n("&View"), menuview);
  menuBar()->insertItem
    (i18n("&Help"), helpMenu(QString::null, false));
}

void TopLevelWidget::makeToolbar()
{
  int id;
  // -----
  nav=new KDataNavigator(toolBar());
  nav->setFrameStyle(QFrame::NoFrame);
  nav->setMode(KDataNavigator::List);
  id=toolBar()->insertWidget(0, 100, nav);
  toolBar()->setItemAutoSized(id);
  connect(nav, SIGNAL(newItem()), SLOT(add()));
}

void TopLevelWidget::makeStatusbar()
{
  //statusBar()->setInsertOrder(KStatusBar::RightToLeft);
  statusBar()->insertItem("20000/20000", Number);
  statusBar()->insertItem("", Text, 1000, true);
}

bool TopLevelWidget::initializeInterface()
{
  if(api->init()!=AddressBook::NoError)
    {
      KMessageBox::sorry
	(this, i18n("The database interface could not be created."),
	 i18n("Database error"));
      return false;
    } else {
      connect(api->addressbook(), SIGNAL(changed()),
	      SLOT(entriesChanged()));
      return true;
    }
}

void TopLevelWidget::entrySelected(int index)
{
  bool GUARD; GUARD=true;
  kDebugInfo(GUARD, 0, "TopLevelWidget::entrySelected: called (%i).", index);
  // -----
  AddressBook::Entry entry;
  KabKey key;
  int number;
  QString text;
  // ----- store user changes:
  if(closingdown) return;
  if(modified) // the current entry has been modified
    { // WORK_TO_DO: respect "Query save on change" configuration setting
      if(current!=0) // make sure non-debug versions also work stable
	{
	  if(KMessageBox::questionYesNo
	     (this,
	      i18n("You changed this entry.\nSave changes?\n"
		   "(Unsaved changes will be lost.)"),
	      i18n("Save changes?"))==KMessageBox::Yes)
	    {
	      save();
	    }
	}
    }
  // -----
  number=api->addressbook()->noOfEntries();
  if(number!=0)
    {
      view->setEnabled(true);
      if(api->addressbook()->getKey(index, key)!=AddressBook::NoError)
	{
	  kDebugInfo("TopLevelWidget::entrySelected: no such entry (%i).",
		     index);
	}
      if(api->addressbook()->getEntry(key, entry)!=AddressBook::NoError)
	{
	  kDebugInfo("TopLevelWidget::entrySelected: cannot access existing entry.");
	}
    } else {
      kDebugInfo("TopLevelWidget::entrySelected: no entries.");
      view->setEnabled(false);
    }
  // -----
  if(number==0)
    {
      text=i18n("No entries.");
    } else {
      text=text.sprintf("%i/%i", index+1, number);
    }
  statusBar()->changeItem(text, Number);
  // -----
  view->setEntry(entry);
  modified=false; // finally reset modification mark
  if(current!=0)
    { // make sure the old key is deleted:
      delete current; current=0;
    }
  current=new KabKey(key);
  kDebugInfo(GUARD, 0, "TopLevelWidget::entrySelected: done (%i).", index);
}

void TopLevelWidget::entriesChanged()
{ // this signal is emitted from AddressBook::updateEntriesMap
  bool GUARD; GUARD=true;
  kDebugInfo(GUARD, 0, "TopLevelWidget::entriesChanged: called.");
  // -----
  QStringList headlines;
  // -----
  if(api->addressbook()->getListOfNames(&headlines, true, false)
     !=AddressBook::NoError)
    {
      return;
    }
  nav->setList(&headlines);
  entrySelected(0); // WORK_TO_DO: remember the visible entry like in kab 1
  // -----
  kDebugInfo(GUARD, 0, "TopLevelWidget::entriesChanged: done.");  
}

void TopLevelWidget::importKab1Addressbook()
{ // WORK_TO_DO: Proof-read messages
  // ###########################################################################
  Kab1Importer importer(api, this);
  connect(&importer, SIGNAL(setStatus(const QString&)),
	  SLOT(setStatus(const QString&)));
  if(importer.exec())
    {
      entriesChanged();
    }
  // ###########################################################################
}

void TopLevelWidget::setStatus(const QString& text)
{
  // ###########################################################################
  messages->append(text);
  if(!timer->isActive())
    {
      timer->start(0);
    } else {
      timer->start(500);
    }
  // ###########################################################################
}

void TopLevelWidget::statusbarTimeOut()
{
  // ###########################################################################
  if(!messages->isEmpty())
    {
      statusBar()->changeItem(messages->first(), Text);
      messages->remove(messages->begin());
      if(messages->isEmpty())
	{
	  timer->start(5000, true);
	} else {
	  timer->start(1000, true);
	}
    } else {
      statusBar()->changeItem("", Text);
    }
  // ###########################################################################
}

void TopLevelWidget::loadDefaultDatabase()
{
  // ###########################################################################
  QString path;
  // -----
  path=api->addressbook()->getStandardFilename();
  if(!path.isEmpty())
    {
      if(api->addressbook()->load(path)!=AddressBook::NoError)
	{
	  KMessageBox::sorry
	    (this, i18n("The standard file could not be loaded."),
	     i18n("File error"));
	}
    }
  modified=false;
  // ###########################################################################
}

void TopLevelWidget::selectViewBC()
{
  menuview->setItemChecked(idViewBC, true);
  menuview->setItemChecked(idViewEdit, false);
  createView(BusinessCard);
  emit(setStatus(i18n("Business card view.")));
}

void TopLevelWidget::selectViewEdit()
{
  menuview->setItemChecked(idViewBC, false);
  menuview->setItemChecked(idViewEdit, true);
  createView(Editing);
  emit(setStatus(i18n("Editing view.")));
}

void TopLevelWidget::enableAllMessages()
{
  if(KMessageBox::questionYesNo
     (this,
      i18n("This will re-enable all messages you disabled before.\n"
	   "Continue?"),
      i18n("Enable messages?"))==KMessageBox::Yes)
    {
      KMessageBox::enableAllMessages();
    }
}

void TopLevelWidget::add()
{ // WORK_TO_DO: check for redundant calls 
  KabKey key;
  AddressBook::Entry entry; // WORK_TO_DO: possibly use templates or default entries?
  int index;
  // -----
  switch(api->add(entry, key, true))
    {
    case AddressBook::NoError:
      // ----- first save the database:
      emit(setStatus(i18n("New entry added.")));
      save();
      emit(databaseChanged());
      entriesChanged();
      // ----- make the new entry the current (and visible):
      if(api->addressbook()->getIndex(key, index)==AddressBook::NoError)
	{
	  entrySelected(index);
	} 
      // ----- and edit it if we are not in editing mode:
      if(currentView!=Editing)
	{
	  if(*current==key)
	    {
	      if(editCurrentEntry())
		{
		  emit(i18n("Saving new entry."));
		  save();
		  emit(i18n("Done."));
		} else {
		  emit(i18n("Rejected."));
		  removeCurrentEntry(true); // silently
		}
	    } else {
	      KMessageBox::information
		(this,
		 i18n("Cannot display the new entry.\n"
		      "Unknown cause."),
		 i18n("Error"));
	      return;
	    }
	}
      break;
    case AddressBook::PermDenied:
      KMessageBox::information
	(this,
	 i18n("Cannot add the new entry.\n"
	      "Permission denied.\n"
	      "You do not have writing permissions for this file."),
	     i18n("Error"));
      return;
    default:
      KMessageBox::information
	(this,
	 i18n("Cannot add the new entry.\n"
	      "Unknown cause."),
	     i18n("Error"));
      return;
    }     
  emit(databaseChanged());
  entriesChanged();
  // ----- recalculate index of this entry (may have changed):
  if(api->addressbook()->getIndex(key, index)==AddressBook::NoError)
    {
      entrySelected(index);
    } 
}

void TopLevelWidget::remove()
{
  removeCurrentEntry();
}

bool TopLevelWidget::removeCurrentEntry(bool quiet)
{
  bool queryOnDelete=true; // WORK_TO_DO: respect configuration settings
  bool doIt=true;
  // -----
  if(queryOnDelete && !quiet)
    {
      doIt=KMessageBox::questionYesNo
	(this,
	 i18n("Really remove this entry?"),
	 i18n("Question"))==KMessageBox::Yes;
    }
  if(doIt)
    {
      if(api->remove(*current)==AddressBook::NoError)
	{
	  emit(setStatus(i18n("Entry deleted.")));
	  save();
	  emit(databaseChanged());
	  entriesChanged();
	  return true;
	} else {
	  KMessageBox::information
	    (this,
	     i18n("Could not delete the entry\n"
		  "(probably permission denied)."),
	     i18n("Error"));
	  emit(setStatus(i18n("Permission denied.")));
	  return false;
	}
    }
  return false;
}

void TopLevelWidget::createView(View v, bool recreate)
{
  bool modificationstate=modified;
  // ----- avoid deleting and creating:
  if(v==currentView && !recreate) return;
  // -----
  AddressBook::Entry entry;
  // -----
  if(view!=0)
    {
      view->getEntry(entry);
      delete view;
      view=0;
    }
  // -----
  switch(v)
    {
    case BusinessCard:
      view=new KABBusinessCard(api, this);
      break;
    case Editing:
      view=new KABEditLook(api, this);
      connect(view, SIGNAL(entryChanged()), SLOT(entryChangedSlot()));
      break;
    default:
      kDebugWarning("TopLevelWidget::createView: unknown kind of view.");
      view=new KABBusinessCard(api, this);
    }
  connect(view, SIGNAL(sendEmail(const QString&)), SLOT(mail(const QString&)));
  connect(view, SIGNAL(browse(const QString&)), SLOT(browse(const QString&)));
  view->setEntry(entry);
  view->setMinimumSize(320, 200);
  setView(view, true);
  view->show();
  currentView=v;
  modified=modificationstate;
  // -----
}

void TopLevelWidget::entryChangedSlot()
{
  register bool GUARD; GUARD=false;
  kDebugInfo(GUARD, 0,
	     "TopLevelWidget::entryChangedSlot: current entry has been changed.");
  modified=true;
}

void TopLevelWidget::quit()
{
  closingdown=true;
  kapp->quit();
}

void TopLevelWidget::aboutKDE()
{
}

void TopLevelWidget::showAboutApplication(void)
{
  QPixmap logo;
  QString path;
  KAboutDialog dialog(this);
  // -----
  path=locate("appdata", "pics/addressbook_logo.png");
  if(path.isEmpty())
    {
      kDebugWarning
	(0, "TopLevelWidget::aboutKAB: cannot locate ressources.");
    } else {
      if(logo.load(path))
	{
	  dialog.setLogo(logo);
	}
    }
  dialog.setCaption("About KDE address book");
  dialog.setVersion("KDE address book 2.0alpha");
  dialog.setAuthor("Mirko Sucker", "mirko@kde.org", "", "Initial developer.");
  dialog.adjust();
  dialog.exec();
}

void TopLevelWidget::mail()
{
  mail(QString::null);
}

void TopLevelWidget::mail(const QString& url)
{
  kdDebug() << "TopLevelWidget::mail: called with url " << url << "." << endl;
}

void TopLevelWidget::browse()
{
  browse(QString::null);
}

void TopLevelWidget::browse(const QString& url)
{
  kdDebug() << "TopLevelWidget::browse: called with url " << url << "." << endl;
}
