// Copyright (c) 2020 Giorgos Vougioukas
//
// LGPL v2.1
//
// Some portions derived from:
/*****************************************************************************
 * taglib.cpp: Taglib tag parser/writer
 *****************************************************************************
 * Copyright (C) 2003-2016 VLC authors and VideoLAN
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *          Rafaël Carré <funman@videolanorg>
 *          Rémi Duraffort <ivoire@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "thalia_taglib/taglib_tag.h"

#include "WDL/sha.h"
#include "WDL/wdlutf8.h"

THA_GenericTag::THA_GenericTag()
  : m_track(0)
  , m_year(0)
  , m_bitrate(0)
  , m_samplerate(0.0)
  , m_channels(0)
  , m_length(0)
  , m_file_size(0)
{}

THA_GenericTag::~THA_GenericTag()
{}

bool THA_GenericTag::Open(const char *filename)
{
  WDL_ASSERT(filename != NULL);

  m_fn.Set(filename);

  WDL_SHA1 sha;
  char hash[WDL_SHA1SIZE];
  sha.reset();
  sha.add(m_fn.Get(), m_fn.GetLength());
  sha.result(hash);

  for (int i = 0; i < WDL_SHA1SIZE; i++)
  {
    m_sha.AppendFormatted(64, "%02x", (unsigned char *)(hash[i] & 0xff));
  }

#ifdef _WIN32
  WDL_WCHAR fn[2048];
  WDL_MBtoWideStr(fn, m_fn.Get(), sizeof(fn));
  TagLib::FileRef fr(fn, true, TagLib::AudioProperties::Fast);
#else
  TagLib::FileRef fr(m_fn.Get(), true, TagLib::AudioProperties::Fast);
#endif

  if (!fr.isNull() && fr.tag() && !fr.tag()->isEmpty())
  {
    if (TagLib::MPEG::File *mpeg =
      dynamic_cast<TagLib::MPEG::File *>(fr.file()))
    {
      if (mpeg->ID3v2Tag())
      {
        ReadID3v2(mpeg->ID3v2Tag());
      }
      else if (mpeg->ID3v1Tag())
      {
        ReadID3v1(mpeg->ID3v1Tag());
      }
    }
    else if (TagLib::FLAC::File *flac =
      dynamic_cast<TagLib::FLAC::File *>(fr.file()))
    {
      if (flac->xiphComment())
      {
        ReadXiphComment(flac->xiphComment());
      }
    }
    else if (TagLib::Vorbis::File *vorbis =
      dynamic_cast<TagLib::Vorbis::File *>(fr.file()))
    {
      if (vorbis->tag())
      {
        ReadXiphComment(vorbis->tag());
      }
    }
    else if (TagLib::File *plain =
      dynamic_cast<TagLib::File *>(fr.file()))
    {
      if (plain->tag())
      {
        ReadPlain(plain->tag());
      }
    }
  }

  if (!fr.isNull() && fr.audioProperties())
  {
    m_file_size = (WDL_INT64)fr.file()->length();

    TagLib::AudioProperties *aprop = fr.audioProperties();

    m_bitrate = aprop->bitrate();
    m_channels = aprop->channels();
    m_samplerate = (double)aprop->sampleRate();
    m_length = aprop->length();

    return true;
  }

  return false;
}

const char *THA_GenericTag::GetType() const
{
  if (!stricmp(m_fn.get_fileext(), ".flac")) return "FLAC";
  else if(!stricmp(m_fn.get_fileext(), ".mp3")) return "MP3";
  else if(!stricmp(m_fn.get_fileext(), ".ogg")) return "OGG";
  else if(!stricmp(m_fn.get_fileext(), ".wv")) return "WV";
  else if(!stricmp(m_fn.get_fileext(), ".wav")) return "WAV";
  else if(!stricmp(m_fn.get_fileext(), ".aiff")) return "AIFF";
  else if(!stricmp(m_fn.get_fileext(), ".opus")) return "OPUS";

  return "";
}

const char *THA_GenericTag::GetTitle() const
{
  return m_title.Get();
}

const char *THA_GenericTag::GetArtist() const
{
  return m_artist.Get();
}

const char *THA_GenericTag::GetAlbum() const
{
  return m_album.Get();
}

const char *THA_GenericTag::GetGenre() const
{
  return m_genre.Get();
}

const char *THA_GenericTag::GetTrack() const
{
  return m_track.Get();
}

const char *THA_GenericTag::GetYear() const
{
  return m_year.Get();
}

const char *THA_GenericTag::GetComment() const
{
  return m_comment.Get();
}

int THA_GenericTag::GetBitRate() const
{
  return m_bitrate;
}

double THA_GenericTag::GetSampleRate() const
{
  return m_samplerate;
}

int THA_GenericTag::GetChannels() const
{
  return m_channels;
}

double THA_GenericTag::GetLength() const
{
  return m_length;
}

WDL_INT64 THA_GenericTag::GetFileSize() const
{
  return m_file_size;
}

const char *THA_GenericTag::GetFileName() const
{
  return m_fn.get_filepart();
}

const char *THA_GenericTag::GetFilePath() const
{
  return m_fn.Get();
}

const char *THA_GenericTag::GetFileExtension() const
{
  return m_fn.get_fileext();
}

const char *THA_GenericTag::GetSHA() const
{
  return m_sha.Get();
}

void THA_GenericTag::ReadID3v2(TagLib::ID3v2::Tag *tag)
{
  m_title.Set(tag->title().to8Bit(true).c_str());
  m_artist.Set(tag->artist().to8Bit(true).c_str());
  m_album.Set(tag->album().to8Bit(true).c_str());
  m_genre.Set(tag->genre().to8Bit(true).c_str());
  m_comment.Set(tag->comment().to8Bit(true).c_str());

  if (tag->track())
  {
    m_track.SetFormatted(512, "%d", (int)tag->track());
  }
  else
  {
    m_track.Set("");
  }

  if (tag->year())
  {
    m_year.SetFormatted(512, "%d", (int)tag->year());
  }
  else
  {
    m_year.Set("");
  }
}

void THA_GenericTag::ReadID3v1(TagLib::ID3v1::Tag *tag)
{
  m_title.Set(tag->title().to8Bit(true).c_str());
  m_artist.Set(tag->artist().to8Bit(true).c_str());
  m_album.Set(tag->album().to8Bit(true).c_str());
  m_genre.Set(tag->genre().to8Bit(true).c_str());
  m_comment.Set(tag->comment().to8Bit(true).c_str());

  if (tag->track())
  {
    m_track.SetFormatted(512, "%d", (int)tag->track());
  }
  else
  {
    m_track.Set("");
  }

  if (tag->year())
  {
    m_year.SetFormatted(512, "%d", (int)tag->year());
  }
  else
  {
    m_year.Set("");
  }
}

void THA_GenericTag::ReadXiphComment(TagLib::Ogg::XiphComment *tag)
{
  m_title.Set(tag->title().to8Bit(true).c_str());
  m_artist.Set(tag->artist().to8Bit(true).c_str());
  m_album.Set(tag->album().to8Bit(true).c_str());
  m_genre.Set(tag->genre().to8Bit(true).c_str());
  m_comment.Set(tag->comment().to8Bit(true).c_str());

  if (tag->track())
  {
    m_track.SetFormatted(512, "%d", (int)tag->track());
  }
  else
  {
    m_track.Set("");
  }

  if (tag->year())
  {
    m_year.SetFormatted(512, "%d", (int)tag->year());
  }
  else
  {
    m_year.Set("");
  }
}

void THA_GenericTag::ReadPlain(TagLib::Tag *tag)
{
  m_title.Set(tag->title().to8Bit(true).c_str());
  m_artist.Set(tag->artist().to8Bit(true).c_str());
  m_album.Set(tag->album().to8Bit(true).c_str());
  m_genre.Set(tag->genre().to8Bit(true).c_str());
  m_comment.Set(tag->comment().to8Bit(true).c_str());

  if (tag->track())
  {
    m_track.SetFormatted(512, "%d", (int)tag->track());
  }
  else
  {
    m_track.Set("");
  }

  if (tag->year())
  {
    m_year.SetFormatted(512, "%d", (int)tag->year());
  }
  else
  {
    m_year.Set("");
  }
}

void THA_GenericTag::SetTitle(const char *val)
{
  m_title.Set(val);
}

void THA_GenericTag::SetArtist(const char *val)
{
  m_artist.Set(val);
}

void THA_GenericTag::SetAlbum(const char *val)
{
  m_album.Set(val);
}

void THA_GenericTag::SetGenre(const char *val)
{
  m_genre.Set(val);
}

void THA_GenericTag::SetTrack(const char *val)
{
  m_track.Set(val);
}

void THA_GenericTag::SetYear(const char *val)
{
  m_year.Set(val);
}

void THA_GenericTag::SetComment(const char *val)
{
  m_comment.Set(val);
}

bool THA_GenericTag::Save()
{
#ifdef _WIN32
  WDL_WCHAR fn[2048];
  WDL_MBtoWideStr(fn, m_fn.Get(), sizeof(fn));
  TagLib::FileRef fr(fn, true, TagLib::AudioProperties::Fast);
#else
  TagLib::FileRef fr(m_fn.Get(), true, TagLib::AudioProperties::Fast);
#endif

  if (!fr.isNull() && fr.tag())
  {
    TagLib::Tag *tag = fr.tag();
    tag->setTitle(TagLib::String(m_title.Get(), TagLib::String::UTF8));
    tag->setArtist(TagLib::String(m_artist.Get(), TagLib::String::UTF8));
    tag->setAlbum(TagLib::String(m_album.Get(), TagLib::String::UTF8));
    tag->setGenre(TagLib::String(m_genre.Get(), TagLib::String::UTF8));
    tag->setTrack(atoi(m_track.Get()));
    tag->setYear(atoi(m_year.Get()));
    tag->setComment(TagLib::String(m_comment.Get(), TagLib::String::UTF8));
    return fr.save();
  }
  return false;
}
