#include "songinfo.h"
#include "strmanip.h"

#include <stdio.h>

// FIXME: Proper check!
/* Tag support using TagLib */
#define WITH_TAGLIB 

#ifdef WITH_ID3LIB
# include <id3/tag.h>

class Mp3Info : public InfoSlave
{
public:
    Mp3Info(const string &filename)
    {
        id3tag.Clear();
        id3tag.Link(filename.c_str());
    }
    virtual string get_artist()
        { return get_text_frame(ID3FID_LEADARTIST); }
    virtual string get_title()
        { return get_text_frame(ID3FID_TITLE); }
    virtual string get_album()
        { return get_text_frame(ID3FID_ALBUM); }

    virtual int get_year()
        { return atoi(get_text_frame(ID3FID_YEAR).c_str()); }
    virtual int get_length();
    virtual int get_bitrate();
    virtual int get_track()
        { return atoi(get_text_frame(ID3FID_TRACKNUM).c_str()); }

protected:
    string get_text_frame(ID3_FrameID id);
    ID3_Tag id3tag;
};

string Mp3Info::get_text_frame(ID3_FrameID id)
{
    static char buffer[1024];
    ID3_Frame *myFrame = id3tag.Find(id);
    if (myFrame)
    {
        myFrame->Field(ID3FN_TEXT).Get(buffer, 1024);
        return buffer;
    }
    return "";
}

// realinfo

int Mp3Info::get_length()
{
  Mp3_Headerinfo *mp3info = (Mp3_Headerinfo *)id3tag.GetMp3HeaderInfo();

  if (mp3info == NULL) {
        return -1;
  }
  
  return mp3info->time;
}

int Mp3Info::get_bitrate()
{
  Mp3_Headerinfo *mp3info = (Mp3_Headerinfo *)id3tag.GetMp3HeaderInfo();

  if (mp3info == NULL) {
        return -1;
  }

  struct stat statbuf;
  stat(filename.c_str(), &statbuf);

  int bitrate = ((statbuf.st_size*8)/1000)/mp3info->time;

  // fix rounding errors created by id3 information for cbr tracks

  if (bitrate == 127)
    bitrate = 128;
  else if (bitrate == 191)
    bitrate = 192;
  else if (bitrate == 255)
    bitrate = 256;

  return bitrate;
}
#endif

#ifdef WITH_VORBIS
# include <vorbis/vorbisfile.h>
# include <vorbis/codec.h>

class OggInfo : public InfoSlave
{
public:
    OggInfo(const string &filename);

    virtual string get_artist()
        { return get_comment("artist"); }
    virtual string get_title()
        { return get_comment("title"); }
    virtual string get_album()
        { return get_comment("album"); }

    virtual int get_year() { 
      string year = "";
      if (get_comment("year") != "")
	year = get_comment("year");
      else if (get_comment("date") != "")
	year = get_comment("date");
      if (year != "")
	return atoi(year.c_str());
      else
	return 0;
    }
    virtual int get_length()
        { return length; }
    virtual int get_bitrate()
        { return info->bitrate_nominal/1000; }
    virtual int get_track()
        { return atoi(get_comment("tracknumber").c_str()); }

    ~OggInfo();
private:
    string get_comment(const string &id);

    OggVorbis_File vf;
    vorbis_comment *comment;
    int length;
};

// OggInfo
OggInfo::OggInfo(const string &filename)
{
    comment = 0;

    FILE *fh = fopen(filename.c_str(), "r");
    if (!fh)
        return;

    if (ov_open(fh, &vf, NULL, 0))
        return;

    comment = ov_comment(&vf, -1);
    length = static_cast<int>(ov_time_total(&vf,-1));
}

OggInfo::~OggInfo()
{
    vorbis_comment_clear(comment);
    ov_clear(&vf);
}

string OggInfo::get_comment(const string &id)
{
    // Why exactly does vorbis_comment_query take a non-const char* ??
    char *content = 0, *tag = const_cast<char*>(id.c_str());
    return (comment &&
            (content = vorbis_comment_query(comment, tag, 0))) ?  content : "";
}
#endif

#ifdef WITH_TAGLIB

#include <fileref.h>
#include <tag.h>

#include <iostream>

using namespace TagLib;

class TagInfo : public InfoSlave
{
    public:
        TagInfo(const string &filename)
#ifndef STANDALONE
	  : fileref(filename.c_str(), AudioProperties::Average) {}
#else
	  : fileref(filename.c_str(), AudioProperties::Accurate) {}
#endif

        virtual string get_artist()
        {
            return !fileref.isNull() && fileref.tag() ? 
                    fileref.tag()->artist().toCString() : "";
        }
        virtual string get_title()
        {
            return !fileref.isNull() && fileref.tag() ? 
                    fileref.tag()->title().toCString() : "";
        }
        virtual string get_album()
        {
            return !fileref.isNull() && fileref.tag() ? 
                    fileref.tag()->album().toCString() : "";
        }

        virtual int get_year()
        {
	    return !fileref.isNull() && fileref.tag() ? 
                    fileref.tag()->year() : 0;
	}

        virtual int get_length()
        {
	    return !fileref.isNull() && fileref.audioProperties() ? 
                    fileref.audioProperties()->length() : 0;
	}

        virtual int get_bitrate()
        {
	    return !fileref.isNull() && fileref.audioProperties() ? 
                    fileref.audioProperties()->bitrate() : 0;
	}

        virtual int get_track()
        {
            return !fileref.isNull() && fileref.tag() ? 
                    fileref.tag()->track() : 0;
	}

    private:
        FileRef fileref;
};
#endif

// SongInfo
void SongInfo::link(const string &_filename)
{
    if (filename == _filename)
        return;

    filename = _filename;

    delete myslave;
    myslave = 0;

    if (filename.length() > 3)
    {
        string ext = string_tolower(path_get_extension(filename));

        if (false) {}
#ifdef WITH_TAGLIB
        else if (1)
            myslave = new TagInfo(filename);
#endif
#ifdef WITH_ID3LIB
        else if (ext == "mp3")
            myslave = new Mp3Info(filename);
#endif
#ifdef WITH_VORBIS
        else if (ext == "ogg")
            myslave = new OggInfo(filename);
#endif
    }

    if (!myslave)
        myslave = new InfoSlave();
}
