/***************************************************************************
    begin                : Sun Feb 17 2002
    copyright            : (C) 2002 - 2004 by Scott Wheeler
    email                : wheeler@kde.org
***************************************************************************/

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

#include <tqregexp.h>
#include <tqfile.h>

#include <taglib/tag.h>
#include <taglib/mpegfile.h>
#include <taglib/vorbisfile.h>
#include <taglib/flacfile.h>
#include <taglib/xiphcomment.h>
#include <taglib/id3v2framefactory.h>

#if (TAGLIB_MAJOR_VERSION > 1) || \
      ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 2))
#include <taglib/oggflacfile.h>
#define TAGLIB_1_2
#endif
#if (TAGLIB_MAJOR_VERSION > 1) || \
    ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 3))
#include <taglib/mpcfile.h>
#define TAGLIB_1_3
#endif

#include "cache.h"
#include "tag.h"
#include "mediafiles.h"
#include "stringshare.h"

#define TStringToTQString(s) TQString::fromUtf8((s).toCString(true))

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


Tag::Tag(const TQString &fileName) :
    m_fileName(fileName),
    m_track(0),
    m_year(0),
    m_seconds(0),
    m_bitrate(0),
    m_isValid(false)
{
    // using tqDebug here since we want this to show up in non-debug builds as well

    tqDebug("Reading tag for %s", fileName.local8Bit().data());

    if(MediaFiles::isMP3(fileName)) {
        TagLib::MPEG::File file(TQFile::encodeName(fileName).data());
        if(file.isValid())
            setup(&file);
    }

    else if(MediaFiles::isFLAC(fileName)) {
        TagLib::FLAC::File file(TQFile::encodeName(fileName).data());
        if(file.isValid())
            setup(&file);
    }
#ifdef TAGLIB_1_3
    else if(MediaFiles::isMPC(fileName)) {
        kdDebug(65432) << "Trying to resolve Musepack file" << endl;
        TagLib::MPC::File file(TQFile::encodeName(fileName).data());
        if(file.isValid())
            setup(&file);
    }
#endif
#ifdef TAGLIB_1_2
    else if(MediaFiles::isOggFLAC(fileName)) {
        TagLib::Ogg::FLAC::File file(TQFile::encodeName(fileName).data());
        if(file.isValid())
            setup(&file);
    }
#endif
    else if(MediaFiles::isVorbis(fileName)) {
        TagLib::Vorbis::File file(TQFile::encodeName(fileName).data());
        if(file.isValid())
            setup(&file);
    }

    else {
        kdError(65432) << "Couldn't resolve the mime type of \"" <<
            fileName << "\" -- this shouldn't happen." << endl;
    }
}

bool Tag::save()
{
    bool result;
    TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF8);

    TagLib::File *file = 0;

    if(MediaFiles::isMP3(m_fileName))
        file = new TagLib::MPEG::File(TQFile::encodeName(m_fileName).data());
    else if(MediaFiles::isFLAC(m_fileName))
        file = new TagLib::FLAC::File(TQFile::encodeName(m_fileName).data());
#ifdef TAGLIB_1_3
    else if(MediaFiles::isMPC(m_fileName))
        file = new TagLib::MPC::File(TQFile::encodeName(m_fileName).data());
#endif
#ifdef TAGLIB_1_2
    else if(MediaFiles::isOggFLAC(m_fileName))
        file = new TagLib::Ogg::FLAC::File(TQFile::encodeName(m_fileName).data());
#endif
    else if(MediaFiles::isVorbis(m_fileName))
        file = new TagLib::Vorbis::File(TQFile::encodeName(m_fileName).data());

    if(file && file->isValid() && file->tag() && !file->readOnly()) {
        file->tag()->setTitle(QStringToTString(m_title));
        file->tag()->setArtist(QStringToTString(m_artist));
        file->tag()->setAlbum(QStringToTString(m_album));
        file->tag()->setGenre(QStringToTString(m_genre));
        file->tag()->setComment(QStringToTString(m_comment));
        file->tag()->setTrack(m_track);
        file->tag()->setYear(m_year);
#ifdef TAGLIB_1_2
        result = file->save();
#else
        file->save();
        result = true;
#endif
    }
    else {
        kdError(65432) << "Couldn't save file." << endl;
        result = false;
    }

    delete file;
    return result;
}

