/***************************************************************************
    copyright            : (C) 2002-2006 by Robby Stephenson
    email                : robby@periapsis.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of version 2 of the GNU General Public License as  *
 *   published by the Free Software Foundation;                            *
 *                                                                         *
 ***************************************************************************/

#include "entryiconview.h"
#include "collection.h"
#include "collectionfactory.h"
#include "imagefactory.h"
#include "controller.h"
#include "entry.h"
#include "field.h"
#include "tellico_utils.h"
#include "tellico_debug.h"
#include "listviewcomparison.h"

#include <tdepopupmenu.h>
#include <kstringhandler.h>
#include <kiconloader.h>
#include <kwordwrap.h>
#include <kimageeffect.h>
#include <tdelocale.h>

#include <tqpainter.h>

namespace {
  static const int MIN_ENTRY_ICON_SIZE = 32;
  static const int MAX_ENTRY_ICON_SIZE = 128;
  static const int ENTRY_ICON_SIZE_PAD = 6;
  static const int ENTRY_ICON_SHADOW_RIGHT = 1;
  static const int ENTRY_ICON_SHADOW_BOTTOM = 1;
}

using Tellico::EntryIconView;
using Tellico::EntryIconViewItem;

EntryIconView::EntryIconView(TQWidget* parent_, const char* name_/*=0*/)
    : TDEIconView(parent_, name_), m_coll(0), m_maxAllowedIconWidth(MAX_ENTRY_ICON_SIZE),
      m_maxIconWidth(MIN_ENTRY_ICON_SIZE), m_maxIconHeight(MIN_ENTRY_ICON_SIZE),
      m_comparison(0) {
  setAutoArrange(true);
  setSorting(true);
  setItemsMovable(false);
  setSelectionMode(TQIconView::Extended);
  setResizeMode(TQIconView::Adjust);
  setMode(TDEIconView::Select);
  setSpacing(4);
//  setWordWrapIconText(false);

  m_defaultPixmaps.setAutoDelete(true);

  connect(this, TQ_SIGNAL(selectionChanged()), TQ_SLOT(slotSelectionChanged()));
  connect(this, TQ_SIGNAL(doubleClicked(TQIconViewItem*)), TQ_SLOT(slotDoubleClicked(TQIconViewItem*)));
  connect(this, TQ_SIGNAL(contextMenuRequested(TQIconViewItem*, const TQPoint&)),
          TQ_SLOT(slotShowContextMenu(TQIconViewItem*, const TQPoint&)));
}

EntryIconView::~EntryIconView() {
  delete m_comparison;
  m_comparison = 0;
}

EntryIconViewItem* EntryIconView::firstItem() const {
  return static_cast<EntryIconViewItem*>(TDEIconView::firstItem());
}

void EntryIconView::findImageField() {
  m_imageField.truncate(0);
  if(!m_coll) {
    return;
  }
  const Data::FieldVec& fields = m_coll->imageFields();
  if(!fields.isEmpty()) {
    m_imageField = fields[0]->name();
  }
//  myDebug() << "EntryIconView::findImageField() - image field = " << m_imageField << endl;
}

const TQString& EntryIconView::imageField() {
  return m_imageField;
}

const TQString& EntryIconView::sortField() {
  return m_comparison ? m_comparison->fieldName() : TQString();
}

const TQPixmap& EntryIconView::defaultPixmap() {
  TQPixmap* pix = m_defaultPixmaps[m_coll->type()];
  if(pix) {
    return *pix;
  }
  TDEIconLoader* loader = TDEGlobal::instance()->iconLoader();
  TQPixmap tmp = loader->loadIcon(TQString::fromLatin1("nocover_") + CollectionFactory::typeName(m_coll->type()),
                                 TDEIcon::User, 0, TDEIcon::DefaultState, 0, true /*canReturnNull */);
  if(tmp.isNull()) {
    myLog() << "EntryIconView::defaultPixmap() - null nocover image, loading tellico.png" << endl;
    tmp = UserIcon(TQString::fromLatin1("tellico"));
  }
  if(TQMAX(tmp.width(), tmp.height()) > static_cast<int>(MIN_ENTRY_ICON_SIZE)) {
    tmp.convertFromImage(tmp.convertToImage().smoothScale(m_maxIconWidth, m_maxIconHeight, TQImage::ScaleMin));
  }
  pix = new TQPixmap(tmp);
  m_defaultPixmaps.insert(m_coll->type(), pix);
  return *pix;
}

