//==============================================
//  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 <qtextstream.h>
#include <qdom.h>
#include <fstream>
#include <qdir.h>
#include <qregexp.h>
#include <qapplication.h>

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

//==============================================
Subalbum::Subalbum(Album* albm, int number)
{
  //set subalbum number
  this->number = number;

  //by default no photos in subalbum
  numPhotos = 0;
  loadedPhotos = 0;

  //set strings to default values
  name = qApp->translate("Subalbum", "Collection %1").arg(number);
  description ="";

  //set default rep images
  smallRepresentativeImage = NULL;
  mediumRepresentativeImage = SubalbumPreviewWidget::createSubalbumPixmap
  ( QString(IMAGE_PATH)+"miscImages/subalbum.png" );
  largeRepresentativeImage = NULL;

  //no photos by default
  firstPhoto = NULL;
  lastPhoto = NULL;

  //next and prev pointers null by default
  prevSubalbum = NULL;
  nextSubalbum = NULL;

  //set album pointer
  this->albm = albm;
}
//==============================================
Subalbum::~Subalbum()
{
  //delete representative images
  delete smallRepresentativeImage;
  delete mediumRepresentativeImage;
  delete largeRepresentativeImage;

  //delete all photos
  Photo* current = firstPhoto;
  while(current != NULL)
  {
    Photo* temp = current->getNext();
    delete current;
    current = temp;
  }
}
//==============================================
QString Subalbum::getName()        { return QString(name);        }
QString Subalbum::getDescription() { return QString(description); }
//==============================================
QPixmap* Subalbum::getRepresentativeImage(int size)
{
  if(size == SMALL)  return smallRepresentativeImage;
  if(size == MEDIUM) return mediumRepresentativeImage;
  if(size == LARGE)  return largeRepresentativeImage;
  else               return NULL;
}
//==============================================
Album* Subalbum::getAlbum()        { return albm;         }

Subalbum* Subalbum::getPrev()      { return prevSubalbum; }
Subalbum* Subalbum::getNext()      { return nextSubalbum; }

Photo* Subalbum::getFirst()        { return firstPhoto;   }
Photo* Subalbum::getLast()         { return lastPhoto;    }

