//==============================================
//  copyright            : (C) 2003-2005 by Will Stokes
//==============================================
//  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.
//==============================================

//Systemwide includes
#include <qimage.h>
#include <qpixmap.h>
#include <qstring.h>
#include <qstringlist.h>
#include <time.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qtextstream.h>
#include <qdom.h>
#include <qdir.h>
#include <qapplication.h>
#include <qregexp.h>
#include <qdatetime.h>
#include <math.h>

//Projectwide includes
#include "album.h"
#include "subalbum.h"
#include "photo.h"
#include "tools/imageTools.h"
#include "tools/fileTools.h"
#include "tools/md5.h"
#include "tools/xmlTools.h"
#include "../config.h"
#include "../gui/subalbumPreviewWidget.h"
#include "../gui/statusWidget.h"

//==============================================
///Sets default information
Album::Album( QString tmpDir, bool createSubalbum )
{
  //set strings to default values
  name = "";
  description ="";
  author = ""; 
  theme = "Slick";
  this->tmpDir = tmpDir;

  //by default no representative image
  smallRepresentativeImage = NULL;
  largeRepresentativeImage = NULL;

  //no Subalbums by default
  firstSubalbum = NULL;
  lastSubalbum = NULL;

  //set the creation/modification date to today
  updateCreationDate();
  updateModificationDate();

  //no subalbums
  numSubalbums = 0;
  numLoadedSubalbums = 0;

  //not previously saved by default
  savedToDisk = false;

  //set default save location (where images are placed before saving) to the tmp directory
  saveLocation = getTmpDir();

  if(createSubalbum)
  {
    Subalbum* s = new Subalbum( this, 1 );
    appendSubalbum( s );
  }
  
  //no interesting modifications yet
  modified = false;

  nextUniqueID = 0;
}
//==============================================
Album::~Album()
{
  //delete representative image
  delete smallRepresentativeImage;
  delete largeRepresentativeImage;

  //delete subalbums
  Subalbum* current = firstSubalbum;
  Subalbum* temp;
  while(current != NULL)
  {
    temp = current->getNext();
    delete current;
    current = temp;
  }

  //remove all old tmp dir contents and directory itself
  if(!tmpDir.isNull())
  {
    QDir oldTmpDir(tmpDir);
    QString tmpDirName = oldTmpDir.dirName();
    QStringList strLst = oldTmpDir.entryList();
    QStringList::iterator it;
    for(it = strLst.begin(); it != strLst.end(); it++)
    {
      oldTmpDir.remove(tmpDir + "/" + *it);
    }
    oldTmpDir.cdUp();
    oldTmpDir.rmdir( tmpDirName );
  }
}
//==============================================
int Album::getModificationYear()  { return modificationYear;     }
int Album::getModificationMonth() { return modificationMonth;    }
int Album::getModificationDay()   { return modificationDay;      }

int Album::getCreationYear()      { return creationYear;         }
int Album::getCreationMonth()     { return creationMonth;        }
int Album::getCreationDay()       { return creationDay;          }