void EntryIconView::setMaxAllowedIconWidth(int width_) {
  m_maxAllowedIconWidth = TQMAX(MIN_ENTRY_ICON_SIZE, TQMIN(MAX_ENTRY_ICON_SIZE, width_));
  setMaxItemWidth(m_maxAllowedIconWidth + 2*ENTRY_ICON_SIZE_PAD);
  m_defaultPixmaps.clear();
  refresh();
}

void EntryIconView::fillView() {
  setSorting(false);
  setGridX(m_maxAllowedIconWidth + 2*ENTRY_ICON_SIZE_PAD); // set default spacing initially

  GUI::CursorSaver cs(TQt::waitCursor);

  bool allDefaultImages = true;
  m_maxIconWidth = TQMAX(MIN_ENTRY_ICON_SIZE, m_maxIconWidth);
  m_maxIconHeight = TQMAX(MIN_ENTRY_ICON_SIZE, m_maxIconHeight);
  EntryIconViewItem* item;
  for(Data::EntryVecIt it = m_entries.begin(); it != m_entries.end(); ++it) {
    item = new EntryIconViewItem(this, it);
    m_maxIconWidth = TQMAX(m_maxIconWidth, item->width());
    m_maxIconHeight = TQMAX(m_maxIconHeight, item->pixmapRect().height());
    if(item->usesImage()) {
      allDefaultImages = false;
    }
  }
  if(allDefaultImages) {
    m_maxIconWidth = m_maxAllowedIconWidth;
    m_maxIconHeight = m_maxAllowedIconWidth;
  }
  // if both width and height are min, then that means there are no images
  m_defaultPixmaps.clear();
  // now reset size of all default pixmaps
  for(item = firstItem(); item; item = item->nextItem()) {
    if(!item->usesImage()) {
      item->updatePixmap();
    }
  }
  setGridX(m_maxIconWidth + 2*ENTRY_ICON_SIZE_PAD); // the pad is so the text can be wider than the icon
  setGridY(m_maxIconHeight + fontMetrics().height());
  sort();
  setSorting(true);
}

void EntryIconView::refresh() {
  if(!m_coll) {
    return;
  }
  showEntries(m_entries);
}

void EntryIconView::clear() {
  TDEIconView::clear();
  m_coll = 0;
  m_entries.clear();
  m_imageField.truncate(0);
}

void EntryIconView::showEntries(const Data::EntryVec& entries_) {
  setUpdatesEnabled(false);
  TDEIconView::clear(); // don't call EntryIconView::clear() since that clears the entries_ ref
  if(entries_.isEmpty()) {
    return;
  }
  m_coll = entries_[0]->collection();
  m_entries = entries_;
  findImageField();
  fillView();
  setUpdatesEnabled(true);
}

void EntryIconView::addEntries(Data::EntryVec entries_) {
  if(entries_.isEmpty()) {
    return;
  }
  if(!m_coll) {
    m_coll = entries_[0]->collection();
  }
  // since the view probably doesn't show all the current entries
  // only add the new ones if the count is the total
  if(m_entries.count() + entries_.count() < m_coll->entryCount()) {
    return;
  }
  int w = MIN_ENTRY_ICON_SIZE;
  int h = MIN_ENTRY_ICON_SIZE;
  for(Data::EntryVecIt entry = entries_.begin(); entry != entries_.end(); ++entry) {
    m_entries.append(entry);
    EntryIconViewItem* item = new EntryIconViewItem(this, entry);
    w = TQMAX(w, item->width());
    h = TQMAX(h, item->pixmapRect().height());
  }
  if(w > m_maxIconWidth || h > m_maxIconHeight) {
    refresh();
  }
}

