/*
 * katalog.cpp. Part of the KDE project.
 *
 * Copyright (C) 1999 Waldo Bastian
 *
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>

#include "katalog.h"

#include "kthread.h"

#include <qdatetime.h>

#include "mainview.h"
#include "catalog.h"

#include <kmenubar.h>
#include <kmessagebox.h>
#include <kimageio.h>
#include <qcursor.h>
#include <kfiledialog.h>
#include <qdragobject.h>

#include <kfiledialog.h>
#include <qfile.h>
#include <qmessagebox.h>
#include <qkeycode.h>
#include <qtextstream.h>
#include <qdragobject.h>
#include <qstringlist.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kglobal.h>
#include <kcmdlineargs.h>
#include <kaboutdata.h>

#include <kio/netaccess.h>

#include <stdlib.h>

#define USE_KACCEL 1
#ifdef USE_KACCEL
#include <kaccel.h>
#else
#include <kstdaccel.h>
#endif


static const char *description = 
	I18N_NOOP("KDE Image cataloging program (Not usable yet).");

static const char *version = "v0.0.1";

static KCmdLineOptions options[] = 
{
   { "+[cataloge]", I18N_NOOP("Cataloge file to open"), 0 },
   { 0, 0, 0 }
};


static int windowCount = 0;

Katalog::Katalog()
{
  windowCount++;
printf("Window Count = %d\n", windowCount);
  mMainView = new MainView(this);
  setView(mMainView);

  QTime timeBase = QTime::currentTime();

#if 0
  KThreadWorker *work = new KThreadWorker();
  KThread *thread = new KThread(work);

  KThreadWorker *work2 = new KThreadWorker();
  KThread *thread2 = new KThread(work2);

  thread2->start();
  thread->start();

  if (thread->finish(500))
  {
printf("Finished in time! (%d sec passed)",
timeBase.msecsTo(QTime::currentTime()));
  }
  else
  {
printf("Not finished in time. (%d sec passed)\n", 
timeBase.msecsTo(QTime::currentTime()));
  }
#endif
 
  //---------------------------------------------------
  // Drag'n'Drop as implemented by Qt (XDnD)
  setAcceptDrops(TRUE);

  //---------------------------------------------------
  // build a menubar

  // a menubar consists of popup menus. So we need a
  // popup menu first
  QPopupMenu* p = new QPopupMenu;

  // the new way in KDE post 1.1. KAccel is a very flexible class to handle
  // keyboard accelerations. These are completely configurable by the
  // user. Please see the kaccel documentation for further
  // details. The major advantage: With a single line of code calling
  // KKeyDialog::configureKeys(KAccel*) you can popup a keybinding
  // editor for your application.
  //
  // In kless we use KAccel only for some global defined standard
  // bindings.

  // create a kaccel object and bind the standard actions to our slots
  KAccel *a = new KAccel( this );
  a->connectItem(KStdAccel::New, this, SLOT( slotNewWindow() ) );
  a->connectItem(KStdAccel::Open, this, SLOT( slotLoadFile() ) );
  a->connectItem(KStdAccel::Save, this, SLOT( slotSaveFile() ) );
//  a->connectItem(KStdAccel::SaveAs, this, SLOT( slotSaveAs() ) );
  a->connectItem(KStdAccel::Quit, this, SLOT( close() ) );
  a->connectItem(KStdAccel::Close, this, SLOT( close() ) );

  // create the file menu. Tell kaccel to show the accelerators in the
  // menu as well. The advantage over doing that directly in Qt is,
  // that kaccels accelerators are internationalized.
  int id;
  id  = p->insertItem(i18n("&New"),this, SLOT(slotNewWindow()));
  a->changeMenuAccel(p, id, KStdAccel::New );
  mNewId = id;
  id = p->insertItem(i18n("&Open"),this, SLOT(slotLoadFile()));
  a->changeMenuAccel(p, id, KStdAccel::Open );
  mOpenId = id;
  p->insertSeparator();
  id = p->insertItem(i18n("&Add image"),this, SLOT(slotAddImage()));
//  mAddImageId = id;

  p->insertSeparator();
  id = p->insertItem(i18n("&Save"),this, SLOT(slotSaveFile()));
  a->changeMenuAccel(p, id, KStdAccel::Save );
  mSaveId = id;
  id = p->insertItem(i18n("S&ave as"),this, SLOT(slotSaveAs()));
//  a->changeMenuAccel(p, id, KStdAccel::SaveAs );
  mSaveAsId = id;
  id = p->insertItem(i18n("&Close"),this, SLOT(slotCloseFile()));
//  a->changeMenuAccel(p, id, KStdAccel::Quit );
  mCloseId = id;
  p->insertSeparator();
  id = p->insertItem(i18n("&Quit"),this, SLOT(close()));
  a->changeMenuAccel(p, id, KStdAccel::Quit );

  // put the popupmenu as File-menu into the menu bar
  menuBar()->insertItem(i18n("&File"), p);

#if 0
  p = new QPopupMenu;
  id = p->insertItem(i18n("&New"),this, SLOT(slotNewWindow()));
  a->changeMenuAccel(p, id, KStdAccel::New );

  id = p->insertItem(i18n("&Close"),this, SLOT(close()));
  a->changeMenuAccel(p, id, KStdAccel::Close );

  // put the popupmenu as Window-menu into the menu bar
  menuBar()->insertItem(i18n("&Window"), p);
#endif


  p = new QPopupMenu;
  p->insertItem(i18n("Show &name"), ID_VIEW_NAME);
  p->insertItem(i18n("Show &size"), ID_VIEW_SIZE);
  p->insertItem(i18n("Show &filesize"), ID_VIEW_FILESIZE);
  p->insertItem(i18n("Show &description"), ID_VIEW_DESC);
  connect( p, SIGNAL(activated(int)), this, SLOT( slotView(int)));
  
  // put the popupmenu as Catalog-menu into the menu bar
  menuBar()->insertItem(i18n("&View"), p);

  p = new QPopupMenu;
  id = p->insertItem(i18n("&Thumbnail size..."),this, SLOT(slotNewWindow()));

  // put the popupmenu as Catalog-menu into the menu bar
  mCatalogId = menuBar()->insertItem(i18n("&Catalog"), p);
  

  menuBar()->insertSeparator();

  // we let KDE generate a nifty help menu
  p = helpMenu(
			i18n("katalog --- An Image Cataloging Program\n\n"
			     "(c) 1999 Waldo Bastian"));
  menuBar()->insertItem(i18n("&Help"), p);

  //---------------------------------------------------
  // build a small toolbar

  // insert some buttons, the icons are from the standard KDE toolbar.
  toolBar()->insertButton(BarIcon("fileopen"),TOOLBAR_OPEN, true,i18n("Open Catalog"));
  toolBar()->insertButton(BarIcon("filefloppy"),TOOLBAR_SAVE, true,i18n("Save Catalog"));
  // Hint: toolBar() returns toolBar(0). If you want another toolbar, simply
  //       use toolBar(1), toolBar(2) and so on.

  // we connect the entire toolbar to one single slot. We could also
  // connect the single toolbar buttons to special slots in the
  // insertButton call above, for example:
  //   insertButton(Icon("exit"),TOOLBAR_EXIT,
  //                SIGNAL(clicked()), kapp, SLOT(quit()),
  //                true,"Exit")
  // This would be even better for this simple toolbar. We have chosen
  // the other design --- which is more flexible for bigger toolbars ---
  // for demonstration purposes in kless.
  connect(toolBar(), SIGNAL(clicked(int)), SLOT(slotToolbarClicked(int)));

    updateTM();
}

Katalog::~Katalog()
{
  windowCount--;
printf("Window Count = %d\n", windowCount);
}

bool 
Katalog::empty()
{
   return mMainView->catalog()->empty();
}

void Katalog::updateTM()
{
  if (!mMainView->catalog())
     mMainView->setCatalog( new Catalog());
  bool catalogOk = !empty();
  Catalog *cat = mMainView->catalog();
//  toolBar()->setItemEnabled(TOOLBAR_OPEN, !catalogOk );
  toolBar()->setItemEnabled(TOOLBAR_SAVE, catalogOk );
//  menuBar()->setItemEnabled(mOpenId, !catalogOk );
  menuBar()->setItemEnabled(mSaveId, catalogOk );
  menuBar()->setItemEnabled(mSaveAsId, catalogOk );
  menuBar()->setItemEnabled(mCloseId, catalogOk );
  menuBar()->setItemEnabled(mCatalogId, catalogOk );

  menuBar()->setItemChecked(ID_VIEW_NAME, cat->viewName() );
  menuBar()->setItemChecked(ID_VIEW_SIZE, cat->viewSize() );
  menuBar()->setItemChecked(ID_VIEW_FILESIZE, cat->viewFilesize() );
  menuBar()->setItemChecked(ID_VIEW_DESC, cat->viewDesc() );
}

void Katalog::slotView(int id)
{
   bool bOn;
   Catalog *cat = mMainView->catalog();
   switch(id)
   {
     case ID_VIEW_NAME: bOn = !cat->viewName(); cat->viewName(bOn); break;
     case ID_VIEW_SIZE: bOn = !cat->viewSize(); cat->viewSize(bOn); break;
     case ID_VIEW_FILESIZE: bOn = !cat->viewFilesize(); cat->viewFilesize(bOn); break;
     case ID_VIEW_DESC: bOn = !cat->viewDesc(); cat->viewDesc(bOn); break;
   }  
   updateTM();
   mMainView->updateView();
}

void Katalog::slotToolbarClicked(int item){
  switch (item) {
  case TOOLBAR_OPEN:
    slotLoadFile();
    break;
  case TOOLBAR_SAVE:
    slotSaveFile();
    break;
  case TOOLBAR_SAVE_AS:
    slotSaveAs();
    break;
  case TOOLBAR_CLOSE:
    slotCloseFile();
    break;
  case TOOLBAR_EXIT:
    close();
    break;
  }
}

class ThreadLoadFile : public KThreadWorker
{
public:
     QString filename;
     Catalog *catalog;
     ThreadLoadFile(QString _filename) { filename = _filename; }
     void exec()
     {
        catalog = new Catalog();
        catalog->load(filename);
     }
};

void Katalog::loadFile(const KURL & url)
{
     mLocation = url;
     setCaption(url.url());

#if 0
     ThreadLoadFile workerThread(filename);
     KThread thread(&workerThread);

     thread.start();
     
     while (!thread.finish(400))
     {
         KMessageBox::information(mMainView, i18n("Please wait..."));    
printf("KMessageBox returned...\n");
     }
     
     mMainView->setCatalog( workerThread.catalog );
#else
     QString tempFile;

     KIO::NetAccess::download( url, tempFile );

     Catalog *catalog = new Catalog();
     catalog->load(tempFile);
     mMainView->setCatalog( catalog );

     KIO::NetAccess::removeTempFile( tempFile );
#endif

     updateTM();
}

void Katalog::dragEnterEvent( QDragEnterEvent *e )
{
printf("dragEnterEvent!\n");
   // Check if we want the drag...
   if (QUriDrag::canDecode( e ) ||
       QImageDrag::canDecode( e ))
   {
      e->accept();
   }
}


void Katalog::dropEvent( QDropEvent *event){
  // the user dropped something on our window.
  // So we simply use KIO slaves to download the stuff wherever it is.
  // The following code respects Qt's DnD tutorial

printf("Got dropevent!\n");
  QImage im;
  QString txt, u, tgt;
  QStrList uri;

  if(QUriDrag::canDecode(event) &&  QUriDrag::decode(event, uri)) 
  {
      kapp->setOverrideCursor( QCursor(waitCursor) );
      for(u = uri.first(); u; u = uri.next())
      {
printf("URL = %s\n", u.ascii());
         mMainView->addImage(KURL(u));
      }

#if 0
   if (KIO::NetAccess::download(u, tgt)){
    setCaption(u);
    loadFile(tgt);
    KIO::NetAccess::removeTempFile(tgt);
   }
#endif
      kapp->restoreOverrideCursor();
  }
printf("Test image...\n");  
  if(QImageDrag::decode(event, im) )
  {
      kapp->setOverrideCursor( QCursor(waitCursor) );
printf("Image = %p\n", &im);
      mMainView->addImage(im);
      kapp->restoreOverrideCursor();
  }

  updateTM();
}

void Katalog::slotNewWindow(){
  // this slot is invoked from File->New_Winodw
  (new Katalog)->show();
}


void Katalog::slotLoadFile(){
  // this slot is invoked from File->Open

  // ask kfiledialog for a filename. We pass "this" to the function
  // to indicate the window manager that the file dialog belongs
  // to our application window.
  KURL u = KFileDialog::getOpenURL(QString::null, "*.kat", this, i18n("Open Katalog")); 
  
  if (u.isEmpty())
  { // no filename, user hit "Cancel" or pressed "Escape"
    return;
  }  

  if (empty())
  {
     // Load in current window
     loadFile(u);
  }
  else
  {  
     // Load in new window
     Katalog *katalog = new Katalog();
     katalog->show();
     katalog->loadFile(u);
  }
}

void Katalog::slotAddImage(){
  // this slot is invoked from File->Add Image

  QString dir;
  if (mMainView->catalog())
     dir = mMainView->catalog()->addImageDir();

printf("Old DIR = %s\n", dir.ascii());

  // ask kfiledialog for a filename. We pass "this" to the function
  // to indicate the window manager that the file dialog belongs
  // to our application window.

//  QStringList files = KFileDialog::getOpenFileNames(0, 0, this); 
  QValueList<KURL> files;
  
  QString filter = KImageIO::pattern();

  KFileDialog dlg(dir, filter, mMainView, "filedialog", true);

  dlg.setCaption(i18n("Add Image"));

  dlg.exec();
 
  KURL oneFile = dlg.selectedURL();

  if (!oneFile.isMalformed())
  {
     files.append(oneFile);
     dir =  dlg.baseURL().url();
     if (!mMainView->catalog())
        mMainView->setCatalog( new Catalog() );
     mMainView->catalog()->addImageDir(dir);
printf("New DIR = %s\n", dir.ascii());
  }

printf("File count = %d\n", files.count());  
  if (files.count() == 0)
  { // no filename, user hit "Cancel" or pressed "Escape"
    return;
  }  

  kapp->setOverrideCursor( QCursor(waitCursor) );
  for(QValueList<KURL>::ConstIterator it = files.begin(); 
      it != files.end(); 
      it++)
  {
     KURL u( *it );

printf("URL = %s\n", u.url().ascii());
     mMainView->addImage(u);
  }
  kapp->restoreOverrideCursor();
  updateTM();
}

void Katalog::slotSaveFile(){
  // this slot is invoked from File->Save

  if (empty()) return;

  if (mLocation.isEmpty())
  {
     slotSaveAs();
  }
  else 
  {
    if (mLocation.isLocalFile()) 
    {
       QString s = mLocation.path();
       mMainView->catalog()->saveAs(s);
    }
    else
    {
       KMessageBox::sorry(mMainView, i18n("Saving to remote locations is not yet supported.\n"));
    }
  }
}



void Katalog::slotSaveAs()
{
  // this slot is invoked from File->SaveAs

  if (empty()) return;

  QString filter = i18n("*.kat|Katalog files\n*|All Files");

  KFileDialog dlg(mLastDir, filter, mMainView, "filedialog", true);

  dlg.setCaption(i18n("Save Katalog As"));

  dlg.exec();

  KURL u = dlg.selectedURL();

  if (!u.isEmpty()){ // no filename, user hit "Cancel" or pressed "Escape"
    if (dlg.baseURL().isLocalFile())
    {
       mLastDir = dlg.baseURL().url();
    }
    mLocation = u;
    setCaption(u.url());
    if (mLocation.isLocalFile()) 
    {
       QString s = mLocation.path();
       mMainView->catalog()->saveAs(s);
    }
    else
    {
       KMessageBox::sorry(mMainView, i18n("Saving to remote locations is not yet supported.\n"));
    }
  }
}

void Katalog::slotCloseFile(){
  // this slot is invoked from File->Close 

  if (empty()) return;

  Catalog *catalog = mMainView->catalog();

  if (catalog->dirty())
  {
     int result = KMessageBox::warningYesNoCancel(mMainView,
                     i18n("The catalog has been changed.\n"
                          "Do you want to save the catalog or discard the changes?"),
                     i18n("Save catalog?"), i18n("&Save"), i18n("&Discard") );
     switch(result)
     {
       case KMessageBox::Yes: 
            slotSaveFile(); 
            if (catalog->dirty())
               return; // Cancel
            break; // Yes

       case KMessageBox::No: 
	    break; // No

       default: 
            return; // Cancel
     }
  }

  delete catalog;
  setCaption(QString::null);
  updateTM();
}

bool Katalog::queryClose(){
  slotCloseFile();
  if (!empty())
     return false;
  return true;
}


void Katalog::saveProperties(KConfig* /* config */){
};

void Katalog::readProperties(KConfig* /* config */){
};

int main(int argc, char* argv[]) 
{
  (void) malloc(10);

  KAboutData aboutData( "katalog", I18N_NOOP("Katalog"), 
    version, description, KAboutData::License_GPL, 
    "(c) 1999, Waldo Bastian");
  aboutData.addAuthor("Waldo Bastian",0, "bastian@kde.org");
  KCmdLineArgs::init( argc, argv, &aboutData );
  KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
  
  KApplication a;
  KImageIO::registerFormats();
  if (a.isRestored())
    RESTORE(Katalog)
  else {
    // no session management: just create one window
    Katalog* katalog = new Katalog;

    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
    if (args->count())
      katalog->loadFile(args->url(0));
    katalog->show();
  }
  a.exec();
}





