/* ============================================================
 *
 * This file is a part of digiKam project
 * http://www.digikam.org
 *
 * Date        : 2004-09-07
 * Description : a pop-up menu implementation to display a 
 *               hierarchical view of digiKam tags.
 * 
 * Copyright (C) 2004 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
 * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot 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, 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.
 * 
 * ============================================================ */

#define ADDTAGID 10000

// TQt includes.

#include <tqpixmap.h>
#include <tqstring.h>
#include <tqpainter.h>
#include <tqstyle.h>
#include <tqvaluelist.h>
#include <tqpixmap.h>
#include <tqvaluevector.h>

// KDE includes.

#include <tdeapplication.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <tdemessagebox.h>

// Local includes.

#include "ddebug.h"
#include "albumiconview.h"
#include "albumiconitem.h"
#include "albummanager.h"
#include "albumdb.h"
#include "album.h"
#include "syncjob.h"
#include "tageditdlg.h"
#include "tagspopupmenu.h"
#include "tagspopupmenu.moc"

namespace Digikam
{

class TagsPopupCheckedMenuItem : public TQCustomMenuItem
{
public:

    TagsPopupCheckedMenuItem(TQPopupMenu* popup, const TQString& txt, const TQPixmap& pix)
        : TQCustomMenuItem(), m_popup(popup), m_txt(txt), m_pix(pix)
    {
    }

    virtual TQSize sizeHint()
    {
        TQFont fn = m_popup->font();
        TQFontMetrics fm(fn);
        int w = fm.width(m_txt) + 5 + kapp->style().pixelMetric(TQStyle::PM_IndicatorWidth, 0);
        int h = TQMAX(fm.height(), m_pix.height());
        return TQSize( w, h );
    }

    virtual void paint(TQPainter* p, const TQColorGroup& cg, bool act, bool enabled,
                       int x, int y, int w, int h )
    {
        p->save();
        p->setPen(act ? cg.highlightedText() : cg.highlight());
        p->drawText(x, y, w, h, TQt::AlignLeft|TQt::AlignVCenter, m_txt);
        p->restore();

        if (!m_pix.isNull())
        {
            TQRect pixRect(x/2 - m_pix.width()/2, y, m_pix.width(), m_pix.height());
            p->drawPixmap( pixRect.topLeft(), m_pix );
        }

        int checkWidth  = kapp->style().pixelMetric(TQStyle::PM_IndicatorWidth,  0);
        int checkHeight = kapp->style().pixelMetric(TQStyle::PM_IndicatorHeight, 0);

        TQStyle::SFlags flags = TQStyle::Style_Default;
        flags |= TQStyle::Style_On;
        if (enabled)
            flags |= TQStyle::Style_Enabled;
        if (act)
            flags |= TQStyle::Style_Active;
        
        TQFont fn = m_popup->font();
        TQFontMetrics fm(fn);
        TQRect r(x + 5 + fm.width(m_txt), y + (h/2-checkHeight/2), checkWidth, checkHeight);
        kapp->style().drawPrimitive(TQStyle::PE_CheckMark, p, r, cg, flags);
    }

private:

    TQPopupMenu *m_popup;

    TQString     m_txt;

    TQPixmap     m_pix;
};

// ------------------------------------------------------------------------

class TagsPopupMenuPriv
{
public:

    TagsPopupMenuPriv(){}

    int                 addToID;

    TQPixmap             addTagPix;

    TQValueList<int>     assignedTags;
    TQValueList<TQ_LLONG> selectedImageIDs;