void EntryIconView::modifyEntries(Data::EntryVec entries_) {
  for(Data::EntryVecIt entry = entries_.begin(); entry != entries_.end(); ++entry) {
    EntryIconViewItem* item = 0;
    for(EntryIconViewItem* it = firstItem(); it; it = it->nextItem()) {
      if(it->entry() == entry) {
        item = it;
        break;
      }
    }
    if(!item) {
      continue;
    }
    item->update();
  }
}

void EntryIconView::removeEntries(Data::EntryVec entries_) {
  for(Data::EntryVecIt entry = entries_.begin(); entry != entries_.end(); ++entry) {
    m_entries.remove(entry);
  }
  bool found = false;
  EntryIconViewItem* item = firstItem();
  while(item) {
    if(entries_.contains(item->entry())) {
      EntryIconViewItem* prev = item;
      item = item->nextItem();
      delete prev;
      found = true;
    } else {
      item = item->nextItem();
    }
  }
  if(found) {
    arrangeItemsInGrid();
  }
}

void EntryIconView::slotSelectionChanged() {
  Data::EntryVec entries;
  const TQPtrList<EntryIconViewItem>& items = selectedItems();
  for(TQPtrListIterator<EntryIconViewItem> it(items); it.current(); ++it) {
    entries.append(it.current()->entry());
  }
  Controller::self()->slotUpdateSelection(this, entries);
}

void EntryIconView::slotDoubleClicked(TQIconViewItem* item_) {
  EntryIconViewItem* i = static_cast<EntryIconViewItem*>(item_);
  if(i) {
    Controller::self()->editEntry(i->entry());
  }
}

void EntryIconView::updateSelected(EntryIconViewItem* item_, bool s_) const {
  if(s_) {
    m_selectedItems.append(item_);
  } else {
    m_selectedItems.removeRef(item_);
  }
}

void EntryIconView::slotShowContextMenu(TQIconViewItem* item_, const TQPoint& point_) {
  TDEPopupMenu menu(this);

  // only insert entry items if one is selected
  if(item_) {
    Controller::self()->plugEntryActions(&menu);
    menu.insertSeparator();
  }

  TDEPopupMenu sortMenu(&menu);
  const TQStringList titles = m_coll->fieldTitles();
  for(TQStringList::ConstIterator it = titles.begin(); it != titles.end(); ++it) {
    sortMenu.insertItem(*it);
  }
  connect(&sortMenu, TQ_SIGNAL(activated(int)), TQ_SLOT(slotSortMenuActivated(int)));

  menu.insertItem(i18n("&Sort By"), &sortMenu);
  menu.exec(point_);
}

void EntryIconView::slotSortMenuActivated(int id_) {
  const TDEPopupMenu* menu = ::tqt_cast<TDEPopupMenu*>(sender());
  if(menu) {
    TQString title = menu->text(id_);
    Data::FieldPtr f = m_coll->fieldByTitle(title);
    if(f) {
      delete m_comparison;
      m_comparison = ListViewComparison::create(f);
      sort();
    }
  }
}

int EntryIconView::compare(const EntryIconViewItem* item1, EntryIconViewItem* item2) {
  if(m_comparison) {
    return m_comparison->compare(item1, item2);
  }
  return 0;
}

/* *********************************************************** */

EntryIconViewItem::EntryIconViewItem(EntryIconView* parent_, Data::EntryPtr entry_)
    : TDEIconViewItem(parent_, entry_->title()), m_entry(entry_), m_usesImage(false) {
  setDragEnabled(false);
  const TQString& imageField = parent_->imageField();
  if(!imageField.isEmpty()) {
    TQPixmap p = ImageFactory::pixmap(m_entry->field(imageField),
                                     parent_->maxAllowedIconWidth(),
                                     parent_->maxAllowedIconWidth());
    if(!p.isNull()) {
      setPixmap(p);
      m_usesImage = true;
    }
  }
}

EntryIconViewItem::~EntryIconViewItem() {
  // be sure to remove from selected list when it's deleted
  EntryIconView* view = iconView();
  if(view) {
    view->updateSelected(this, false);
  }
}

