/***************************************************************************
                          emoticon.cpp  -  description
                             -------------------
    begin                : Mon Apr 15 2002
    copyright            : (C) 2002 by Mike K. Bennett
    email                : mkb137b@hotmail.com
 ***************************************************************************/

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

#include "emoticon.h"

#include "utils/kmessshared.h"
#include "kmessdebug.h"
#include "currentaccount.h"

#include <QImage>
#include <QRegExp>
#include <QFileInfo>
//#include <QCryptographicHash>

#include <KStandardDirs>


#ifdef KMESSDEBUG_EMOTICONS
  #define KMESSDEBUG_EMOTICONS_GENERAL
#endif



/**
 * Constructor for standard emoticons
 *
 * @param pictureName  The picture file name
 * @param tooltip      The name of this emoticon: it will be displayed as tooltip in the Emoticons Widget
 */
Emoticon::Emoticon( const QString &pictureName, const QString &tooltip )
  : pictureDirectory_(QString::null)
  , pictureName_(pictureName)
  , picturePath_(QString::null)
  , height_(EMOTICONS_DEFAULT_SIZE)
  , isCustomEmoticon_(false)
  , originalPictureName_(pictureName)
  , tooltip_(tooltip)
  , valid_(false)
  , width_(EMOTICONS_DEFAULT_SIZE)
{
  // Try to find the location of the current image
  update();
}



/**
 * Constructor for custom emoticons, with custom picture path
 *
 * @param pictureName       The picture file name
 * @param tooltip           The name of this emoticon: it will be displayed as tooltip in the Emoticons Widget
 * @param pictureDirectory  The folder where this emoticon's image is located
 */
Emoticon::Emoticon( const QString &pictureName, const QString &tooltip, const QString &pictureDirectory )
  : pictureDirectory_(pictureDirectory)
  , pictureName_(pictureName)
  , height_(EMOTICONS_DEFAULT_SIZE)
  , isCustomEmoticon_(true)
  , originalPictureName_(pictureName)
  , tooltip_(tooltip)
  , valid_(false)
  , width_(EMOTICONS_DEFAULT_SIZE)
{
  // Try to find the location of the current image
  update();
}



/**
 * Create an exact copy of another emoticon
 *
 * @param other  The object to copy
 */
Emoticon::Emoticon( const Emoticon &other )
  : QObject()
  , pictureDirectory_(other.pictureDirectory_)
  , pictureName_(other.pictureName_)
  , picturePath_(other.picturePath_)
  , height_(EMOTICONS_DEFAULT_SIZE)
  , isCustomEmoticon_(other.isCustomEmoticon_)
  , originalPictureName_(other.originalPictureName_)
  , shortcuts_(other.shortcuts_)
  , tooltip_(other.tooltip_)
  , valid_(other.valid_)
  , width_(EMOTICONS_DEFAULT_SIZE)
{
}



/**
 * Destructor. Does nothing notable.
 */
Emoticon::~Emoticon()
{
}



/**
 * Add a text shortcut which will translate to this emoticon
 *
 * @param  shortcut   Shortcut text
 */
void Emoticon::addShortcut( const QString &shortcut )
{
  shortcuts_.append( shortcut );
}



/**
 * Get the picture file name
 */
const QString& Emoticon::getPictureName() const
{
  return pictureName_;
}



/**
 * Get the full path of the picture file
 */
const QString& Emoticon::getPicturePath() const
{
  return picturePath_;
}



// Get the data hash for the picture file contents
const QString& Emoticon::getDataHash() const
{
  return dataHash_;
}



/**
 * Get a suitable bit of HTML which represents the emoticon
 *
 * The images output as HTML are limited in width and height, though the original files keep their
 * original dimensions - and are sent untouched to your contacts.
 *
 * @param small   If true, the HTML will paint a tiny version of the emoticon
 */
QString Emoticon::getHtml( bool small ) const
{
  int width, height;
  QString emoticonClass;

  // Enforce minimum and maximum size limits
  if( small )
  {
    width  = width_; // qMin( EMOTICONS_DEFAULT_SIZE, width_  ); // Require only a max height
    height = qMin( EMOTICONS_DEFAULT_SIZE, height_ );
  }
  else
  {
    width  = width_; // qMin( EMOTICONS_MAX_SIZE, width_  ); // Require only a max height
    height = qMin( EMOTICONS_MAX_SIZE, height_ );
  }

  // Resize the image so the aspect ratio is kept, and its dimensions don't exceed the enforced
  // maximum size
  if( width_ > height_ )
  {
      height = ( width * height_ ) / width_;
  }
  else
  {
      width = ( height * width_ ) / height_;
  }

  if( isCustomEmoticon_ )
  {
    emoticonClass = "customEmoticon";
  }
  else
  {
    emoticonClass = "standardEmoticon";
  }

  return "<img src='" + picturePath_ + "' alt='" + KMessShared::htmlEscape( shortcuts_.first() )
       + "' height='" + QString::number(height)
       + "' width='" + QString::number(width) + "' valign='bottom' class='"
       + emoticonClass + "' />";
}



/**
 * Get a regular expression pattern which will match any of the text shortcuts of the emoticon
 */
QString Emoticon::getPattern() const
{
  QString pattern;
  QStringList::const_iterator shortcut;

  for( shortcut = shortcuts_.begin(); shortcut != shortcuts_.end(); ++shortcut )
  {
    if( pattern.length() > 0 )
    {
      pattern += '|';
    }
    pattern += QRegExp::escape( *shortcut );
  }

  return pattern;
}



/**
 * Get the first shortcut associated to this emoticon
 */
