/***************************************************************************
    copyright            : (C) 2004 Nathan Toone
                         : (C) 2005 Michael Pyne <michael.pyne@kdemail.net>
    email                : nathan@toonetown.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 <tdeglobal.h>
#include <tdeapplication.h>
#include <tdestandarddirs.h>
#include <kdebug.h>

#include <tqregexp.h>
#include <tqlayout.h>
#include <tqlabel.h>
#include <tqcursor.h>

#include "collectionlist.h"
#include "playlistsearch.h"
#include "playlistitem.h"
#include "coverinfo.h"
#include "tag.h"

struct CoverPopup : public TQWidget
{
    CoverPopup(const TQPixmap &image, const TQPoint &p) :
        TQWidget(0, 0, WDestructiveClose | WX11BypassWM)
    {
        TQHBoxLayout *layout = new TQHBoxLayout(this);
        TQLabel *label = new TQLabel(this);

        layout->addWidget(label);
        label->setFrameStyle(TQFrame::Box | TQFrame::Raised);
        label->setLineWidth(1);
        label->setPixmap(image);

        setGeometry(p.x(), p.y(), label->width(), label->height());
        show();
    }
    virtual void leaveEvent(TQEvent *) { close(); }
    virtual void mouseReleaseEvent(TQMouseEvent *) { close(); }
};

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////


CoverInfo::CoverInfo(const FileHandle &file) :
    m_file(file),
    m_hasCover(false),
    m_haveCheckedForCover(false),
    m_coverKey(CoverManager::NoMatch),
    m_needsConverting(false)
{

}

bool CoverInfo::hasCover()
{
    if(m_haveCheckedForCover)
        return m_hasCover;

    m_haveCheckedForCover = true;

    // Check for new-style covers.  First let's determine what our coverKey is
    // if it's not already set, as that's also tracked by the CoverManager.
    if(m_coverKey == CoverManager::NoMatch)
        m_coverKey = CoverManager::idForTrack(m_file.absFilePath());

    // We were assigned a key, let's see if we already have a cover.  Notice
    // that due to the way the CoverManager is structured, we should have a
    // cover if we have a cover key.  If we don't then either there's a logic
    // error, or the user has been mucking around where they shouldn't.
    if(m_coverKey != CoverManager::NoMatch)
        m_hasCover = CoverManager::hasCover(m_coverKey);

    // We *still* don't have it?  Check the old-style covers then.
    if(!m_hasCover) {
        m_hasCover = TQFile(coverLocation(FullSize)).exists();

        if(m_hasCover)
            m_needsConverting = true;
    }

    return m_hasCover;
}

void CoverInfo::clearCover()
{
    m_hasCover = false;

    // Yes, we have checked, and we don't have it. ;)
    m_haveCheckedForCover = true;

    m_needsConverting = false;

    // We don't need to call removeCover because the CoverManager will
    // automatically unlink the cover if we were the last track to use it.
    CoverManager::setIdForTrack(m_file.absFilePath(), CoverManager::NoMatch);
    m_coverKey = CoverManager::NoMatch;
}

void CoverInfo::setCover(const TQImage &image)
{
    if(image.isNull())
        return;

    m_haveCheckedForCover = true;
    m_needsConverting = false;
    m_hasCover = true;

    TQPixmap cover;
    cover.convertFromImage(image);

    // If we use replaceCover we'll change the cover for every other track
    // with the same coverKey, which we don't want since that case will be
    // handled by Playlist.  Instead just replace this track's cover.
    m_coverKey = CoverManager::addCover(cover, m_file.tag()->artist(), m_file.tag()->album());
    if(m_coverKey != CoverManager::NoMatch)
        CoverManager::setIdForTrack(m_file.absFilePath(), m_coverKey);
}

void CoverInfo::setCoverId(coverKey id)
{
    m_coverKey = id;
    m_haveCheckedForCover = true;
    m_needsConverting = false;
    m_hasCover = id != CoverManager::NoMatch;

    // Inform CoverManager of the change.
    CoverManager::setIdForTrack(m_file.absFilePath(), m_coverKey);
}

void CoverInfo::applyCoverToWholeAlbum(bool overwriteExistingCovers) const
{
    TQString artist = m_file.tag()->artist();
    TQString album = m_file.tag()->album();
    PlaylistSearch::ComponentList components;
    ColumnList columns;

    columns.append(PlaylistItem::ArtistColumn);
    components.append(PlaylistSearch::Component(artist, false, columns, PlaylistSearch::Component::Exact));

    columns.clear();
    columns.append(PlaylistItem::AlbumColumn);
    components.append(PlaylistSearch::Component(album, false, columns, PlaylistSearch::Component::Exact));

    PlaylistList playlists;
    playlists.append(CollectionList::instance());

    PlaylistSearch search(playlists, components, PlaylistSearch::MatchAll);

    // Search done, iterate through results.

    PlaylistItemList results = search.matchedItems();
    PlaylistItemList::ConstIterator it = results.constBegin();
    for(; it != results.constEnd(); ++it) {

        // Don't worry about files that somehow already have a tag,
        // unless the coversion is forced.
        if(!overwriteExistingCovers && !(*it)->file().coverInfo()->m_needsConverting)
            continue;

        kdDebug(65432) << "Setting cover for: " << *it << endl;
        (*it)->file().coverInfo()->setCoverId(m_coverKey);
    }
}

TQPixmap CoverInfo::pixmap(CoverSize size) const
{
    if(m_needsConverting)
        convertOldStyleCover();

    if(m_coverKey == CoverManager::NoMatch)
        return TQPixmap();

    if(size == Thumbnail)
        return CoverManager::coverFromId(m_coverKey, CoverManager::Thumbnail);
    else
        return CoverManager::coverFromId(m_coverKey, CoverManager::FullSize);
}

void CoverInfo::popup() const
{
    TQPixmap image = pixmap(FullSize);
    TQPoint mouse  = TQCursor::pos();
    TQRect desktop = TDEApplication::desktop()->screenGeometry(mouse);
    
    int x = mouse.x();
    int y = mouse.y();
    int height = image.size().height() + 4;
    int width  = image.size().width() + 4;

    // Detect the right direction to pop up (always towards the center of the
    // screen), try to pop up with the mouse pointer 10 pixels into the image in
    // both directions.  If we're too close to the screen border for this margin,
    // show it at the screen edge, accounting for the four pixels (two on each
    // side) for the window border.

    if(x - desktop.x() < desktop.width() / 2)
        x = (x - desktop.x() < 10) ? desktop.x() : (x - 10);
    else
        x = (x - desktop.x() > desktop.width() - 10) ? desktop.width() - width +desktop.x() : (x - width + 10);

    if(y - desktop.y() < desktop.height() / 2)
        y = (y - desktop.y() < 10) ? desktop.y() : (y - 10);
    else
        y = (y - desktop.y() > desktop.height() - 10) ? desktop.height() - height + desktop.y() : (y - height + 10);

    new CoverPopup(image, TQPoint(x, y));
}

/**
 * DEPRECATED
 */
