// 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_apic.h"

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

#define THA_MAX_APIC_SIZE (8 * 1024 * 1024)

THA_GenericAPIC::THA_GenericAPIC()
{}

THA_GenericAPIC::~THA_GenericAPIC()
{}

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

  m_fn.Set(filename);

#ifdef _WIN32
  WDL_WCHAR fn[2048];
  WDL_MBtoWideStr(fn, m_fn.Get(), sizeof(fn));
  TagLib::FileRef fr(fn, false, TagLib::AudioProperties::Fast);
#else
  TagLib::FileRef fr(m_fn.Get(), false, 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 (TagLib::FLAC::File *flac =
      dynamic_cast<TagLib::FLAC::File *>(fr.file()))
    {
      if (flac->ID3v2Tag())
      {
        ReadID3v2(flac->ID3v2Tag());
      }
      else if (flac->xiphComment())
      {
        ReadXiphComment(flac->xiphComment());

        if (!m_apic.GetSize())
        {
          TagLib::List<TagLib::FLAC::Picture *> apic =
            flac->pictureList();

          for (TagLib::List<TagLib::FLAC::Picture *>::Iterator it =
            apic.begin(); it != apic.end(); ++it)
          {
            if ((*it)->data().size() > 0)
            {
              m_apic.Resize((*it)->data().size());
              memcpy(m_apic.Get(), (*it)->data().data(), (*it)->data().size());
              break;
            }
          }
        }
      }
    }
    else if (TagLib::Ogg::Vorbis::File *vorbis =
      dynamic_cast<TagLib::Ogg::Vorbis::File *>(fr.file()))
    {
      if (vorbis->tag())
      {
        ReadXiphComment(vorbis->tag());
      }
    }
  }
  else
  {
    return false;
  }

  return true;
}

int THA_GenericAPIC::GetPicSize() const
{
  return m_apic.GetSize();
}

void *THA_GenericAPIC::GetPic() const
{
  return m_apic.Get();
}

void THA_GenericAPIC::ReadID3v2(TagLib::ID3v2::Tag *tag)
{
  static const int scores[] =
  {
    0,  /* Other */
    5,  /* 32x32 PNG image that should be used as the file icon */
    4,  /* File icon of a different size or format. */
    20, /* Front cover image of the album. */
    19, /* Back cover image of the album. */
    13, /* Inside leaflet page of the album. */
    18, /* Image from the album itself. */
    17, /* Picture of the lead artist or soloist. */
    16, /* Picture of the artist or performer. */
    14, /* Picture of the conductor. */
    15, /* Picture of the band or orchestra. */
    9,  /* Picture of the composer. */
    8,  /* Picture of the lyricist or text writer. */
    7,  /* Picture of the recording location or studio. */
    10, /* Picture of the artists during recording. */
    11, /* Picture of the artists during performance. */
    6,  /* Picture from a movie or video related to the track. */
    1,  /* Picture of a large, coloured fish. */
    12, /* Illustration related to the track. */
    3,  /* Logo of the band or performer. */
    2   /* Logo of the publisher (record company). */
  };

  TagLib::ID3v2::FrameList apic = tag->frameListMap()["APIC"];

  if (!apic.isEmpty())
  {
    TagLib::ID3v2::AttachedPictureFrame *pd = NULL;
    for (TagLib::ID3v2::FrameList::Iterator it = apic.begin();
      it != apic.end(); ++it)
    {
      TagLib::ID3v2::AttachedPictureFrame *pf =
        dynamic_cast<TagLib::ID3v2::AttachedPictureFrame *>(*it);

      if (!pf) continue;

      if (pd == NULL) pd = pf;
      else
      {
        int a = pd->type() >= sizeof(scores) ? 0 : scores[pd->type()];
        int b = pf->type() >= sizeof(scores) ? 0 : scores[pf->type()];
        if (a > b) pd = pf;
      }
    }

    if (pd)
    {
      m_apic.Resize(pd->picture().size());
      memcpy(m_apic.Get(), pd->picture().data(),
        pd->picture().size());
    }
  }
}

void THA_GenericAPIC::ReadXiphComment(TagLib::Ogg::XiphComment *tag)
{
  TagLib::StringList cover = tag->fieldListMap()["COVERART"];

  if (cover.size() != 0)
  {
    WDL_HeapBuf buf;
    buf.Resize(THA_MAX_APIC_SIZE);
    const int buflen = wdl_base64decode(cover[0].toCString(true),
      (unsigned char *)buf.Get(), buf.GetSize());
    if (buflen)
    {
      m_apic.Resize(buflen);
      memcpy(m_apic.Get(), buf.Get(), buflen);
    }
  }
  else
  {
    TagLib::StringList blockpic = tag->fieldListMap()["METADATA_BLOCK_PICTURE"];

    if (blockpic.size() > 0)
    {
      WDL_HeapBuf buf;
      buf.Resize(THA_MAX_APIC_SIZE);
      const int buflen = wdl_base64decode(blockpic[0].toCString(true),
        (unsigned char *)buf.Get(), buf.GetSize());
      if (buflen)
      {
        m_apic.Resize(buflen);
        memcpy(m_apic.Get(), buf.Get(), buflen);
      }
    }
  }
}
