
#include "tagengine.h"

#include <tqfile.h>

// #include <taglib/attachedpictureframe.h>
#include <taglib/fileref.h>
#include <taglib/id3v1genres.h> //used to load genre list
#include <taglib/mpegfile.h>
#include <taglib/tag.h>
#include <taglib/tstring.h>
#include <taglib/tlist.h>
#include <taglib/apetag.h>
#include <taglib/id3v2tag.h>
#include <taglib/id3v1tag.h>
#include <taglib/mpcfile.h>
#include <taglib/mpegfile.h>
#include <taglib/oggfile.h>
#include <taglib/oggflacfile.h>
#include <taglib/vorbisfile.h>
#include <taglib/flacfile.h>
#include <taglib/textidentificationframe.h>
#include <taglib/uniquefileidentifierframe.h>
#include <taglib/xiphcomment.h>
#include "wavpack/wvfile.h"
#include "trueaudio/ttafile.h"

#include <config.h>
#ifdef HAVE_MP4V2
#include "mp4/mp4file.h"
#include "mp4/mp4tag.h"
#else
#include "m4a/mp4file.h"
#include "m4a/mp4itunestag.h"
#endif

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


// TODO COMPILATION tag
// FIXME BPM tag

TagData::TagData( const TQString& _artist, const TQString& _composer,
             const TQString& _album, const TQString& _title,
             const TQString& _genre, const TQString& _comment,
             int _track, int _disc, int _year,
             int _length, int _fileSize, int _bitrate, int _samplingRate )
{
    artist = _artist;
    composer = _composer;
    album = _album;
    title = _title;
    genre = _genre;
    comment = _comment;
    track = _track;
    disc = _disc;
    year = _year;
    length = _length;
    fileSize = _fileSize;
    bitrate = _bitrate;
    samplingRate = _samplingRate;
}

TagData::~TagData()
{}


TagEngine::TagEngine()
{
    TagLib::StringList genres = TagLib::ID3v1::genreList();
    for( TagLib::StringList::ConstIterator it = genres.begin(), end = genres.end(); it != end; ++it )
        genreList += TStringToTQString( (*it) );

    genreList.sort();
}

TagEngine::~TagEngine()
{}