QString Album::getName()          { return QString(name);        }
QString Album::getDescription()   { return QString(description); }
QString Album::getAuthor()        { return QString(author);      }
//==============================================
QPixmap* Album::getRepresentativeImage(int size)
{
  if(size == SMALL)      return smallRepresentativeImage;
  else if(size == LARGE) return largeRepresentativeImage;
  else                   return NULL;
}
//==============================================
Subalbum* Album::getFirstSubalbum() { return firstSubalbum; }
Subalbum* Album::getLastSubalbum()  { return lastSubalbum;  }
//==============================================
bool Album::prevSave()      { return savedToDisk; }
bool Album::albumModified() { return modified;    }
//==============================================
QString Album::getSaveLocation() { return saveLocation; }
QString Album::getTmpDir()       { return tmpDir;       }
QString Album::getTheme()        { return theme;        }
int Album::getNumSubalbums()     { return numSubalbums; }
//==============================================
int Album::getNumPhotos()
{
  //compute number of photos and size on disk
  int numPhotos = 0;
  Subalbum* curr = firstSubalbum;
  while(curr != NULL)
  {
    numPhotos+= curr->getNumPhotos();
    curr = curr->getNext();
  }
  return numPhotos; 
}
//==============================================
void Album::setName(QString val)
{
  if(name != val)
  {
    name = val;
    modified = true;
  }
}
//==============================================
void Album::setDescription(QString val)
{
  if(description != val)
  {
    description = val;
    modified = true;
  }
}
//==============================================
void Album::setAuthor(QString val)
{
  if(author != val)
  {
    author = val;
    modified = true;
  }
}
//==============================================
void Album::setRepresentativeImages(QString imageFilename)
{
  //delete representative images
  delete smallRepresentativeImage;
  delete largeRepresentativeImage;

  //if being set to null, set back to defaults
  if(imageFilename.isNull())
  {
    smallRepresentativeImage = NULL;
    largeRepresentativeImage = NULL;
  }
  else
  {
    //compute representative image sizes
    int imageWidth, imageHeight;
    getImageSize( imageFilename, imageWidth, imageHeight );
    
    int smallRepWidth = 0;
    int smallRepHeight = 0;
    int largeRepWidth = 0;
    int largeRepHeight = 0;
    calcScaledImageDimensions( imageWidth, imageHeight,
                               107, REP_IMAGE_HEIGHT,
                               smallRepWidth, smallRepHeight);
    calcScaledImageDimensions( imageWidth, imageHeight,
                               500, 320,
                               largeRepWidth, largeRepHeight);

    //create various representative images

    //copy and scale small version
    QImage thumbnailSmall;
    scaleImage( imageFilename, thumbnailSmall, smallRepWidth, smallRepHeight );
    smallRepresentativeImage = new QPixmap( thumbnailSmall.width(), thumbnailSmall.height() );
    smallRepresentativeImage->convertFromImage( thumbnailSmall );

    //copy and scale large version
    QImage thumbnailLarge;
    scaleImage( imageFilename, thumbnailLarge, largeRepWidth, largeRepHeight );
    largeRepresentativeImage = new QPixmap( thumbnailLarge.width(), thumbnailLarge.height() );
    largeRepresentativeImage->convertFromImage( thumbnailLarge );
  }

  //set modified
  modified = true;
}
//==============================================
void Album::appendSubalbum(Subalbum* val)
{
  //if passed a null pointer bail!
  if( val == NULL) return;

  //empty list - stick on front
  if(firstSubalbum == NULL)
  {
    firstSubalbum = val;
    lastSubalbum = val;
  }
  //else - append to end
  else
  {
    lastSubalbum->setNext( val );
    val->setPrev( lastSubalbum );
    lastSubalbum = val;
  }

  numSubalbums++;
  modified = true;
}
//==============================================
void Album::removeSubalbum(Subalbum* val)
{
  //if passed a null pointer bail!
  if( val == NULL) return;
  
  //reset head and tail pointers if necessary
  if( val == firstSubalbum ) firstSubalbum = val->getNext();
  if( val == lastSubalbum )  lastSubalbum  = val->getPrev();
  
  //split out
  if( val->getPrev() != NULL ) val->getPrev()->setNext( val->getNext() );
  if( val->getNext() != NULL ) val->getNext()->setPrev( val->getPrev() );
  
  //delete object
  delete val;
  val = NULL;
  numSubalbums--;
  modified = true;
}
//==============================================
void Album::updateCreationDate()
{
  //set creation date to today
  QDate date    = QDate::currentDate();
  creationYear  = date.year();
  creationMonth = date.month();
  creationDay   = date.day();
}
//==============================================
void Album::updateModificationDate()
{
  //set last modification date to today
  QDate date        = QDate::currentDate();
  modificationYear  = date.year();
  modificationMonth = date.month();
  modificationDay   = date.day();
}
//==============================================
int Album::importFromDisk(StatusWidget* status, QString fileName, bool disableCheckPhotoMods)
{
  //update file
  updateXML( QFileInfo(fileName).dirPath(TRUE) );

  //open file
  QFile albumFile( fileName );

  //unable to open xml file? alert user
  if( !albumFile.open( IO_ReadOnly ) )
    return ALBUM_READ_ERROR;

  //parse dom
  QDomDocument albumDom;
  if( !albumDom.setContent( &albumFile ) )
    return ALBUM_XML_ERROR;

  //close file
  albumFile.close();

  //get main directory all other files and subdirectories are in
  QString rootDir = QFileInfo(albumFile).dirPath(TRUE);
  saveLocation = rootDir + "/img";

  //if representative image exists load
  QImage repImage(rootDir + "/img/album.jpg");
  if(!repImage.isNull())
  {
    setRepresentativeImages( rootDir + "/img/album.jpg");
  }

  //count number of photos in album, needed for showing loading progress
  int numPhotos = 0;
  QDomElement root = albumDom.documentElement();
  QDomNode node = root.firstChild();
  while( !node.isNull() )
  {
    if( node.isElement() && node.nodeName() == "subalbum" )
    {
      QDomNode childNode = node.firstChild();
      while( !childNode.isNull() )
      {
        if( childNode.isElement() && childNode.nodeName() == "photo" )
          numPhotos++;
        childNode = childNode.nextSibling();
      }
    }
    node = node.nextSibling();
  }

  //setup progress bar
  status->showProgressBar( StatusWidget::tr("Loading:"), numPhotos );
  qApp->processEvents();

  int subalbumNum = 0;

  //get root node and start parsing DOM
  root = albumDom.documentElement();
  node = root.firstChild();
  QDomText val;
  while( !node.isNull() )
  {
    //------------------------------------------------------------
    //album name
    if( node.isElement() && node.nodeName() == "name" )
    {
      val = node.firstChild().toText();
      if(!val.isNull())
        name = val.nodeValue();
     name.replace("\\&quot;","\"");
    }
    //------------------------------------------------------------
    //album description
    else if( node.isElement() && node.nodeName() == "description" )
    {
      val = node.firstChild().toText();
      if(!val.isNull())
        description = val.nodeValue();
     description.replace("\\&quot;","\"");
    }
    //------------------------------------------------------------
    //album author
    else if( node.isElement() && node.nodeName() == "author" )
    {
      val = node.firstChild().toText();
      if(!val.isNull())
        author = val.nodeValue();
     author.replace("\\&quot;","\"");
    }
    //------------------------------------------------------------
    //album theme
    else if( node.isElement() && node.nodeName() == "theme" )
    {
      val = node.firstChild().toText();
      if(!val.isNull())
        theme = val.nodeValue();
     theme.replace("\\&quot;","\"");
    }
    //------------------------------------------------------------
    //album creation date
    else if( node.isElement() && node.nodeName() == "created" )
    {
      val = node.firstChild().toText();

      //split value based on spaces, should be 7 fields
      QStringList vals = QStringList::split( QRegExp(" "), val.nodeValue() );
      int i=0;
      int intVals[3];
      QStringList::Iterator it;
      for ( it = vals.begin(); it != vals.end(); ++it )
      {
        intVals[i] = QString(*it).toInt();
        i++;
        //only read first 3 entires, year/month/day, don't overwrite
        //buffer on addition entries if xml messed up
        if(i > 2)
          break;
      }
      creationYear = intVals[0];
      creationMonth = intVals[1];
      creationDay = intVals[2];
    }
    //------------------------------------------------------------
    //subalbum
    else if( node.isElement() && node.nodeName() == "subalbum" )
    {
      //increase counter
      subalbumNum++;

      //create new subalbum
      Subalbum* salbum = new Subalbum(this, numSubalbums+1);

      //populate it
      salbum->importFromDisk( &node, subalbumNum, status, (rootDir + "/"), disableCheckPhotoMods );

      //append it to list of subalbums
      appendSubalbum(salbum);
    }
    //------------------------------------------------------------
    //advance to next node
    node = node.nextSibling();
    //------------------------------------------------------------
  }

  //reset number of loaded subalbums
  numLoadedSubalbums = numSubalbums;

  //hide progress bar
  status->setStatus( qApp->translate("Album", "Album loaded.") );

  //save load directory name and loaded/saved bit
  saveLocation = rootDir;
  savedToDisk = true;

  return ALBUM_LOADED;
}
//==============================================
int Album::exportToDisk(StatusWidget* status, QString dirName, QString themeName)
{
  //check to see if save location has actually changed, if not don't force save images
  //this occurs when user blindly selects the same old spot, or is just changing the theme used by default
  bool forceSave = true;

  if(saveLocation == dirName)
    forceSave = false;

  //backup theme and save location, if save fails revert to previous values
  oldSaveLocation = saveLocation;
  QString oldTheme = theme;

  //attempt to save album
  saveLocation = dirName;
  theme = themeName;
  int result = exportToDisk(status, forceSave);

  //if album saving failed revert save location and theme
  if(result != ALBUM_EXPORTED)
  {
    saveLocation = oldSaveLocation;
    theme = oldTheme;
  }
  //else update tmp save dir
  else
  {
    //remove all old tmp dir contents and directory itself
    QDir oldTmpDir(tmpDir);
    QString tmpDirName = oldTmpDir.dirName();
    QStringList strLst = oldTmpDir.entryList();
    QStringList::iterator it;
    for(it = strLst.begin(); it != strLst.end(); it++)
    {
      oldTmpDir.remove( tmpDir + "/" + *it);
    }

    oldTmpDir.cdUp();
    oldTmpDir.rmdir( tmpDirName );

    //create and set new temp dir location
    QDir saveDir( saveLocation );
    if(!saveDir.exists( "tmp" ))
      saveDir.mkdir( "tmp" );
    tmpDir = saveLocation + "/tmp";
    
    //reset unique id counter
    nextUniqueID = 0;
  }

  //return result
  return result;
}
//==============================================
int Album::exportToDisk(StatusWidget* status, bool forceSave)
{
  //------------------------------------------
  //create subdirs
  QDir localDir(saveLocation);
  //img dirs
  localDir.mkdir("img");
  //subalbum dirs
  localDir.setPath(saveLocation + "/img");

  //make a temporary 0 directory for copying new images, they'll be moved to their final
  //location during the reordering step
  localDir.mkdir( "0" );
  
  //iterate over each subalbum and create its image directory
  Subalbum* current = firstSubalbum;  
  int collectionNum = 0;
  while(current != NULL)
  {
    collectionNum++;
    QString dirName = QString("%1") .arg( collectionNum );
    localDir.mkdir(dirName);
    current = current->getNext();
  }
  //------------------------------------------
  //checks worked, go ahead with export

  //count number of photos
  int totalPhotos=0;
  current = firstSubalbum;
  while(current != NULL)
  {
    totalPhotos+=current->getNumPhotos();
    current = current->getNext();
  }

  //setup progress bar
  status->showProgressBar( StatusWidget::tr("Saving:"), 4*totalPhotos );
  qApp->processEvents();

  //copy over theme resources
  exportThemeResources( theme );

  //export album cover image, subalbum thumbnails
  exportTopLevelImages();

  //export subalbum images (thumbnail, slideshow, and full versions of all images)
  exportSubalbumImages(status, forceSave);

  //remove any _orig images for photos which have been reverted to their original form
  removeStagnantOrigFiles(status);
  
  //apply reordering to all images
  reorderSubalbumImages(status);

  //reset subalbum numbers to current ordering
  current = firstSubalbum;
  int n=0;
  while(current !=NULL)
  {
    n++;
    current->setSubalbumNumber(n);
    current = current->getNext();
  }

  //remove collection 0 directory
  QDir rootDir(saveLocation + "/img/");
  rootDir.rmdir( "0" );
  
  //remove old images that nolonger belong
  removeStagnantImages();

  //remove previous html/htm files
  localDir.setPath(saveLocation);
  QStringList list = localDir.entryList( QDir::Files );
  QStringList::Iterator file;
  for ( file = list.begin(); file != list.end(); ++file )
  {
    if( (*file).endsWith(".html") || (*file).endsWith(".htm") )
      localDir.remove( saveLocation + "/" + *file );
  }

  //export xml structure of album
  int result = exportToXML(status, saveLocation);
  if(result != ALBUM_EXPORTED) { return result; }

  //export various html pages using selected theme
  transformXMLtoHTML( saveLocation, theme, false );

  //------------------------------------------
  //remove files from temp folder
  QDir tmpDirHandle( getTmpDir() );
  QStringList strLst = tmpDirHandle.entryList();
  QStringList::iterator it;
  for(it = strLst.begin(); it != strLst.end(); it++)
  {
    tmpDirHandle.remove( getTmpDir() + "/" + *it);
  }
  //------------------------------------------
  savedToDisk = true;

  //just saved so no modifications since last save
  modified = false;

  //hide progress bar
  status->setStatus( qApp->translate("Album", "Album saved.") );
  //------------------------------------------
  return ALBUM_EXPORTED;
}
//==============================================
int Album::exportCompressedWebAlbum(StatusWidget* status, 
                                    QString exportLocation, 
                                    QString exportMessage)
{
  //------------------------------------------
  //copy all images
  QDir localDir(exportLocation);
  localDir.mkdir("img");
  localDir.setPath(exportLocation + "/img");
  
  //copy album image
  if(getRepresentativeImage(LARGE) != NULL)
  { getRepresentativeImage(LARGE)->save(exportLocation + "/img/album.jpg", "JPEG", 95); }
  else
  { localDir.remove(exportLocation + "/img/album.jpg"); }
    
  int numPhotos = getNumPhotos();  
  int photosLeft = numPhotos;  
  int updateInverval = numPhotos / 50;
  int updateCount = 0;
  
  //iterate over each collection
  Subalbum* curCollection = firstSubalbum;
  int collectionNum=1;
  while(curCollection != NULL)
  {
    QString collectionDir = QString("%1").arg( collectionNum );
    localDir.mkdir( collectionDir );

    //copy collection image
    QString collectionThumbFilename = QString(exportLocation + "/img/%1_thumb.jpg" ).arg(collectionNum);
    if(curCollection->getRepresentativeImage(LARGE) != NULL )
    { curCollection->getRepresentativeImage(LARGE)->save( collectionThumbFilename, "JPEG", 95); }
    else
    { localDir.remove( collectionThumbFilename ); }
    
    //copy each photo
    Photo* curPhoto = curCollection->getFirst();
    int photoNum = 1;
    while(curPhoto != NULL)
    {
      //update status message
      status->updateProgress( numPhotos - photosLeft, exportMessage.arg( photosLeft ) );
      
      //make sure events are processed every 2% of the photos that are processes
      updateCount++;
      if(updateCount > updateInverval)
      {
        updateCount = 0;
        qApp->processEvents();        
      }      
      
      //copy files
      QString newFilePath = QDir::convertSeparators( exportLocation + "/img/" + 
                                                     collectionDir + "/" + 
                                                     QString("%1").arg(photoNum) );

      copyFile( curPhoto->getSlideshowFilename(), newFilePath + "_slideshow.jpg" );
      copyFile( curPhoto->getThumbnailFilename(), newFilePath + "_thumb.jpg" );
      
      curPhoto = curPhoto->getNext();
      photoNum++;
      photosLeft--;
    }
    
    curCollection = curCollection->getNext();
    collectionNum++;
  }
  //------------------------------------------
  //copy theme resources
  QStringList fileList;
  QStringList::Iterator file;
  
  //create HTML and misc resources directories
  localDir.setPath(exportLocation);
  localDir.mkdir("resources");
  
  //remove all files in these directories from previous saves with other themes
  localDir.setPath(exportLocation + "/resources");
  fileList = localDir.entryList( QDir::Files );
  for ( file = fileList.begin(); file != fileList.end(); ++file )
  { localDir.remove( exportLocation + "/resources/" + *file ); }
  
  //copy files over from theme's directory
  localDir.setPath(THEMES_PATH + theme + "/resources");
  fileList = localDir.entryList( QDir::Files );
  for ( file = fileList.begin(); file != fileList.end(); ++file )
  { copyFile( THEMES_PATH + theme + "/resources/" + *file, exportLocation + "/resources/" + *file); }
  //------------------------------------------
  //export xml file
  exportToXML(status, exportLocation);
  //------------------------------------------  
  //remove previous html/htm files
  localDir.setPath(exportLocation);
  fileList = localDir.entryList( QDir::Files );
  for ( file = fileList.begin(); file != fileList.end(); ++file )
  {
    if( (*file).endsWith(".html") || (*file).endsWith(".htm") )
      localDir.remove( exportLocation + "/" + *file );
  }
  //------------------------------------------
  //construct html files
  transformXMLtoHTML( exportLocation, theme, true );
  //------------------------------------------
  //remove xml file
  localDir.remove( exportLocation + "/Album.xml" );  
  //------------------------------------------
  return ALBUM_EXPORTED;
}
//==============================================
int Album::exportLargeImages(StatusWidget* status, QString exportPath, QString exportMessage)
{
  //determine number of digits collecion # requires
  uint collectionDigits = (uint) (1 + log( (double) getNumSubalbums() ) / log( 10.0 ) );
  
  //determine number of digits photo # requires, this
  //involves walking through the album and finding the collection with the most phots first
  int mostPhotos = 0;  
  Subalbum* curCollection = getFirstSubalbum();
  while(curCollection != NULL )
  {
    mostPhotos = QMAX( mostPhotos, curCollection->getNumPhotos() );
    curCollection = curCollection->getNext(); 
  }
  uint photoDigits = (uint) ( 1 + log( (double) mostPhotos ) / log( 10.0 ) );   
  //------------
  //copy files  
  int numPhotos = getNumPhotos();  
  int photosLeft = numPhotos;  
  
  int collectionNum = 1;
  curCollection = getFirstSubalbum();
  
  int updateInverval = numPhotos / 50;
  int updateCount = 0;
  
  while(curCollection != NULL )
  {
    //construct collection string
    QString collectionString = QString("%1").arg(collectionNum);
    while(collectionString.length() < collectionDigits)
    { collectionString = "0" + collectionString; }  
    
    //copy all photos in collection
    int photoNum = 1;
    Photo* curPhoto = curCollection->getFirst();
    while(curPhoto != NULL)
    {
      //update status message
      status->updateProgress( numPhotos - photosLeft, exportMessage.arg( photosLeft ) );
      
      //make sure events are processed every 2% of the photos that are processes
      updateCount++;
      if(updateCount > updateInverval)
      {
        updateCount = 0;
        qApp->processEvents();        
      }

      //construct photo string
      QString photoString = QString("%1").arg(photoNum);
      while(photoString.length() < photoDigits)
      { photoString = "0" + photoString; }  
      
      //construct new photo path
      QString newFilePath = QDir::convertSeparators( exportPath + "/" + collectionString + 
                                                     "_" + photoString + ".jpg" );
      //copy file
      copyFile( curPhoto->getImageFilename(), newFilePath );
      
      //move on to next file
      photosLeft--;
      curPhoto = curPhoto->getNext();
      photoNum++;
      
    } //while photo
    
    //move on to next collection
    curCollection = curCollection->getNext();
    collectionNum++;    
  }// while collection
   //------------
  return ALBUM_EXPORTED;
}
//==============================================
int Album::exportToXML(StatusWidget* status, QString exportPath)
{
  //update modification date
  updateModificationDate();

  //create/open xml file
  QFile file( exportPath + "/Album.xml" );
  if(file.open(IO_WriteOnly))
  {
    //-----
    QTextStream stream;
    stream.setDevice( &file );
    stream.setEncoding( QTextStream::UnicodeUTF8 );
    
    //write album information
    stream << "<?xml version=\"1.0\"?>\n";
    stream << "<album version=\"1.1\">\n";
    stream << "  <name>" << fixXMLString(name) << "</name>\n";
    stream << "  <description>" << fixXMLString(description) << "</description>\n";
    stream << "  <author>" << fixXMLString(author) << "</author>\n";
    stream << "  <created>" << creationYear << " " << creationMonth << " " << creationDay << "</created>\n";
    stream << "  <modified>" << modificationYear << " " << modificationMonth << " " << modificationDay << "</modified>\n";
    stream << "  <theme>" << theme << "</theme>\n";
    stream << "  <thumbnailDimensions>" << THUMBNAIL_WIDTH << " " << THUMBNAIL_HEIGHT << "</thumbnailDimensions>\n";
    stream << "  <slideshowDimensions>" << SLIDESHOW_WIDTH << " " << SLIDESHOW_HEIGHT << "</slideshowDimensions>\n";

    //if album has a represenatative image save it's path
    if(getRepresentativeImage(LARGE) != NULL )
    {
      stream << "  <thumb path=\"img/album.jpg\"/>\n";
    }

    //write subalbums
    Subalbum* current = firstSubalbum;
    while(current != NULL)
    {
      current->exportToXML(status, stream);
      current = current->getNext();
    }

    //end album
    stream << "</album>\n";
    file.close();

    return ALBUM_EXPORTED;
  }
  else
  {
    return ALBUM_ERROR_OPEN_FILE;
  }
}
//==============================================
void Album::exportTopLevelImages()
{
  //if image set export it
  if(getRepresentativeImage(LARGE) != NULL)
  {
    getRepresentativeImage(LARGE)->save(saveLocation + "/img/album.jpg", "JPEG", 95);
  }
  //else make sure any previously set images are removed
  else
  {
    QDir rootDir(saveLocation + "/img/");
    rootDir.remove(saveLocation + "/img/album.jpg");
  }

  //export subalbum thumbs
  int n=0;
  Subalbum* current = firstSubalbum;
  while(current != NULL)
  {
    n++;
    //if subalbum has representative image export it
    if(current->getRepresentativeImage(LARGE) != NULL )
    {
      QString fileName = QString(saveLocation + "/img/%1_thumb.jpg" ).arg(n);
      current->getRepresentativeImage(LARGE)->save(fileName, "JPEG", 95);
    }
    //otherwise make sure anyprevious set images are removed
    else
    {
      QDir rootDir(saveLocation + "/img/");
      rootDir.remove( saveLocation + QString("/img/%1_thumb.jpg").arg(n) );
    }
    current = current->getNext();
  }
}
//==============================================
void Album::exportSubalbumImages(StatusWidget* status, bool forceSave)
{
  //iterate over all subalbums
  int subalbumNumber=0;
  Subalbum* currentSubalbum = firstSubalbum;
  while(currentSubalbum != NULL)
  {
    subalbumNumber++;

    //iterate over all photos in this subalbum
    int photoNumber=0;
    Photo* currentPhoto = currentSubalbum->getFirst();
    while(currentPhoto != NULL)
    {
      photoNumber++;
      //---------------------------------------
      //if the current photo does not need to be saved then move on
      if( !forceSave && !currentPhoto->getNeedsSavingVal() )
      {
        currentPhoto = currentPhoto->getNext();
        status->incrementProgress();
        qApp->processEvents();
        continue;
      }
      //---------------------------------------
      //get initial photo # and subalbum #, used for saving
      int initPhotoNumber = currentPhoto->getInitialPhotoNumber();
      int initSubalbumNumber = currentPhoto->getInitialSubalbumNumber();
      //---------------------------------------
      //export thumbnail image
      QString oldName = currentPhoto->getThumbnailFilename();
      QString newName = QString(saveLocation + "/img/%1/%2_thumb.jpg" )
                        .arg(initSubalbumNumber).arg(initPhotoNumber);
      
      //if file has been modified move from current location to final location
      if( currentPhoto->getNeedsSavingVal() ) { moveFile( oldName, newName ); }
      //If file has not been modified we must be doing a save-as and saving has been forced. In this case
      //COPY file from current location to final location, DON'T delete previous copy!!!
      else { copyFile(oldName, newName); }

      //compute and store md5 for slideshow image
      std::ifstream thumbnailFile( QFile::encodeName(newName) );
      if(thumbnailFile.is_open())
      {
        currentPhoto->setThumbnailChecksum( getMD5(thumbnailFile) );
        thumbnailFile.close();
      }
      //---------------------------------------
      //export slideshow image
      oldName = currentPhoto->getSlideshowFilename();
      newName = QString(saveLocation + "/img/%1/%2_slideshow.jpg" )
                        .arg(initSubalbumNumber).arg(initPhotoNumber);

      //if file has been modified move from current location to final location
      if( currentPhoto->getNeedsSavingVal() ) { moveFile( oldName, newName ); }
      //If file has not been modified we must be doing a save-as and saving has been forced. In this case
      //COPY file from current location to final location, DON'T delete previous copy!!!
      else { copyFile(oldName, newName); }

      //compute and store md5 for slideshow image
      std::ifstream slideshowFile( QFile::encodeName(newName) );
      if(slideshowFile.is_open())
      {
        currentPhoto->setSlideshowChecksum( getMD5(slideshowFile) );
        slideshowFile.close();
      }
      //---------------------------------------
      //export full size image
      oldName = currentPhoto->getImageFilename();
      newName = QString(saveLocation + "/img/%1/%2.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);

      //if file has been modified move from current location to final location
      if( currentPhoto->getNeedsSavingVal() ) 
      {
        QString tempOrigName = getTmpDir() + QString("/%1_%2_orig.jpg")
        .arg(initSubalbumNumber).arg(initPhotoNumber);
                     
        QString finalOrigName = QString(saveLocation + "/img/%1/%2_orig.jpg" )
          .arg(initSubalbumNumber).arg(initPhotoNumber);

        ///Before we move the file we must be sure to preserve the photos original format.
        ///if the photo was not recently reverted (if it was then we're saving out
        ///the original form so no need to backup) and the file has previously been saved and
        ///an orig file does not exist, we better backup the previously saved version quick
        QDir tmpDir;
        if( !currentPhoto->getRecentlyReverted() &&
            tmpDir.exists(newName) &&
            !tmpDir.exists(finalOrigName) )
        {
          moveFile( newName, finalOrigName );
        }      
        //if photo previously saved, there is no orig file, but the photo 
        //is modified and we're doing a force save than make sure to copy 
        //over the original as well even though it's not an orig file
        else if ( currentPhoto->getEverSaved() &&
                  currentPhoto->getNeedsSavingVal() &&
                  forceSave &&
                  saveLocation.compare( oldSaveLocation ) != 0 )
        {
          QString storedOrigLocation = oldSaveLocation +         
                                       QString("/img/%1/%2_orig.jpg").arg(currentPhoto->getInitialSubalbumNumber())
                                                                     .arg(currentPhoto->getInitialPhotoNumber());
          QString storedLocation = oldSaveLocation +         
                                   QString("/img/%1/%2.jpg").arg(currentPhoto->getInitialSubalbumNumber())
                                                            .arg(currentPhoto->getInitialPhotoNumber());
          
          if( tmpDir.exists(storedOrigLocation) )
            copyFile( storedOrigLocation, finalOrigName );
          else if( tmpDir.exists(storedLocation) )
            copyFile( storedLocation, finalOrigName );
        }
        ///If a photo has never been saved before, make sure to also move over any orig file if
        ///one exists. The presence of such a file indicates a photo was modified before it
        ///was ever saved, but the original form has been preseved and should be backed up at this
        ///time to allow a user to revert to the photos original form in the future.
        else if( !currentPhoto->getRecentlyReverted() &&
                 !tmpDir.exists(newName) &&
                 tmpDir.exists(tempOrigName) )
        {
          moveFile( tempOrigName, finalOrigName );
        }

        ///ok, now it's safe to move over currrent version of the photo
        moveFile( oldName, newName );
      }
      //If file does not need to be saved a force save is taking place. This occurs when a user chooses
      //save as and copies an entire album to a different location so all files must be copied. Make
      //sure to copy over the original form of the photo as well if this file exists
      else
      {
        //copy current image
        copyFile( oldName, newName );
        
        ///----
        //if orig file exists copy it too
        QDir tmpDir;
         
        QString tempOrigName = getTmpDir() + QString("/%1_%2_orig.jpg")
           .arg(initSubalbumNumber).arg(initPhotoNumber);

        QString curOrigName = currentPhoto->getImageFilename();
        curOrigName.truncate( curOrigName.length() - 4 );
        curOrigName = curOrigName + "_orig.jpg";

        QString finalOrigName = QString(saveLocation + "/img/%1/%2_orig.jpg" )
           .arg(initSubalbumNumber).arg(initPhotoNumber);

        //if the photo was recently reverted ignore the presence of orig files
        if( !currentPhoto->getRecentlyReverted() )
        {
          //if the photo was never previously saved and an orig file
          //exists in the tmp directory make sure to copy it over
          if( !currentPhoto->getEverSaved() &&
              tmpDir.exists( tempOrigName ) )
          {
            copyFile( tempOrigName, finalOrigName );
          }        
          //if the photo was previously saved and an orig file exists
          //in the previous save location make sure to copy it over
          else if( currentPhoto->getEverSaved() &&
                   tmpDir.exists( curOrigName ) )
          {
            copyFile( curOrigName, finalOrigName );
          }        
        }
        ///----
      }
      //---------------------------------------
      //compute and store md5 for image
      std::ifstream imageFile( QFile::encodeName(newName) );
      if(imageFile.is_open())
      {
        currentPhoto->setImageChecksum( getMD5(imageFile) );
        imageFile.close();
      }
      //---------------------------------------
      //set new storage locations of files
      currentPhoto->setImageFilename
        ( QString(saveLocation + "/img/%1/%2.jpg").arg(initSubalbumNumber).arg(initPhotoNumber) );
      
      currentPhoto->setSlideshowFilename
        ( QString(saveLocation + "/img/%1/%2_slideshow.jpg").arg(initSubalbumNumber).arg(initPhotoNumber) );
      
      currentPhoto->setThumbnailFilename
        ( QString(saveLocation + "/img/%1/%2_thumb.jpg").arg(initSubalbumNumber).arg(initPhotoNumber) );
      //---------------------------------------
      //set image as not needing saving and as being saved
      currentPhoto->setNeedsSavingVal(false);
      currentPhoto->setEverSaved(true);
      //---------------------------------------
      //update progress bar
      status->incrementProgress();
      qApp->processEvents();
      //---------------------------------------
      //move on to next photo in subalbum
      currentPhoto = currentPhoto->getNext();
      //---------------------------------------
    }
    //---------------------------------------
    //move on to next subalbum
    currentSubalbum = currentSubalbum->getNext();
  }
}
//==============================================
void Album::removeStagnantOrigFiles(StatusWidget* status)
{
  QDir tmpDir;
  
  //iterate over all collections
  Subalbum* currentSubalbum = firstSubalbum;
  while(currentSubalbum != NULL)
  {
    //iterate over all photos in this subalbum
    Photo* currentPhoto = currentSubalbum->getFirst();
    while(currentPhoto != NULL)
    {
      //if photo recently reverted and orig file is not the current filename remove orig file
      //the orig and current name will match up if a previously saved (but not modified) photo 
      //is modified, reverted, then saved out again
      if(currentPhoto->getRecentlyReverted() &&
         currentPhoto->getImageFilename().compare( currentPhoto->originalImageFilename() ) != 0 )
      {
        tmpDir.remove( currentPhoto->originalImageFilename() );
        currentPhoto->setRecentlyReverted( false );
      }
      
      //move on to next photo
      currentPhoto = currentPhoto->getNext();
      status->incrementProgress();
      qApp->processEvents();
    }
    
    //move on to next subalbum
    currentSubalbum  = currentSubalbum->getNext();
  }      
}
//==============================================
void Album::reorderSubalbumImages(StatusWidget* status)
{
  //--------------------------------------------------------
  //--------------------------------------------------------
  //first pass over all photos, those whose initial and current numbers don't match up
  //rename slightly so we don't overwrte them the second time around
  //--------------------------------------------------------
  //--------------------------------------------------------
  //iterate over all subalbums
  QDir tmpDir;
  int subalbumNumber=0;
  Subalbum* currentSubalbum = firstSubalbum;
  while(currentSubalbum != NULL)
  {
    subalbumNumber++;

    //iterate over all photos in this subalbum
    int photoNumber=0;
    Photo* currentPhoto = currentSubalbum->getFirst();
    while(currentPhoto != NULL)
    {
      photoNumber++;
      int initPhotoNumber    = currentPhoto->getInitialPhotoNumber();
      int initSubalbumNumber = currentPhoto->getInitialSubalbumNumber();

      //if photo has moved rename full image, orig image (if it exists), slideshow image, and thumbnail images
      if( initPhotoNumber != photoNumber || initSubalbumNumber != subalbumNumber)
      {
        QString oldName = QString(saveLocation + "/img/%1/%2.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        QString newName = QString(saveLocation + "/img/%1/%2_moved.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        moveFile( oldName, newName );
        //-----      
        oldName = QString(saveLocation + "/img/%1/%2_orig.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        newName = QString(saveLocation + "/img/%1/%2_orig_moved.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        if(tmpDir.exists(oldName) ) { moveFile( oldName, newName ); }
        //-----
        oldName = QString(saveLocation + "/img/%1/%2_slideshow.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        newName = QString(saveLocation + "/img/%1/%2_slideshow_moved.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        moveFile( oldName, newName );
        //-----
        oldName = QString(saveLocation + "/img/%1/%2_thumb.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        newName = QString(saveLocation + "/img/%1/%2_thumb_moved.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        moveFile( oldName, newName );
      }
      
      //move on to next photo
      currentPhoto = currentPhoto->getNext();
      status->incrementProgress();
      qApp->processEvents();
    }

    //move on to next subalbum
    currentSubalbum = currentSubalbum->getNext();
  }

  //--------------------------------------------------------
  //--------------------------------------------------------
  //second pass over all photos, those whose initial and current numbers don't match up
  //rename to their final names and reset initial photo and subalbum numbers
  //--------------------------------------------------------
  //--------------------------------------------------------
  //iterate over all subalbums
  subalbumNumber=0;
  currentSubalbum = firstSubalbum;
  while(currentSubalbum != NULL)
  {
    subalbumNumber++;

    //iterate over all photos in this subalbum
    int photoNumber=0;
    Photo* currentPhoto = currentSubalbum->getFirst();
    while(currentPhoto != NULL)
    {
      photoNumber++;
      int initPhotoNumber    = currentPhoto->getInitialPhotoNumber();
      int initSubalbumNumber = currentPhoto->getInitialSubalbumNumber();
      
      //if the current photo has moved rename full image, slideshow image, and thumbnail image to their final names
      if( initPhotoNumber != photoNumber || initSubalbumNumber != subalbumNumber)
      { 
        QString oldName = QString(saveLocation + "/img/%1/%2_moved.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        QString newName = QString(saveLocation + "/img/%1/%2.jpg" ).arg(subalbumNumber).arg(photoNumber);
        moveFile( oldName, newName );
        //-----
        oldName = QString(saveLocation + "/img/%1/%2_orig_moved.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        newName = QString(saveLocation + "/img/%1/%2_orig.jpg" ).arg(subalbumNumber).arg(photoNumber);
        if(tmpDir.exists(oldName) ) { moveFile( oldName, newName ); }
        //-----
        oldName = QString(saveLocation + "/img/%1/%2_slideshow_moved.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        newName = QString(saveLocation + "/img/%1/%2_slideshow.jpg" ).arg(subalbumNumber).arg(photoNumber);
        moveFile( oldName, newName );
        //-----
        oldName = QString(saveLocation + "/img/%1/%2_thumb_moved.jpg" ).arg(initSubalbumNumber).arg(initPhotoNumber);
        newName = QString(saveLocation + "/img/%1/%2_thumb.jpg" ).arg(subalbumNumber).arg(photoNumber);
        moveFile( oldName, newName );
        //---------------------------------------
        //reset initial photo and subalbum numbers, and filenames
        currentPhoto->setInitialPhotoNumber(photoNumber);
        currentPhoto->setInitialSubalbumNumber(subalbumNumber);
        currentPhoto->setImageFilename( QString(saveLocation + "/img/%1/%2.jpg").
                                        arg(subalbumNumber).arg(photoNumber) );
        currentPhoto->setSlideshowFilename( QString(saveLocation + "/img/%1/%2_slideshow.jpg").
                                            arg(subalbumNumber).arg(photoNumber) );
        currentPhoto->setThumbnailFilename( QString(saveLocation + "/img/%1/%2_thumb.jpg").
                                            arg(subalbumNumber).arg(photoNumber) );
      }
      
      //move on to next photo
      currentPhoto = currentPhoto->getNext();
      status->incrementProgress();
      qApp->processEvents();
    }

    //move on to next subalbum
    currentSubalbum = currentSubalbum->getNext();
  }
}
//==============================================
void Album::removeStagnantImages()
{
  QDir rootDir(saveLocation + "/img/");

  //iterate over each collection
  int subalbumNumber=0;
  Subalbum* currentSubalbum = firstSubalbum;
  while(currentSubalbum != NULL)
  {
    subalbumNumber++;

    //remove all photos who are numbered greater
    //than the number of photos in the subalbum
    int photoNum = currentSubalbum->getNumPhotos()+1;
    while(true)
    {
      QString imageString     = QString(saveLocation + "/img/%1/%2.jpg").arg(subalbumNumber).arg(photoNum);
      QString origString      = QString(saveLocation + "/img/%1/%2_orig.jpg").arg(subalbumNumber).arg(photoNum);
      QString slideshowString = QString(saveLocation + "/img/%1/%2_slideshow.jpg").arg(subalbumNumber).arg(photoNum);
      QString thumbString     = QString(saveLocation + "/img/%1/%2_thumb.jpg").arg(subalbumNumber).arg(photoNum);
      
      //if none of the possible images exist then assume 
      //no more stagnant images exist in this collection
      //
      if( !rootDir.exists(imageString)     && !rootDir.exists(origString) &&
          !rootDir.exists(slideshowString) && !rootDir.exists(thumbString) )
        break;
      //else delete photos and move on
      else
      {
        rootDir.remove( imageString );
        rootDir.remove( origString );
        rootDir.remove( slideshowString );
        rootDir.remove( thumbString );
        photoNum++;
      }
    }

    //reset number of loaded photos since old photos removed now
    currentSubalbum->resetNumLoadedPhotos();

    //move on to next collection
    currentSubalbum = currentSubalbum->getNext();
  }
  //---------------------------------
  //remove stagnant collections and all their contents
  subalbumNumber = numSubalbums+1;
  while(true)
  {    
    //check to see if the directory exists, if not we are done
    QString imageDirString = QString(saveLocation + "/img/%1/").arg(subalbumNumber);
    if( !rootDir.exists(imageDirString) )
      break;

    //get filelist for directory
    QDir imageDir(  imageDirString );
    QStringList list = imageDir.entryList( QDir::Files );

    //remove each file in directory
    QStringList::Iterator file;
    for ( file = list.begin(); file != list.end(); ++file )
    { rootDir.remove( QString(saveLocation + "/img/%1/" + *file).arg(subalbumNumber) ); }

    //remove directory
    rootDir.rmdir( QString("%1").arg(subalbumNumber) );

    //remove thumbnail image
    rootDir.remove( QString(saveLocation + "/img/%1_thumb.jpg").arg(subalbumNumber) );

    //move on to next subalbum
    subalbumNumber++;
  }

  //reset number of loaded subalbums since stagnant directories removed now
  numLoadedSubalbums = numSubalbums;
  //---------------------------------
}
//==============================================
void Album::exportThemeResources( QString theme )
{
  QStringList fileList;
  QStringList::Iterator file;
  QDir localDir;
  
  //remove any "resources" directories created by 1.0* versions of Album Shaper
  localDir.setPath( saveLocation + "/resources" );
  fileList = localDir.entryList();
  for(file = fileList.begin(); file != fileList.end(); file++)
  {
    localDir.remove(saveLocation + "/resources/" + *file);
  }
  localDir.cdUp();
  localDir.rmdir( "resources" );
  
  //create HTML and misc resources directories
  localDir.setPath(saveLocation);
  localDir.mkdir("resources");
//  localDir.mkdir("misc_resources");

  //remove all files in these directories from previous saves with other themes
  localDir.setPath(saveLocation + "/resources");
  fileList = localDir.entryList( QDir::Files );
  for ( file = fileList.begin(); file != fileList.end(); ++file )
  { localDir.remove( saveLocation + "/resources/" + *file ); }
  //--
/*
 localDir.setPath(saveLocation + "/misc_resources");
  fileList = localDir.entryList( QDir::Files );
  for ( file = fileList.begin(); file != fileList.end(); ++file )
  { localDir.remove( saveLocation + "/misc_resources/" + *file ); }
*/    
  //copy files over from theme's directory
  localDir.setPath(THEMES_PATH + theme + "/resources");
  fileList = localDir.entryList( QDir::Files );
  for ( file = fileList.begin(); file != fileList.end(); ++file )
  { copyFile( THEMES_PATH + theme + "/resources/" + *file, saveLocation + "/resources/" + *file); }
  //--
/*
 localDir.setPath(THEMES_PATH + theme + "/misc_resources");
  fileList = localDir.entryList( QDir::Files );
  for ( file = fileList.begin(); file != fileList.end(); ++file )
  { copyFile( THEMES_PATH + theme + "/misc_resources/" + *file, saveLocation + "/misc_resources/" + *file); }  
*/
}
//==============================================
void Album::syncSubalbumList(SubalbumPreviewWidget* item)
{
  //check to see if any changes actually took place
  bool change = false;
  Subalbum* tmp = firstSubalbum;
  SubalbumPreviewWidget* tmp2 = item;
  while( tmp2 != NULL)
  {
    //pointers do not match up
    if(tmp != tmp2->getSubalbum() )
    {
      change = true;
      break;
    }

    tmp = tmp->getNext();
    tmp2 = (SubalbumPreviewWidget*)tmp2->nextItem();
  }

  //if no change then quit
  if(!change)
    return;

  //base case, no items
  if(item == NULL)
  {
    firstSubalbum = NULL;
    lastSubalbum = NULL;
    return;
  }

  //set first and last pointers
  firstSubalbum = item->getSubalbum();
  firstSubalbum->setNext(NULL);
  firstSubalbum->setPrev(NULL);
  lastSubalbum = firstSubalbum;

  //set all next pointers
  while(item->nextItem() != NULL)
  {
    item->getSubalbum()->setNext( ((SubalbumPreviewWidget*)item->nextItem())->getSubalbum() );
    item->getSubalbum()->getNext()->setPrev( item->getSubalbum() );
    item = (SubalbumPreviewWidget*)item->nextItem();
    lastSubalbum = item->getSubalbum();
    lastSubalbum->setNext(NULL);
  }
  
}
//==============================================
void Album::setModified(bool val) { modified = val; }
//==============================================
int Album::getNextUniquePhotoID()
{
  nextUniqueID++;
  return nextUniqueID;
}
//==============================================
QStringList Album::getThumbnailFilenames()
{
  //iterate over all collections
  QStringList thumbnailList;
  Subalbum* currCollection = firstSubalbum;
  while(currCollection != NULL)
  {
    //iterate over all photos
    Photo* currPhoto = currCollection->getFirst();
    while( currPhoto != NULL )
    {
      thumbnailList.append( currPhoto->getThumbnailFilename() );
      currPhoto = currPhoto->getNext();
    }
    
    currCollection = currCollection->getNext();
  }

  return thumbnailList; 
}
//==============================================