QString Emoticon::getShortcut() const
{
  return shortcuts_.first();
}



/**
 * Get all the shortcuts associated to this emoticon
 */
const QStringList & Emoticon::getShortcuts() const
{
  return shortcuts_;
}



/**
 * Get the associated tooltip
 */
QString Emoticon::getTooltip() const
{
  return tooltip_;
}



/**
 * Find if this emoticon really contains a valid picture
 */
bool Emoticon::isValid() const
{
  return valid_;
}



// Return whether a string is suitable for use as an emoticon shortcut
bool Emoticon::isValidShortcut( const QString &shortcut )
{
  // NOTE: If you update this regexp, in dialogs/addemoticondialog.cpp there is
  // a string which needs to be updated in interfaceChanged().
  return ! shortcut.contains( QRegExp(
         "^/.+"         // Shortcuts which start with / interfere with the text commands
         "|"
         "\\[[A-Z]+\\]" // [anything] is reserved for text formatting
         "|"
         "^.{8,}$"      // Messenger only supports shortcuts long a maximum of 7 chars
         , Qt::CaseInsensitive ) );
}



/**
 * Change the name of the emoticon picture.
 *
 * Will issue an update to find out where the new file may be located.
 *
 * @param pictureName  The new picture file name
 */
void Emoticon::setPictureName( const QString &pictureName )
{
  pictureName_ = pictureName;
  update();
}



/**
 * Delete all the shortcuts and replace them with a single one
 *
 * @param shortcut  The new shortcut
 */
void Emoticon::setShortcut( const QString &shortcut )
{
  shortcuts_.clear();
  shortcuts_.append( shortcut );
}



/**
 * Delete all the shortcuts and replace them with another list
 *
 * @param shortcuts  The new list of shortcuts
 */
void Emoticon::setShortcuts( const QStringList &shortcuts )
{
  shortcuts_.clear();
  shortcuts_ = shortcuts;
}



/**
 * Change the associated tooltip
 *
 * @param pictureTooltip  The new tooltip
 */
void Emoticon::setTooltip( const QString &pictureTooltip )
{
  tooltip_ = pictureTooltip;
}


/**
 * Update the internal emoticon data
 *
 * Standard emoticons are searched in both the current theme's folder and in the KMess' default folder;
 * custom emoticons only in the path which has been specified at creation. This means that custom emoticons
 * get updated, but only their file type may really change.
 */
void Emoticon::update()
{
  bool           found = false;
  QImage         emoticonData;
  QString        emoticonPath;
  QStringList    allowedTypes;
  KStandardDirs *dirs = KGlobal::dirs();

  // These are the allowed types for emoticon pictures. Add more to support more formats.
  // Search in the KDE API Docs for "QImageIO" class to find out which formats are supported, and how to add more.
  allowedTypes << "png" << "gif" << "mng" << "jpg";

  if( isCustomEmoticon_ )
  {
    emoticonPath = pictureDirectory_;
  }
  else
  {
    emoticonPath = CurrentAccount::instance()->getEmoticonStyle();
  }

  // For each iteration:
  // - with a standard emoticon, will check both folders (current and default theme);
  // - with a custom emoticon, will skip the check in the default theme folder, since they simply can't be there.
  foreach( const QString &pictureType, allowedTypes )
  {
    if( isCustomEmoticon_ )
    {
      picturePath_ = emoticonPath + '/' + pictureName_ + '.' + pictureType;
    }
    else
    {
      picturePath_ = dirs->findResource( "emoticons", emoticonPath + '/' + pictureName_ + '.' + pictureType );
    }

    if( ! picturePath_.isNull() && KStandardDirs::exists( picturePath_ ) )
    {
      found = true;
      break;
    }
  }

  // If this emoticon does not exist in this theme, try again loading the default KMess emoticon
  if( ! found )
  {
    foreach( const QString &pictureType, allowedTypes )
    {
      // Search in KMess' default theme folder
      picturePath_ = dirs->findResource( "emoticons", "KMess-new/" + originalPictureName_ + "." + pictureType );

      if( ! picturePath_.isNull() && KStandardDirs::exists( picturePath_ ) )
      {
        break;
      }
    }
  }

  // Fail if the path isn't valid, or if the emoticon can't be loaded
  if( picturePath_.isEmpty() || ! emoticonData.load( picturePath_ ) || emoticonData.isNull() )
  {
#ifdef KMESSDEBUG_EMOTICONS_GENERAL
    kDebug() << "Update failed: " << picturePath_ << "(" << (isCustomEmoticon_?"custom":"standard") << ", shortcut" << originalPictureName_ << ")";
#endif
    valid_ = false;
    return;
  }

  valid_ = true;

  // Find the dimensions of the image, to make it render nicer.
  width_  = emoticonData.width();
  height_ = emoticonData.height();

  // Try to read the data hash from the filename
  if( isCustomEmoticon_ )
  {
    QFileInfo fileInfo( picturePath_ );
    dataHash_ = fileInfo.baseName();
    dataHash_.replace( "_", "/" );
    // if this dataHash_ ever turns out to be incorrect, this is the way to regenerate it:
    // QFile emoticonFile( picturePath_ );
    // emoticonFile.open( QIODevice::ReadOnly() );
    // dataHash_ = QCryptographicHash::hash( emoticonFile.readAll(), QCryptographicHash::Sha1 ).toBase64();
    // emoticonFile.close();

#ifdef KMESSDEBUG_EMOTICONS_GENERAL
    kDebug() << "Read data hash from filename for " << picturePath_ << ": " << dataHash_;
#endif
  }

  emit changed();
}



#include "emoticon.moc"