TQString CoverInfo::coverLocation(CoverSize size) const
{
    TQString fileName(TQFile::encodeName(m_file.tag()->artist() + " - " + m_file.tag()->album()));
    TQRegExp maskedFileNameChars("[ /?:\"]");

    fileName.replace(maskedFileNameChars, "_");
    fileName.append(".png");

    TQString dataDir = TDEGlobal::dirs()->saveLocation("appdata");
    TQString subDir;

    switch (size) {
    case FullSize:
        subDir = "large/";
        break;
    default:
        break;
    }
    TQString fileLocation = dataDir + "covers/" + subDir + fileName.lower();

    return fileLocation;
}

bool CoverInfo::convertOldStyleCover() const
{
    // Ah, old-style cover.  Let's transfer it to the new system.
    kdDebug() << "Found old style cover for " << m_file.absFilePath() << endl;

    TQString artist = m_file.tag()->artist();
    TQString album = m_file.tag()->album();
    TQString oldLocation = coverLocation(FullSize);
    m_coverKey = CoverManager::addCover(oldLocation, artist, album);

    m_needsConverting = false;

    if(m_coverKey != CoverManager::NoMatch) {
        CoverManager::setIdForTrack(m_file.absFilePath(), m_coverKey);

        // Now let's also set the ID for the tracks matching the track and
        // artist at this point so that the conversion is complete, otherwise
        // we can't tell apart the "No cover on purpose" and "Has no cover yet"
        // possibilities.

        applyCoverToWholeAlbum();

        // If we convert we need to remove the old cover otherwise we'll find
        // it later if the user un-sets the new cover.
        if(!TQFile::remove(oldLocation))
            kdError(65432) << "Unable to remove converted cover at " << oldLocation << endl;

        return true;
    }
    else {
        kdDebug() << "We were unable to replace the old style cover.\n";
        return false;
    }
}