/*
    KDE Icon Editor - a small graphics drawing program for the KDE
    Copyright (C) 1998  Thomas Tanghus (tanghus@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.

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

#include <stdlib.h>

#include <klocale.h>
#include <kmessagebox.h>
#include <kfiledialog.h>
#include <kio/netaccess.h>

#include "kicon.h"

KIconEditIcon::KIconEditIcon(QObject *parent, const QImage *img, const QString &filename) 
   : QObject(parent)
{
  f = 0;
  _lastdir = "/";
  checkUnNamedBackup(img);

  if(!filename.isEmpty())
  {
    open(img, filename);
  }
}

KIconEditIcon::~KIconEditIcon()
{
}

void KIconEditIcon::cleanup()
{
  if(_backup.length() > 0 && QFile::exists(_backup))
  {
    if(!removeFile(_backup))
    {
      QString msg = i18n("There was an error removing temporary backup file:\n");
      msg += _backup;
      msg += '\n';
      KMessageBox::error((QWidget*)parent(), msg);
    }
  }

  _backup = "";
  _url = "";
  unlock(f);
  emit newname("");
}

void KIconEditIcon::open(const QImage *image, const QString &xpm)
{
  QImage *img = (QImage*)image;
  QString filename(xpm);

  if(filename.isEmpty())
    return;

  debug("KIconEditIcon::open %s", filename.ascii());
/*
  if(busyDownloading)
  {
    KMessageBox::sorry((QWidget*)parent(), 
      i18n("We've already got a download running.\nPlease be patient."));
    return;    
  }
*/
  QString tmp = "";

  if(filename.at(0) == '/')
    filename = "file:" + filename;

  debug("KIconEditIcon::open - checked for absolute path");

  KURL _turl(filename);
  if(_turl.isMalformed()) // try to see if it is a relative filename
  {
    debug("KIconEditIcon::open (malformed) %s", filename.ascii());

    QFileInfo fi(filename);
    if(fi.isRelative())
      filename = "file:" + fi.absFilePath();
    _turl = filename;

    if(_turl.isMalformed()) // Giving up
    {
      QString msg = i18n("The URL: %1 \nseems to be malformed.\n").arg(_turl.url());
      KMessageBox::sorry((QWidget*)parent(), msg);
      return;
    }
  }

  debug("KIconEditIcon::open (not malformed) %s", filename.ascii());

  bool r;
  tmp = kapp->checkRecoverFile(_turl.url().data(), r);

  if(r)
  {
    debug("KIcon: Found recover file");
    QString msg = i18n("The KDE Icon Editor has found a recover file for:\n"
"%1\nDo you want to open this?").arg(filename);
    if(KMessageBox::questionYesNo((QWidget*)parent(), msg) == 0)
    {
      debug("Opening recover file for: %s", filename.data());
    }
  }
  else
  {
    tmp = kapp->tempSaveName(_turl.url());
    //debug("KIcon: tempSaveName: %s", tmp.data());

    debug("isLocalFile   : %d", _turl.isLocalFile());
    debug("Path:         : %s", _turl.path().ascii());
    debug("URL:          : %s", _turl.url().data());

    if(_turl.isLocalFile())
    {
      debug("KIconEditIcon::open local: %s", _turl.url().data());
      QFileInfo f(_turl.path());
      if(!f.exists() || !f.isReadable())
      {
        QString msg = i18n("The file:\n");
        msg += _turl.path();
        msg += i18n("\ndoesn't exist or isn't readable.\n");
        KMessageBox::error((QWidget*)parent(), msg);
        return;
      }
      else
      {
        removeFile(_backup);
        copyFile(_turl.path(), tmp.data());
      }
    } // if local
    else
    {
      debug("KIconEditIcon::open non-local: %s %s", _turl.path().ascii(), tmp.data());
      if(busyDownloading)
      {
        QString msg = i18n("The Icon Editor is already downloading a non-local file.\nPlease wait.\n");
        KMessageBox::error((QWidget*)parent(), msg);
        return;
      }

      QString src = _turl.url();

      debug("KIconEditIcon::open - before download: %s", tmp.data());
      removeFile(_backup);
      busyDownloading = true;
      if(!KIO::NetAccess::download(src, tmp))
      {
        busyDownloading = false;
        QString msg = i18n("There was a KFM error creating a backup for:\n%1\n").arg(_turl.path());
        KMessageBox::error((QWidget*)parent(), msg);
      }
      busyDownloading = false;

      debug("KIconEditIcon::open - after download: %s", tmp.data());
    } // remote
    if(!QFile::exists(tmp))
    {
      QString msg = i18n("There was an error creating a backup for:\n%1\n").arg(_turl.url());
      KMessageBox::error((QWidget*)parent(), msg);
      return;
    }
    debug("KIconEditIcon::open - backup created");
  } // not recovered

  FILE *fs = lock(tmp.data());

  if(!fs)
  {
    QString msg = i18n("KIcon: There was a error locking:\n%1\n").arg(tmp);
    KMessageBox::error((QWidget*)parent(), msg);
    removeFile(tmp);
    return;
  }

  debug("KIconEditIcon::open - Before loading image");
  if(!img->load(tmp.data()))
  {
    QString msg = i18n("KIcon: There was a QT error loading:\n%1\n").arg(tmp);
    KMessageBox::error((QWidget*)parent(), msg);
    removeFile(tmp);
    return;
  }

  // hack to make pixels opaque
  QString format = QImage::imageFormat(tmp.data());
  if(format == "JFIF")
  {
    *img = img->convertDepth(32);
    for(int y = 0; y < img->height(); y++)
    {
      uint *l = (uint*)img->scanLine(y);
      for(int x = 0; x < img->width(); x++, l++)
      {
        if(*l < 0xff000000) // the dnd encoding stuff turns off the opaque bits
          *l = *l | 0xff000000;
      }
    }
  }

  debug("KIconEditIcon::open - Image loaded");
  unlock(f);
  f = fs;
  _backup = tmp;
  _url = _turl.url();

  debug("KIcon: _backup: %s", _backup.data());
  debug("KIcon: _url: %s", _url.data());
  emit loaded(img);
  emit newname(_url.data());
  debug("KIconEditIcon::open - done");
}