CacheDataStream &Tag::read(CacheDataStream &s)
{
    switch(s.cacheVersion()) {
    case 1: {
        TQ_INT32 track;
        TQ_INT32 year;
        TQ_INT32 bitrate;
        TQ_INT32 seconds;

        s >> m_title
          >> m_artist
          >> m_album
          >> m_genre
          >> track
          >> year
          >> m_comment
          >> bitrate
          >> m_lengthString
          >> seconds;

        m_track = track;
        m_year = year;
        m_bitrate = bitrate;
        m_seconds = seconds;
        break;
    }
    default: {
        static TQString dummyString;
        static int dummyInt;
        TQString bitrateString;

        s >> dummyInt
          >> m_title
          >> m_artist
          >> m_album
          >> m_genre
          >> dummyInt
          >> m_track
          >> dummyString
          >> m_year
          >> dummyString
          >> m_comment
          >> bitrateString
          >> m_lengthString
          >> m_seconds
          >> dummyString;

        bool ok;
        m_bitrate = bitrateString.toInt(&ok);
        if(!ok)
            m_bitrate = 0;
        break;
    }
    }

    // Try to reduce memory usage: share tags that frequently repeat, squeeze others

    m_title.squeeze();
    m_lengthString.squeeze();

    m_comment = StringShare::tryShare(m_comment);
    m_artist  = StringShare::tryShare(m_artist);
    m_album   = StringShare::tryShare(m_album);
    m_genre   = StringShare::tryShare(m_genre);

    return s;
}

////////////////////////////////////////////////////////////////////////////////
// private methods
////////////////////////////////////////////////////////////////////////////////

Tag::Tag(const TQString &fileName, bool) :
    m_fileName(fileName),
    m_track(0),
    m_year(0),
    m_seconds(0),
    m_bitrate(0),
    m_isValid(true)
{

}

void Tag::setup(TagLib::File *file)
{
    m_title   = TStringToTQString(file->tag()->title()).stripWhiteSpace();
    m_artist  = TStringToTQString(file->tag()->artist()).stripWhiteSpace();
    m_album   = TStringToTQString(file->tag()->album()).stripWhiteSpace();
    m_genre   = TStringToTQString(file->tag()->genre()).stripWhiteSpace();
    m_comment = TStringToTQString(file->tag()->comment()).stripWhiteSpace();

    m_track = file->tag()->track();
    m_year  = file->tag()->year();

    m_seconds = file->audioProperties()->length();
    m_bitrate = file->audioProperties()->bitrate();

    const int seconds = m_seconds % 60;
    const int minutes = (m_seconds - seconds) / 60;

    m_lengthString = TQString::number(minutes) + (seconds >= 10 ? ":" : ":0") + TQString::number(seconds);

    if(m_title.isEmpty()) {
        int i = m_fileName.findRev('/');
        int j = m_fileName.findRev('.');
        m_title = i > 0 ? m_fileName.mid(i + 1, j - i - 1) : m_fileName;
    }

    m_isValid = true;
}

////////////////////////////////////////////////////////////////////////////////
// related functions
////////////////////////////////////////////////////////////////////////////////

TQDataStream &operator<<(TQDataStream &s, const Tag &t)
{
    s << t.title()
      << t.artist()
      << t.album()
      << t.genre()
      << TQ_INT32(t.track())
      << TQ_INT32(t.year())
      << t.comment()
      << TQ_INT32(t.bitrate())
      << t.lengthString()
      << TQ_INT32(t.seconds());

    return s;
}

CacheDataStream &operator>>(CacheDataStream &s, Tag &t)
{
    return t.read(s);
}