    TagsPopupMenu::Mode mode;
};

TagsPopupMenu::TagsPopupMenu(const TQValueList<TQ_LLONG>& selectedImageIDs, int addToID, Mode mode)
             : TQPopupMenu(0)
{
    d = new TagsPopupMenuPriv;
    d->selectedImageIDs = selectedImageIDs;
    d->addToID          = addToID;
    d->mode             = mode;

    TDEIconLoader *iconLoader = TDEApplication::kApplication()->iconLoader();
    d->addTagPix            = iconLoader->loadIcon("tag",
                                        TDEIcon::NoGroup,
                                        TDEIcon::SizeSmall,
                                        TDEIcon::DefaultState,
                                        0, true);
    
    connect(this, TQ_SIGNAL(aboutToShow()),
            this, TQ_SLOT(slotAboutToShow()));

    connect(this, TQ_SIGNAL(activated(int)),
            this, TQ_SLOT(slotActivated(int)));
}

TagsPopupMenu::~TagsPopupMenu()
{
    delete d;
}

void TagsPopupMenu::clearPopup()
{
    d->assignedTags.clear();
    clear();
}

TQPopupMenu* TagsPopupMenu::buildSubMenu(int tagid)
{
    AlbumManager* man = AlbumManager::instance();
    TAlbum* album     = man->findTAlbum(tagid);
    if (!album)
        return 0;

    TQPopupMenu* popup = new TQPopupMenu(this);

    connect(popup, TQ_SIGNAL(activated(int)), 
            this, TQ_SLOT(slotActivated(int)));

    if (d->mode == ASSIGN)
    {
        popup->insertItem(d->addTagPix, i18n("Add New Tag..."), ADDTAGID + album->id());
        popup->insertSeparator();
                
        TQPixmap pix = SyncJob::getTagThumbnail(album);
        if ((d->mode == ASSIGN) && (d->assignedTags.contains(album->id())))
        {
            popup->insertItem(new TagsPopupCheckedMenuItem(popup, album->title(), pix),
                              d->addToID + album->id());
        }
        else
        {
            popup->insertItem(pix, album->title(), d->addToID + album->id());
        }                
        
        if (album->firstChild())
        {
            popup->insertSeparator();
        }
    }
    else
    {
        if (!album->isRoot())
        {
            TQPixmap pix = SyncJob::getTagThumbnail(album);
            popup->insertItem(pix, album->title(), d->addToID + album->id());
            popup->insertSeparator();
        }
    }
    
    iterateAndBuildMenu(popup, album);

    return popup;
}

void TagsPopupMenu::slotAboutToShow()
{
    clearPopup();

    AlbumManager* man = AlbumManager::instance();

    if (d->mode == REMOVE || d->mode == DISPLAY)
    {
        if (d->selectedImageIDs.isEmpty())
            return;

        d->assignedTags = man->albumDB()->getItemCommonTagIDs(d->selectedImageIDs);

        if (d->assignedTags.isEmpty())
            return;

        // also add the parents of the assigned tags
        IntList tList;
        for (IntList::iterator it = d->assignedTags.begin();
             it != d->assignedTags.end(); ++it)
        {
            TAlbum* album = man->findTAlbum(*it);
            if (album)
            {
                Album* a = album->parent();
                while (a)
                {
                    tList.append(a->id());
                    a = a->parent();
                }
            }
        }

        for (IntList::iterator it = tList.begin();
             it != tList.end(); ++it)
        {
            d->assignedTags.append(*it);
        }
    }
    else if (d->mode == ASSIGN)
    {
        if (d->selectedImageIDs.count() == 1)
        {
            d->assignedTags = man->albumDB()->getItemCommonTagIDs(d->selectedImageIDs);
        }
    }
        
    TAlbum* album = man->findTAlbum(0);
    if (!album)
        return;

    if (d->mode == ASSIGN)
    {
        insertItem(d->addTagPix, i18n("Add New Tag..."), ADDTAGID);
        if (album->firstChild())
        {
            insertSeparator();
        }
    }
    
    iterateAndBuildMenu(this, album);
}

// for qHeapSort
typedef TQPair<TQString, Album*> TagsMenuSortType;
bool operator<(const TagsMenuSortType &lhs, const TagsMenuSortType &rhs)
{
    return lhs.first < rhs.first;
}

void TagsPopupMenu::iterateAndBuildMenu(TQPopupMenu *menu, TAlbum *album)
{
    TQValueVector<TagsMenuSortType> sortedTags;

    for (Album* a = album->firstChild(); a; a = a->next())
    {
        sortedTags.push_back(qMakePair(a->title(), a));
    }

    qHeapSort(sortedTags);
    
    for (TQValueVector<TagsMenuSortType>::Iterator i = sortedTags.begin(); i != sortedTags.end(); ++i)
    {
        Album *a = i->second;
        
        if (d->mode == REMOVE || d->mode == DISPLAY)
        {
            IntList::iterator it = tqFind(d->assignedTags.begin(), 
                                         d->assignedTags.end(), a->id());
            if (it == d->assignedTags.end())
                continue;
        }
        
        TQPixmap pix = SyncJob::getTagThumbnail((TAlbum*)a);
        TQString t = a->title();
        t.replace('&',"&&");

        if (a->firstChild())
        {
            menu->insertItem(pix, t, buildSubMenu(a->id()));
        }
        else
        {
            if ((d->mode == ASSIGN) && (d->assignedTags.contains(a->id())))
            {
                menu->insertItem(new TagsPopupCheckedMenuItem(this, a->title(), pix),
                                                              d->addToID + a->id());
            }
            else
            {
                menu->insertItem(pix, t, d->addToID + a->id());
            }
        }
    }
}

void TagsPopupMenu::slotActivated(int id)
{
    if (id >= ADDTAGID)
    {
        int tagID = id - ADDTAGID;

        AlbumManager* man = AlbumManager::instance();
        TAlbum* parent    = man->findTAlbum(tagID);
        if (!parent)
        {
            DWarning() << "Failed to find album with id "
                       << tagID << endl;
            return;
        }

        TQString title, icon;
        if (!TagEditDlg::tagCreate(kapp->activeWindow(), parent, title, icon))
            return;

        TQMap<TQString, TQString> errMap;
        AlbumList tList = TagEditDlg::createTAlbum(parent, title, icon, errMap);
        TagEditDlg::showtagsListCreationError(kapp->activeWindow(), errMap);

        for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it)
            emit signalTagActivated((*it)->id());
    }
    else
    {
        int tagID = id - d->addToID;
        emit signalTagActivated(tagID);
    }
}

}  // namespace Digikam