int Subalbum::getSubalbumNumber()  { return number;       }
int Subalbum::getNumPhotos()       { return numPhotos;    }
int Subalbum::getNumLoadedPhotos() { return loadedPhotos; }
//==============================================
void Subalbum::setName(QString val)
{
  if(name != val)
  {
    name = val;
    albm->setModified();
  }
}
//==============================================
void Subalbum::setDescription(QString val)
{
  if(description != val)
  {
    description = val;
    albm->setModified();
  }
}
//==============================================
void Subalbum::setRepresentativeImage(QString imageFilename)
{
  //delete old representative images
  delete smallRepresentativeImage;
  delete mediumRepresentativeImage;
  delete largeRepresentativeImage;

  //if being set to null, set back to defaults
  if(imageFilename.isNull())
  {
    smallRepresentativeImage = NULL;
    largeRepresentativeImage = NULL;

    mediumRepresentativeImage = SubalbumPreviewWidget::createSubalbumPixmap
                                ( QString(IMAGE_PATH)+"miscImages/subalbum.png" );
  }
  //else create various sized cover images
  else
  {
    int imageWidth, imageHeight;
    getImageSize( imageFilename, imageWidth, imageHeight );
    
    //small version (show at top)
    int smallRepWidth = 0;
    int smallRepHeight = 0;
    calcScaledImageDimensions( imageWidth, imageHeight,
                               107, REP_IMAGE_HEIGHT,
                               smallRepWidth, smallRepHeight);
    QImage thumbnailSmall;
    scaleImage( imageFilename, thumbnailSmall, smallRepWidth, smallRepHeight );
    smallRepresentativeImage = new QPixmap( thumbnailSmall.width(), thumbnailSmall.height() );
    smallRepresentativeImage->convertFromImage( thumbnailSmall );

    //medium version (seen in collections listing)
    mediumRepresentativeImage = SubalbumPreviewWidget::createSubalbumPixmap( imageFilename );
    
    //large version, used for actually saving out
    largeRepresentativeImage = new QPixmap( imageFilename );
    //---------------------------------------------------------
  }

  //set modified
  albm->setModified();
}
//==============================================
void Subalbum::setSubalbumNumber(int newVal) { number = newVal;          }
void Subalbum::resetNumLoadedPhotos()        { loadedPhotos = numPhotos; }
void Subalbum::setModified()                 { albm->setModified();      }
//==============================================
void Subalbum::addPhoto(Photo* newPhoto)
{
  //set the photos next and prev points to NULL by default
  newPhoto->setNext(NULL);
  newPhoto->setPrev(NULL);

  //if list empty set to head
  if(firstPhoto == NULL)
  {
    firstPhoto = newPhoto;
    lastPhoto = newPhoto;
  }
  //else append to end of list
  else
  {
    lastPhoto->setNext(newPhoto);
    newPhoto->setPrev( lastPhoto );
    lastPhoto = newPhoto;
  }

  numPhotos++;
  albm->setModified();
}
//==============================================
bool Subalbum::addPhoto(QString fileName, bool replaceDescription, Photo* newPhoto)
{
  //create new photo if necessary
  if(newPhoto == NULL)
    newPhoto = new Photo(this, getLast(), numPhotos);

  //replace description with filename if instructed
  //(aka /home/bob/happy.jpg -> happy)
  if(replaceDescription)
  {
    //get filename
    QString desc(fileName);

    //remove path
    desc = desc.section( QRegExp("/"), -1);

    //remove extension if it exists
    int extensionIndex = desc.findRev( '.' );
    if(extensionIndex > 0 )
      desc.truncate(extensionIndex);

    //convert _'s to spaces
    desc = desc.replace('_', ' ');

    //set photos description
    newPhoto->setDescription( desc );
  }

  //attempt to set image
  if(!newPhoto->setImage(fileName, albm->getNextUniquePhotoID() ))
  {
    delete newPhoto;
    return false;
  }

  //if this is the first photo set as the head
  if(firstPhoto == NULL)
  {
    firstPhoto = newPhoto;
    lastPhoto = newPhoto;
  }
  //else append to end of list
  else
  {
    lastPhoto->setNext(newPhoto);
    newPhoto->setPrev( lastPhoto );
    lastPhoto = newPhoto;
  }

  numPhotos++;
  albm->setModified();
  return true;
}
//==============================================
bool Subalbum::lazyAddPhoto(QString imageName, QString slideshowName, QString thumbnailName, 
                            Photo* newPhoto)
{
  //attempt to set image
  if(!newPhoto->setImage(imageName, slideshowName, thumbnailName))
  {
    delete newPhoto;
    return false;
  }

  //if this is the first photo set as the head
  if(firstPhoto == NULL)
  {
    firstPhoto = newPhoto;
    lastPhoto = newPhoto;
  }
  //else append to end of list
  else
  {
    lastPhoto->setNext(newPhoto);
    newPhoto->setPrev( lastPhoto );
    lastPhoto = newPhoto;
  }
  
  numPhotos++;
  albm->setModified();
  return true;
}
//==============================================
void Subalbum::removePhoto(Photo* val)
{
  //if photo pointer is null then bail
  if(val == NULL) return;
  
  //reset head and tail pointers if necessary
  if( val == firstPhoto )   firstPhoto = val->getNext();
  if( val == lastPhoto )    lastPhoto  = val->getPrev();
  
  //splice 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;
  numPhotos--;
  albm->setModified();
}
//==============================================
void Subalbum::setPrev(Subalbum* val)
{
  prevSubalbum = val;
  albm->setModified();
}
//==============================================
void Subalbum::setNext(Subalbum* val)
{
  nextSubalbum = val;
  albm->setModified();
}
//==============================================
void Subalbum::exportToXML(StatusWidget*, QTextStream& stream)
{
  //write subalbum information
  stream << "  <subalbum>\n";

  //if subalbum has a represenatative image save it's path
   if(getRepresentativeImage(LARGE) != NULL )
   {
     stream << "    <thumb path=\"img/" << number << "_thumb.jpg\"/>\n";
     stream << "    <name>" << fixXMLString(name) << "</name>\n";
   }
   //else if the name is empty or just whitespace override it with "Subalbum #" so
   //it is not invisible on the coverpage
   else
   {
     QString strippedName = fixXMLString(name);
     if(strippedName.stripWhiteSpace() == "")
       stream << QString("  <name>%1 %2</name>\n").arg(qApp->translate("Subalbum", "Collection")).arg(number);
     else
       stream << "    <name>" << fixXMLString(name) << "</name>\n";
   }

  stream << "    <description>" << fixXMLString(description) << "</description>\n";

  //write photos
  Photo* current = firstPhoto;
  while(current != NULL)
  {
    current->exportToXML(stream);
    current = current->getNext();
  }

  //close subalbum
  stream << "  </subalbum>\n";

}
//==============================================
void Subalbum::importFromDisk(QDomNode* root, 
                              int subalbumNum,
                              StatusWidget* status,
                              QString dirName, 
                              bool disableCheckPhotoMods)
{
  //if representative image exists load
  QString repName = QString(dirName + "/img/%1_thumb.jpg").arg(subalbumNum);
  QImage repImage(repName);
  if(!repImage.isNull())
  {
    setRepresentativeImage(repName);
  }

  QDomNode node = root->firstChild();
  QDomText val;
  int photoNum = 0;
  while( !node.isNull() )
  {
    //------------------------------------------------------------
    //subalbum name
    if( node.isElement() && node.nodeName() == "name" )
    {
      val = node.firstChild().toText();
      if(!val.isNull())
        name = val.nodeValue();
     name.replace("\\&quot;","\"");
    }
    //------------------------------------------------------------
    //subalbum description
    else if( node.isElement() && node.nodeName() == "description" )
    {
      val = node.firstChild().toText();
      if(!val.isNull())
        description = val.nodeValue();
     description.replace("\\&quot;","\"");
    }
    //------------------------------------------------------------
    //photo
    else if( node.isElement() && node.nodeName() == "photo" )
    {
      //increase counter
      photoNum++;

      //create new photo object
      QString imageName = QString(dirName + "img/%1/%2.jpg").arg(subalbumNum).arg(photoNum);
      QString slideshowName = QString(dirName + "img/%1/%2_slideshow.jpg").arg(subalbumNum).arg(photoNum);
      QString thumbName = QString(dirName + "img/%1/%2_thumb.jpg").arg(subalbumNum).arg(photoNum);
      Photo* newPhoto = new Photo(this, getLast(), photoNum);

      //load photo information from disk
      QDateTime* modificationTimes = newPhoto->importFromDisk( &node );

      //first check to see if modifications times have changed
      bool lazyLoad = true; //assume no modifications

      //skip checking for mods if disable checking is set
      if(!disableCheckPhotoMods)
      {
        QFileInfo info[3];
        info[0].setFile( imageName );
        info[1].setFile( slideshowName );
        info[2].setFile( thumbName );
        if(
              modificationTimes[0] != info[0].lastModified() ||
              modificationTimes[1] != info[1].lastModified() ||
              modificationTimes[2] != info[2].lastModified()
           )
          lazyLoad = false;
      }

      //if no changes have occured do lazy load - don't
      //bother scaling down thumbnail and slideshow images
      //from original image
      std::ifstream imageFile    ( QFile::encodeName(imageName)     );
      std::ifstream slideshowFile( QFile::encodeName(slideshowName) );
      std::ifstream thumbnailFile( QFile::encodeName(thumbName)     );

      if(  imageFile.is_open() &&
            thumbnailFile.is_open() &&
            slideshowFile.is_open() &&
           (
              lazyLoad ||
              (
                getMD5(imageFile) == newPhoto->getImageChecksum() &&
                getMD5(slideshowFile) == newPhoto->getSlideshowChecksum() &&
                getMD5(thumbnailFile) == newPhoto->getThumbnailChecksum()
              )
           )
        )
      {
        //close ifstreams
        imageFile.close();
        slideshowFile.close();
        thumbnailFile.close();

        //populate image
        lazyAddPhoto(imageName, slideshowName, thumbName, newPhoto);
      }
      //else reload image and scale it since changes have occured.
      else
      {
        //close ifstreams if open
        if(imageFile.is_open())
          imageFile.close();
        if(thumbnailFile.is_open())
          thumbnailFile.close();

        //populate image
        addPhoto(imageName, false, newPhoto);
      }

      if(imageFile.is_open())
        imageFile.close();
      if(slideshowFile.is_open())
        slideshowFile.close();
      if(thumbnailFile.is_open())
        thumbnailFile.close();

      //update progress bar
      status->incrementProgress();
      qApp->processEvents();
    }
    //------------------------------------------------------------
    //advance to next node
    node = node.nextSibling();
    //------------------------------------------------------------
  }
  //------------------------------------------------------------
  //set loaded number
  resetNumLoadedPhotos();
  //------------------------------------------------------------
}
//==============================================
void Subalbum::photoMoved(Photo* val)
{
  //if null pointer bail
  if(val == NULL) return;
  
  //update first and last pointers if necessary
  if(val == firstPhoto) firstPhoto = val->getNext();
  if(val == lastPhoto)  lastPhoto  = val->getPrev();

  //splice out
  if(val->getPrev() != NULL) val->getPrev()->setNext( val->getNext() );
  if(val->getNext() != NULL) val->getNext()->setPrev( val->getPrev() );

  numPhotos--;
  albm->setModified();
}
//==============================================
void Subalbum::syncPhotoList(PhotoPreviewWidget* item)
{
  //base case, no items
  if(item == NULL)
  {
    firstPhoto = NULL;
    lastPhoto = NULL;
    return;
  }

  //set first and last pointers
  firstPhoto = item->getPhoto();
  firstPhoto->setNext(NULL);
  firstPhoto->setPrev(NULL);
  lastPhoto = firstPhoto;

  //set all next/prev pointers
  while(item->nextItem() != NULL)
  {
    item->getPhoto()->setNext( ((PhotoPreviewWidget*)item->nextItem())->getPhoto() );
    item->getPhoto()->getNext()->setPrev( item->getPhoto() );
    item = (PhotoPreviewWidget*)item->nextItem();
    lastPhoto = item->getPhoto();
    lastPhoto->setNext(NULL);
  }
  
}
//==============================================