// Prompts the user for a file to open.
void KIconEditIcon::promptForFile(const QImage *img)
{
  debug("KIconEditIcon::promptForFile(const QImage *img)");
  
  QString filter = i18n("*|All files (*)\n"
                        "*.xpm|XPM (*.xpm)\n"
                        "*.gif|GIF files (*.gif)\n"
                        "*jpg|JPEG files (*.jpg)\n");
  
  KURL url = KFileDialog::getOpenURL( getenv("HOME"), filter );
  
  if( url.isEmpty() )
    return;
  
  QString tempFile;

  KIO::NetAccess::download( url, tempFile );
  open( img, tempFile.data() );
  KIO::NetAccess::removeTempFile( tempFile );
}

void KIconEditIcon::checkUnNamedBackup(const QImage *image)
{
  QImage *img = (QImage*)image;
  bool r;
  QString tmp = kapp->checkRecoverFile("UnNamed.xpm", r);

  if(r)
  {
    debug("KIcon: Found recover file");
    QString msg = i18n("The KDE Icon Editor has found a backup\n"
"file for an unsaved icon.\nDo you want to open this?");
    if(KMessageBox::questionYesNo((QWidget*)parent(), msg) == 0)
    {
      debug("Opening recover file for: %s", tmp.data());
      if(!img->load(tmp.data()))
      {
        QString msg = i18n("There was a QT error loading the backup file:\n%1\n"
"Do you wan't to remove this backup file?\n").arg(tmp);
        if(KMessageBox::questionYesNo((QWidget*)parent(), msg) == 0)
          return;
      }

      debug("KIconEditIcon::checkForUnNamed - Image loaded");
      _backup = tmp;
      emit loaded(img);
      emit newname(tmp.data());
    }
  }
}


// only used for backups under a crash
void KIconEditIcon::saveBackup(const QImage *image, const QString &_filename)
{
  if(!image || image->isNull())
    return;

  QString filename = _filename;

  if(filename.isEmpty())
  {
    if(_url.isEmpty())
      filename = "UnNamed.xpm";
    else
      filename = _url.data();
  }

  QString str = kapp->tempSaveName(filename);

  QImage *img = (QImage*)image, *tmp;
  img->setAlphaBuffer(true);
  tmp = new QImage(img->convertDepth(8, ColorOnly|AvoidDither));
  CHECK_PTR(tmp);
  tmp->setAlphaBuffer(true);
  tmp->save(str.data(), "XPM");
  delete tmp;
}