void EntryIconViewItem::setSelected(bool s_) {
  setSelected(s_, false);
}

void EntryIconViewItem::setSelected(bool s_, bool cb_) {
  EntryIconView* view = iconView();
  if(!view) {
    return;
  }
  if(s_ != isSelected()) {
    view->updateSelected(this, s_);
    TDEIconViewItem::setSelected(s_, cb_);
  }
}

void EntryIconViewItem::updatePixmap() {
  EntryIconView* view = iconView();
  const TQString& imageField = view->imageField();
  m_usesImage = false;
  if(imageField.isEmpty()) {
    setPixmap(view->defaultPixmap());
  } else {
    TQPixmap p = ImageFactory::pixmap(m_entry->field(imageField),
                                     view->maxAllowedIconWidth(),
                                     view->maxAllowedIconWidth());
    if(p.isNull()) {
      setPixmap(view->defaultPixmap());
    } else {
      setPixmap(p);
      m_usesImage = true;
      calcRect();
    }
  }
}

void EntryIconViewItem::update() {
  setText(m_entry->title());
  updatePixmap();
  iconView()->arrangeItemsInGrid();
}

void EntryIconViewItem::calcRect(const TQString& text_) {
  TDEIconViewItem::calcRect(text_);
  TQRect r = pixmapRect();
  r.rRight()  += ENTRY_ICON_SHADOW_RIGHT;
  r.rBottom() += ENTRY_ICON_SHADOW_BOTTOM;
  setPixmapRect(r);
}

void EntryIconViewItem::paintItem(TQPainter* p_, const TQColorGroup &cg_) {
  p_->save();
  paintPixmap(p_, cg_);
  paintText(p_, cg_);
  p_->restore();
}

void EntryIconViewItem::paintFocus(TQPainter*, const TQColorGroup&) {
  // don't draw anything
}

void EntryIconViewItem::paintPixmap(TQPainter* p_, const TQColorGroup& cg_) {
  // only draw the shadow if there's an image
  // oh, and don't draw it if it's a file catalog, it doesn't look right
  if(m_usesImage && !isSelected() && m_entry->collection()->type() != Data::Collection::File) {
    // pixmapRect() already includes shadow size, so shift the rect by that amount
    TQRect r = pixmapRect(false);
    r.setLeft(r.left() + ENTRY_ICON_SHADOW_RIGHT);
    r.setTop(r.top() + ENTRY_ICON_SHADOW_BOTTOM);
    TQColor c = Tellico::blendColors(iconView()->backgroundColor(), TQt::black, 10);
    p_->fillRect(r, c);
  }
  TDEIconViewItem::paintPixmap(p_, cg_);
}

void EntryIconViewItem::paintText(TQPainter* p_, const TQColorGroup &cg_) {
  TQRect tr = textRect(false);
  int textX = tr.x() + 2;
  int textY = tr.y();

  if(isSelected()) {
    p_->setBrush(TQBrush(cg_.highlight()));
    p_->setPen(TQPen(cg_.highlight()));
    p_->drawRoundRect(tr, 1000/tr.width(), 1000/tr.height());
    p_->setPen(TQPen(cg_.highlightedText()));
  } else {
    if(iconView()->itemTextBackground() != TQt::NoBrush) {
      p_->fillRect(tr, iconView()->itemTextBackground());
    }
    p_->setPen(cg_.text());
  }

  int align = iconView()->itemTextPos() == TQIconView::Bottom ? AlignHCenter : AlignAuto;
  wordWrap()->drawText(p_, textX, textY, align | KWordWrap::Truncate);
}

TQString EntryIconViewItem::key() const {
  const TQString& sortField = iconView()->sortField();
  if(sortField.isEmpty()) {
    return TDEIconViewItem::key();
  }
  return m_entry->field(sortField);
}

int EntryIconViewItem::compare(TQIconViewItem* item_) const {
  int res = iconView()->compare(this, static_cast<EntryIconViewItem*>(item_));
  return res == 0 ? TDEIconViewItem::compare(item_) : res;
}

#include "entryiconview.moc"
