/*
 * Copyright 2006 Milan Digital Audio LLC
 * Copyright 2009-2026 GrandOrgue contributors (see AUTHORS)
 * License GPL-2.0 or later
 * (https://www.gnu.org/licenses/old-licenses/gpl-2.0.html).
 */

#include "GOImageCache.h"

#include <wx/intl.h>
#include <wx/mstream.h>

#include "files/GOOpenedFile.h"
#include "loader/GOLoaderFilename.h"

#include "GOBuffer.h"
#include "GOLog.h"
#include "Images.h"

#define BITMAP_LIST                                                            \
  WOOD(01, 02)                                                                 \
  WOOD(03, 04)                                                                 \
  WOOD(05, 06)                                                                 \
  WOOD(07, 08)                                                                 \
  WOOD(09, 10)                                                                 \
  WOOD(11, 12)                                                                 \
  WOOD(13, 14)                                                                 \
  WOOD(15, 16)                                                                 \
  WOOD(17, 18)                                                                 \
  WOOD(19, 20)                                                                 \
  WOOD(21, 22)                                                                 \
  WOOD(23, 24)                                                                 \
  WOOD(25, 26)                                                                 \
  WOOD(27, 28)                                                                 \
  WOOD(29, 30)                                                                 \
  WOOD(31, 32)                                                                 \
  WOOD(33, 34)                                                                 \
  WOOD(35, 36)                                                                 \
  WOOD(37, 38)                                                                 \
  WOOD(39, 40)                                                                 \
  WOOD(41, 42)                                                                 \
  WOOD(43, 44)                                                                 \
  WOOD(45, 46)                                                                 \
  WOOD(47, 48)                                                                 \
  WOOD(49, 50)                                                                 \
  WOOD(51, 52)                                                                 \
  WOOD(53, 54)                                                                 \
  WOOD(55, 56)                                                                 \
  WOOD(57, 58)                                                                 \
  WOOD(59, 60)                                                                 \
  WOOD(61, 62)                                                                 \
  WOOD(63, 64)                                                                 \
  STOP(01)                                                                     \
  STOP(02)                                                                     \
  STOP(03)                                                                     \
  STOP(04)                                                                     \
  STOP(05)                                                                     \
  STOP(06)                                                                     \
  STOP(07)                                                                     \
  PISTON(01)                                                                   \
  PISTON(02)                                                                   \
  PISTON(03)                                                                   \
  PISTON(04)                                                                   \
  PISTON(05)                                                                   \
  LABEL(01)                                                                    \
  LABEL(02)                                                                    \
  LABEL(03)                                                                    \
  LABEL(04)                                                                    \
  LABEL(05)                                                                    \
  LABEL(06)                                                                    \
  LABEL(07)                                                                    \
  LABEL(08)                                                                    \
  LABEL(10)                                                                    \
  LABEL(11)                                                                    \
  LABEL(12)                                                                    \
  LABEL(13)                                                                    \
  LABEL(14)                                                                    \
  LABEL(15)                                                                    \
  ENCLOSURE(A)                                                                 \
  ENCLOSURE(B)                                                                 \
  ENCLOSURE(C)                                                                 \
  ENCLOSURE(D)                                                                 \
  PEDAL(, Wood, Black, 01)                                                     \
  PEDAL(Inverted, Black, Wood, 01)                                             \
  MANUAL(, White, Black, 01)                                                   \
  MANUAL(Inverted, Black, White, 01)                                           \
  MANUAL(Wood, Wood, Black, 01)                                                \
  MANUAL(InvertedWood, Black, Wood, 01)                                        \
  PEDAL(, Wood, Black, 02)                                                     \
  PEDAL(Inverted, Black, Wood, 02)                                             \
  MANUAL(, White, Black, 02)                                                   \
  MANUAL(Inverted, Black, White, 02)                                           \
  MANUAL(Wood, Wood, Black, 02)                                                \
  MANUAL(InvertedWood, Black, Wood, 02)

#define ENCLOSURE(A)                                                           \
  ENC(A, 00)                                                                   \
  ENC(A, 01)                                                                   \
  ENC(A, 02)                                                                   \
  ENC(A, 03)                                                                   \
  ENC(A, 04)                                                                   \
  ENC(A, 05)                                                                   \
  ENC(A, 06)                                                                   \
  ENC(A, 07)                                                                   \
  ENC(A, 08)                                                                   \
  ENC(A, 09)                                                                   \
  ENC(A, 10)                                                                   \
  ENC(A, 11)                                                                   \
  ENC(A, 12)                                                                   \
  ENC(A, 13)                                                                   \
  ENC(A, 14)                                                                   \
  ENC(A, 15)