TagData* TagEngine::readTags( const TQString& file )
{
    TagLib::FileRef fileref( TQFile::encodeName(file) );
    TagData* tagData = new TagData();

    if( !fileref.isNull() )
    {
        TagLib::Tag *tag = fileref.tag();

        tagData->track = 0;
        tagData->year = 0;
        tagData->disc = 0;
        tagData->track_gain = 210588; // 0 is a valid value
        tagData->album_gain = 210588;

        if( tag )
        {
            tagData->title = TStringToTQString( tag->title() ).stripWhiteSpace();
            tagData->artist = TStringToTQString( tag->artist() ).stripWhiteSpace();
            tagData->album = TStringToTQString( tag->album() ).stripWhiteSpace();
            tagData->genre = TStringToTQString( tag->genre() ).stripWhiteSpace();
            tagData->comment = TStringToTQString( tag->comment() ).stripWhiteSpace();
            tagData->track = tag->track();
            tagData->year = tag->year();
        }

        TagLib::AudioProperties *audioProperties = fileref.audioProperties();

        if( audioProperties )
        {
            tagData->length = audioProperties->lengthInSeconds();
            // TODO read all information
            //tagData->fileSize = ;
            // = audioProperties->channels();
            //tagData->bitrate = audioProperties->bitrate();
            tagData->samplingRate = audioProperties->sampleRate();
        }

        TQString disc;
        TQString track_gain;
        TQString album_gain;
        if ( TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File *>( fileref.file() ) )
        {
            if ( file->ID3v2Tag() )
            {
                if ( !file->ID3v2Tag()->frameListMap()[ "TPOS" ].isEmpty() )
                    disc = TStringToTQString( file->ID3v2Tag()->frameListMap()["TPOS"].front()->toString() ).stripWhiteSpace();

                if ( !file->ID3v2Tag()->frameListMap()[ "TCOM" ].isEmpty() )
                    tagData->composer = TStringToTQString( file->ID3v2Tag()->frameListMap()["TCOM"].front()->toString() ).stripWhiteSpace();
            }
            if ( file->APETag() )
            {
                if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
                    track_gain = TStringToTQString( file->APETag()->itemListMap()["REPLAYGAIN_TRACK_GAIN"].toString() ).stripWhiteSpace();

                if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
                    album_gain = TStringToTQString( file->APETag()->itemListMap()["REPLAYGAIN_ALBUM_GAIN"].toString() ).stripWhiteSpace();
            }
        }
        else if ( TagLib::Ogg::Vorbis::File *file = dynamic_cast<TagLib::Ogg::Vorbis::File *>( fileref.file() ) )
        {
            if ( file->tag() )
            {
                if ( !file->tag()->fieldListMap()[ "COMPOSER" ].isEmpty() )
                    tagData->composer = TStringToTQString( file->tag()->fieldListMap()["COMPOSER"].front() ).stripWhiteSpace();

                if ( !file->tag()->fieldListMap()[ "DISCNUMBER" ].isEmpty() )
                    disc = TStringToTQString( file->tag()->fieldListMap()["DISCNUMBER"].front() ).stripWhiteSpace();

                if ( !file->tag()->fieldListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
                    track_gain = TStringToTQString( file->tag()->fieldListMap()["REPLAYGAIN_TRACK_GAIN"].front() ).stripWhiteSpace();

                if ( !file->tag()->fieldListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
                    album_gain = TStringToTQString( file->tag()->fieldListMap()["REPLAYGAIN_ALBUM_GAIN"].front() ).stripWhiteSpace();
            }
        }
        else if ( TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File *>( fileref.file() ) )
        {
            if ( file->xiphComment() )
            {
                if ( !file->xiphComment()->fieldListMap()[ "COMPOSER" ].isEmpty() )
                    tagData->composer = TStringToTQString( file->xiphComment()->fieldListMap()["COMPOSER"].front() ).stripWhiteSpace();

                if ( !file->xiphComment()->fieldListMap()[ "DISCNUMBER" ].isEmpty() )
                    disc = TStringToTQString( file->xiphComment()->fieldListMap()["DISCNUMBER"].front() ).stripWhiteSpace();

                if ( !file->xiphComment()->fieldListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
                    track_gain = TStringToTQString( file->xiphComment()->fieldListMap()["REPLAYGAIN_TRACK_GAIN"].front() ).stripWhiteSpace();

                if ( !file->xiphComment()->fieldListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
                    album_gain = TStringToTQString( file->xiphComment()->fieldListMap()["REPLAYGAIN_ALBUM_GAIN"].front() ).stripWhiteSpace();
            }

            /*if ( file->tag() )
            {
                if ( !file->tag()->fieldListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
                    track_gain = TStringToTQString( file->tag()->fieldListMap()["REPLAYGAIN_TRACK_GAIN"].front() ).stripWhiteSpace();

                if ( !file->tag()->fieldListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
                    album_gain = TStringToTQString( file->tag()->fieldListMap()["REPLAYGAIN_ALBUM_GAIN"].front() ).stripWhiteSpace();
            }*/
        }
        else if ( TagLib::MP4::File *file = dynamic_cast<TagLib::MP4::File *>( fileref.file() ) )
        {
            TagLib::MP4::Tag *mp4tag = dynamic_cast<TagLib::MP4::Tag *>( file->tag() );
            if( mp4tag )
            {
                tagData->composer = TStringToTQString( mp4tag->composer() );

                disc = TQString::number( mp4tag->disk() );
            }
        }
/*        else if ( TagLib::MPC::File *file = dynamic_cast<TagLib::MPC::File *>( fileref.file() ) )
        {
            if ( file->APETag() )
            {
                if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
                    track_gain = TStringToTQString( file->APETag()->itemListMap()["REPLAYGAIN_TRACK_GAIN"].toString() ).stripWhiteSpace();

                if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
                    album_gain = TStringToTQString( file->APETag()->itemListMap()["REPLAYGAIN_ALBUM_GAIN"].toString() ).stripWhiteSpace();
            }
        }*/
        else if ( TagLib::WavPack::File *file = dynamic_cast<TagLib::WavPack::File *>( fileref.file() ) )
        {
            if ( file->APETag() )
            {
                if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
                    track_gain = TStringToTQString( file->APETag()->itemListMap()["REPLAYGAIN_TRACK_GAIN"].toString() ).stripWhiteSpace();

                if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
                    album_gain = TStringToTQString( file->APETag()->itemListMap()["REPLAYGAIN_ALBUM_GAIN"].toString() ).stripWhiteSpace();
            }
        }
        else if ( TagLib::TTA::File *file = dynamic_cast<TagLib::TTA::File *>( fileref.file() ) )
        {
            if ( file->ID3v2Tag() )
            {
                if ( !file->ID3v2Tag()->frameListMap()[ "TPOS" ].isEmpty() )
                    disc = TStringToTQString( file->ID3v2Tag()->frameListMap()["TPOS"].front()->toString() ).stripWhiteSpace();

                if ( !file->ID3v2Tag()->frameListMap()[ "TCOM" ].isEmpty() )
                    tagData->composer = TStringToTQString( file->ID3v2Tag()->frameListMap()["TCOM"].front()->toString() ).stripWhiteSpace();
            }
        }

        if( !disc.isEmpty() )
        {
            int i = disc.find('/');
            if( i != -1 )
                // disc.right( i ).toInt() is total number of discs, we don't use this at the moment
                tagData->disc = disc.left( i ).toInt();
            else
                tagData->disc = disc.toInt();
        }

        if( !track_gain.isEmpty() )
        {
            int i = track_gain.find(' ');
            if( i != -1 )
                tagData->track_gain = track_gain.left( i ).toFloat();
            else
                tagData->track_gain = track_gain.toFloat();
        }

        if( !album_gain.isEmpty() )
        {
            int i = album_gain.find(' ');
            if( i != -1 )
                tagData->album_gain = album_gain.left( i ).toFloat();
            else
                tagData->album_gain = album_gain.toFloat();
        }

        return tagData;
    }

    return 0;
}

bool TagEngine::writeTags( const TQString& file, TagData* tagData )
{
    if( !tagData ) tagData = new TagData();

    //Set default codec to UTF-8 (see bugs 111246 and 111232)
    TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding( TagLib::String::UTF8 );

    TagLib::FileRef fileref( TQFile::encodeName(file), false );

    if ( !fileref.isNull() )
    {
        TagLib::Tag* tag = fileref.tag();
        if ( tag )
        {
            tag->setTitle( QStringToTString(tagData->title) );
            tag->setArtist( QStringToTString(tagData->artist) );
            tag->setAlbum( QStringToTString(tagData->album) );
            tag->setTrack( tagData->track );
            tag->setYear( tagData->year );
            tag->setComment( QStringToTString(tagData->comment) );
            tag->setGenre( QStringToTString(tagData->genre) );
        }
        else
        {
            return false;
        }

        if ( TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File *>( fileref.file() ) )
        {
            if ( file->ID3v2Tag() )
            {
                if ( !file->ID3v2Tag()->frameListMap()[ "TPOS" ].isEmpty() )
                {
                    file->ID3v2Tag()->frameListMap()[ "TPOS" ].front()->setText( QStringToTString( TQString::number(tagData->disc) ) );
                }
                else
                {
                    TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TPOS", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
                    frame->setText( QStringToTString( TQString::number(tagData->disc) ) );
                    file->ID3v2Tag()->addFrame( frame );
                }

                if ( !file->ID3v2Tag()->frameListMap()[ "TCOM" ].isEmpty() )
                {
                    file->ID3v2Tag()->frameListMap()[ "TCOM" ].front()->setText( QStringToTString( tagData->composer ) );
                }
                else
                {
                    TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TCOM", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
                    frame->setText( QStringToTString( tagData->composer ) );
                    file->ID3v2Tag()->addFrame( frame );
                }

                // HACK sets the id3v2 genre tag as string
                if ( !file->ID3v2Tag()->frameListMap()[ "TCON" ].isEmpty() )
                {
                    file->ID3v2Tag()->frameListMap()[ "TCON" ].front()->setText( QStringToTString( tagData->genre ) );
                }
                else
                {
                    TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TCON", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
                    frame->setText( QStringToTString( tagData->genre ) );
                    file->ID3v2Tag()->addFrame( frame );
                }

                // HACK sets the id3v2 year tag
                if ( !file->ID3v2Tag()->frameListMap()[ "TYER" ].isEmpty() )
                {
                    file->ID3v2Tag()->frameListMap()[ "TYER" ].front()->setText( QStringToTString( TQString::number(tagData->year) ) );
                }
                else
                {
                    TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TYER", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
                    frame->setText( QStringToTString( TQString::number(tagData->year) ) );
                    file->ID3v2Tag()->addFrame( frame );
                }
            }
        }
        else if ( TagLib::Ogg::Vorbis::File *file = dynamic_cast<TagLib::Ogg::Vorbis::File *>( fileref.file() ) )
        {
            if ( file->tag() )
            {
                file->tag()->addField( "COMPOSER", QStringToTString( tagData->composer ), true );

                file->tag()->addField( "DISCNUMBER", QStringToTString( TQString::number(tagData->disc) ), true );
            }
        }
        else if ( TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File *>( fileref.file() ) )
        {
            if ( file->xiphComment() )
            {
                file->xiphComment()->addField( "COMPOSER", QStringToTString( tagData->composer ), true );

                file->xiphComment()->addField( "DISCNUMBER", QStringToTString( TQString::number(tagData->disc) ), true );
            }
        }
        else if ( TagLib::MP4::File *file = dynamic_cast<TagLib::MP4::File *>( fileref.file() ) )
        {
            TagLib::MP4::Tag *mp4tag = dynamic_cast<TagLib::MP4::Tag *>( file->tag() );
            if( mp4tag )
            {
                mp4tag->setComposer( QStringToTString( tagData->composer ) );

                mp4tag->setDisk( tagData->disc );
            }
        }
        if ( TagLib::TTA::File *file = dynamic_cast<TagLib::TTA::File *>( fileref.file() ) )
        {
            if ( file->ID3v2Tag() )
            {
                if ( !file->ID3v2Tag()->frameListMap()[ "TPOS" ].isEmpty() )
                {
                    file->ID3v2Tag()->frameListMap()[ "TPOS" ].front()->setText( QStringToTString( TQString::number(tagData->disc) ) );
                }
                else
                {
                    TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TPOS", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
                    frame->setText( QStringToTString( TQString::number(tagData->disc) ) );
                    file->ID3v2Tag()->addFrame( frame );
                }

                if ( !file->ID3v2Tag()->frameListMap()[ "TCOM" ].isEmpty() )
                {
                    file->ID3v2Tag()->frameListMap()[ "TCOM" ].front()->setText( QStringToTString( tagData->composer ) );
                }
                else
                {
                    TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TCOM", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
                    frame->setText( QStringToTString( tagData->composer ) );
                    file->ID3v2Tag()->addFrame( frame );
                }
            }
        }

        return fileref.save();
    }
    return false;
}

// bool TagEngine::canWrite( TQString format )
// {
//     format = format.lower();
// 
//     if( format == "ogg" ||
//         format == "flac" || format == "fla" ||
//         format == "mp3" || // TODO mp2 ?
//         format == "mpc" ||
//         format == "aac" ||
//         format == "ape" || format == "mac" ||
//         format == "aa" ||
//         format == "m4a" || format == "m4b" || format == "m4p" || format == "mp4" || format == "m4v" || format == "mp4v" ||
//         format == "ra" || format == "rv" || format == "rm" || format == "rmj" || format == "rmvb" ||
//         format == "wma" || format == "asf" )
//     {
//         return true;
//     }
//     else {
//         return false;
//     }
// }