void KIconEditIcon::save(const QImage *image, const QString &_filename)
{
  debug("KIconEditIcon::save");
/*
  if(busyDownloading)
  {
    KMessageBox::error((QWidget*)parent(),
      i18n("We've already got a download running.\nPlease be patient."));
    return;    
  }
*/
  QString filename = _filename;
  if(filename.isEmpty())
  {
    if(_url.isEmpty())
    {
      saveAs(image);
      return;
    }
    else
      filename = _url;
  }

  QImage *img = (QImage*)image, *tmp;
  img->setAlphaBuffer(true);
  tmp = new QImage(img->convertDepth(8, ColorOnly|AvoidDither));
  CHECK_PTR(tmp);
  tmp->setAlphaBuffer(true);

  QString str = kapp->tempSaveName(filename);

  QImageIO iio;
  iio.setImage(*img);
  iio.setFileName(str);
  iio.setFormat("XPM");
  iio.setDescription("Created by the KDE Icon Editor");
  
  if(iio.write())
  {
    debug("KIconEditIcon::save - backup image written: %s", str.data());
    KURL _turl(filename);
    if(_turl.isLocalFile())
    {
/*
      if(QFile::exists(filename))
      {
        QString msg = i18n("The file:\n%1\nallready exist. Overwrite it?").arg(filename);
        if(KMessageBox::questionYesNo((QWidget*)parent(), msg) == 0)
        {
          removeFile(str.data());
          delete tmp;
          return;
        }
      }
*/
      if(!copyFile(str, _turl.path()))
      {
        QString msg = i18n("There was an error creating a backup for:\n%1\n").arg(_url);
        KMessageBox::error((QWidget*)parent(), msg);
        delete tmp;
        return;
      }
    } // local
    else
    {
      KIO::NetAccess::download( str, filename );
    }
    if(_backup != str)
    {
      removeFile(_backup);
      _backup = str.copy();
    }
    emit saved();
    emit newmessage(i18n("Done saving"));
    _url = filename;
    emit newname(_url.data());
  }
  else // if(iio.write(...
  {
    QString msg = i18n("There was an error saving:\n%1\n").arg(_url);
    KMessageBox::error((QWidget*)parent(), msg);
    debug("KIconEditIcon::save - %s", msg.data());
  }
  delete tmp;
  debug("KIconEditIcon::save - done");
}

void KIconEditIcon::saveAs(const QImage *image)
{
  debug("KIconEditIcon::saveAs");
  
  KURL url = KFileDialog::getSaveURL(_lastdir.data(), "*.xpm");
  
  if( url.isEmpty() )
    return;
  
  if( !url.isLocalFile() )
  {
    KMessageBox::sorry( 0L, i18n( "Only saving to local files supported yet." ) );
    return;
  }
    
  save( image, url.path().data() );
}

FILE *KIconEditIcon::lock(const QString &file)
{
  QCString fname = QFile::encodeName(file);

  FILE *fs = fopen(fname.data(), "r+");
  if (!fs) 
  {
    debug("Cannot open file `%s': %s", fname.data(), 
	  strerror(errno));
    return 0;
  }

  int rc;
#if !HAVE_FLOCK
  struct flock fl;
  fl.l_type=F_WRLCK;
  fl.l_whence=0;
  fl.l_start=0;
  fl.l_len=0;
#endif


#if HAVE_FLOCK
  rc = flock(fileno(fs), LOCK_NB|LOCK_EX);
#else
  rc = fcntl(fileno(fs), F_SETLK, &fl);
#endif

  if (rc < 0)
  {
    debug("Cannot lock `%s': %s (%d)", fname.data(),
	  strerror(errno), errno);
#if HAVE_FLOCK
    rc = flock(fileno(fs), LOCK_UN);
#else
    fl.l_type = F_UNLCK;
    rc = fcntl(fileno(fs), F_SETLK, &fl);
#endif
    return 0;
  }
  debug("Locked %d %s", fileno(fs), fname.data());
  return fs;
}

bool KIconEditIcon::unlock(FILE *fs)
{
  if(!fs)
    return false;

  int rc;
#if !HAVE_FLOCK
  struct flock fl;
  fl.l_type=F_UNLCK;
  fl.l_whence=0;
  fl.l_start=0;
  fl.l_len=0;
#endif

#if HAVE_FLOCK
  debug("Using flock");
  rc = flock(fileno(fs), LOCK_UN);
#else
  debug("Using fcntl");
  rc = fcntl(fileno(fs), F_SETLK, &fl);
  //rc = fcntl(fileno(fs), F_SETLK, F_UNLCK);
#endif

  if(rc == 0)
  {
    debug("Unlocked : %d", fileno(fs));
    fclose(fs);
    fs = 0;
    return true;
  }
  debug("Cannot unlock descriptor %d: %s (%d)", fileno(fs), strerror(errno), errno);
  
  return false;
}