#define WOOD(A, B)                                                             \
  DECLARE_IMAGE(Wood##A, "wood" #A);                                           \
  DECLARE_IMAGE_ROT(Wood##A, "wood" #B);
#define STOP(A)                                                                \
  DECLARE_IMAGE(drawstop##A##off, "drawstop" #A "_off");                       \
  DECLARE_IMAGE(drawstop##A##on, "drawstop" #A "_on");
#define PISTON(A)                                                              \
  DECLARE_IMAGE(piston##A##off, "piston" #A "_off");                           \
  DECLARE_IMAGE(piston##A##on, "piston" #A "_on");
#define LABEL(A) DECLARE_IMAGE(label##A, "label" #A);
#define ENC(A, B) DECLARE_IMAGE(Enclosure##A##B, "enclosure" #A #B);
#define PEDAL(A, B, C, D)                                                      \
  DECLARE_IMAGE(PedalNatural##B##Up##D, "Pedal" #A #D "Off_Natural");          \
  DECLARE_IMAGE(PedalNatural##B##Down##D, "Pedal" #A #D "On_Natural");         \
  DECLARE_IMAGE(PedalSharp##C##Up##D, "Pedal" #A #D "Off_Sharp");              \
  DECLARE_IMAGE(PedalSharp##C##Down##D, "Pedal" #A #D "On_Sharp");
#define MANUAL(A, B, C, D)                                                     \
  DECLARE_IMAGE(ManualC##B##Up##D, "Manual" #A #D "Off_C");                    \
  DECLARE_IMAGE(ManualC##B##Down##D, "Manual" #A #D "On_C");                   \
  DECLARE_IMAGE(ManualD##B##Up##D, "Manual" #A #D "Off_D");                    \
  DECLARE_IMAGE(ManualD##B##Down##D, "Manual" #A #D "On_D");                   \
  DECLARE_IMAGE(ManualE##B##Up##D, "Manual" #A #D "Off_E");                    \
  DECLARE_IMAGE(ManualE##B##Down##D, "Manual" #A #D "On_E");                   \
  DECLARE_IMAGE(ManualNatural##B##Up##D, "Manual" #A #D "Off_Natural");        \
  DECLARE_IMAGE(ManualNatural##B##Down##D, "Manual" #A #D "On_Natural");       \
  DECLARE_IMAGE(ManualSharp##C##Up##D, "Manual" #A #D "Off_Sharp");            \
  DECLARE_IMAGE(ManualSharp##C##Down##D, "Manual" #A #D "On_Sharp");

#define DECLARE_IMAGE(A, B) wxImage GetImage_##A();
#define DECLARE_IMAGE_ROT(A, B)

BITMAP_LIST

#undef DECLARE_IMAGE
#undef DECLARE_IMAGE_ROT

#define GOBitmapPrefix "../GO:"

#define DECLARE_IMAGE(A, B)                                                    \
  RegisterImage(                                                               \
    wxT(GOBitmapPrefix B), WX_EMPTY_STRING, new wxImage(GetImage_##A()));
#define DECLARE_IMAGE_ROT(A, B)                                                \
  static wxImage A##_r(GetImage_##A().Rotate90());                             \
  RegisterImage(wxT(GOBitmapPrefix B), WX_EMPTY_STRING, new wxImage(A##_r));

const wxString GOImageCache::WX_EMPTY_STRING = wxEmptyString;

const wxImage *GOImageCache::FindImage(
  const wxString &filename, const wxString &maskname) const {
  const wxImage *pImage = nullptr;

  for (unsigned i = 0; i < m_filenames.size(); i++)
    if (m_filenames[i] == filename && m_masknames[i] == maskname) {
      pImage = m_images[i];
      break;
    }
  return pImage;
}

void GOImageCache::RegisterImage(
  const wxString &filename, const wxString &maskname, wxImage *pImage) {
  m_images.push_back(pImage);
  m_filenames.push_back(filename);
  m_masknames.push_back(maskname);
}

GOImageCache::GOImageCache(const GOFileStore &fileStore)
  : r_FileStore(fileStore) {
  BITMAP_LIST;
}

const wxImage *GOImageCache::GetWoodImage(unsigned woodImageNum) const {
  return FindImage(
    wxString::Format(wxT(GOBitmapPrefix "wood%02d"), woodImageNum),
    wxEmptyString);
}

bool GOImageCache::LoadImageFromFile(const wxString &filename, wxImage &image) {
  bool result;
  GOLog *const log = dynamic_cast<GOLog *>(wxLog::GetActiveTarget());

  if (log)
    log->SetCurrentFileName(filename);

  try {
    GOLoaderFilename name;
    name.Assign(filename);

    std::unique_ptr<GOOpenedFile> file = name.Open(r_FileStore);
    GOBuffer<char> data;

    result = file->ReadContent(data);
    if (result) {
      wxMemoryInputStream is(data.get(), data.GetSize());

      result = image.LoadFile(is, wxBITMAP_TYPE_ANY, -1);
    }

    if (log)
      log->ClearCurrentFileName();
  } catch (...) {
    if (log)
      log->ClearCurrentFileName();
    throw;
  }
  return result;
}

const wxImage *GOImageCache::LoadImage(
  const wxString &filename, const wxString &maskName) {
  const wxImage *pImage = FindImage(filename, maskName);

  if (!pImage) {
    wxImage image, maskimage;

    if (!LoadImageFromFile(filename, image))
      throw wxString::Format(
        _("Failed to open the graphic '%s'"), filename.c_str());

    if (maskName != wxEmptyString) {
      if (!LoadImageFromFile(maskName, maskimage))
        throw wxString::Format(
          _("Failed to open the graphic '%s'"), maskName.c_str());

      if (
        image.GetWidth() != maskimage.GetWidth()
        || image.GetHeight() != maskimage.GetHeight())
        throw wxString::Format(
          _("bitmap size of '%s' does not match mask '%s'"),
          filename.c_str(),
          maskName.c_str());

      image.SetMaskFromImage(maskimage, 0xFF, 0xFF, 0xFF);
    }

    wxImage *pNewImage = new wxImage(image);

    if (pNewImage->HasMask())
      pNewImage->InitAlpha();
    RegisterImage(filename, maskName, pNewImage);
    pImage = pNewImage;
  }
  return pImage;
}
