// Aseprite
// Copyright (C) 2018-2022  Igara Studio S.A.
// Copyright (C) 2001-2018  David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.

#ifdef HAVE_CONFIG_H
  #include "config.h"
#endif

#include "app/context.h"
#include "app/doc.h"
#include "app/file/file.h"
#include "app/file/file_format.h"
#include "app/file/file_formats_manager.h"
#include "base/fs.h"
#include "base/string.h"
#include "dio/detect_format.h"
#include "doc/cel.h"
#include "doc/file/act_file.h"
#include "doc/file/col_file.h"
#include "doc/file/gpl_file.h"
#include "doc/file/hex_file.h"
#include "doc/file/pal_file.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/palette.h"
#include "doc/sprite.h"

#include <cstring>

namespace app {

using namespace doc;

static const char* palExts[] = { "act", "col", "gpl", "hex", "pal" };

base::paths get_readable_palette_extensions()
{
  base::paths paths = get_readable_extensions();
  for (const char* s : palExts)
    paths.push_back(s);
  return paths;
}

base::paths get_writable_palette_extensions()
{
  base::paths paths = get_writable_extensions(FILE_SUPPORT_INDEXED);
  for (const char* s : palExts)
    paths.push_back(s);
  return paths;
}

std::unique_ptr<doc::Palette> load_palette(const char* filename, const FileOpConfig* config)
{
  dio::FileFormat dioFormat = dio::detect_format(filename);
  std::unique_ptr<Palette> pal = nullptr;

  switch (dioFormat) {
    case dio::FileFormat::ACT_PALETTE: pal = doc::file::load_act_file(filename); break;

    case dio::FileFormat::COL_PALETTE: pal = doc::file::load_col_file(filename); break;

    case dio::FileFormat::GPL_PALETTE: pal = doc::file::load_gpl_file(filename); break;

    case dio::FileFormat::HEX_PALETTE: pal = doc::file::load_hex_file(filename); break;

    case dio::FileFormat::PAL_PALETTE: pal = doc::file::load_pal_file(filename); break;

    default:                           {
      FileFormat* ff = FileFormatsManager::instance()->getFileFormat(dioFormat);
      if (!ff || !ff->support(FILE_SUPPORT_LOAD))
        break;

      std::unique_ptr<FileOp> fop(FileOp::createLoadDocumentOperation(
        nullptr,
        filename,
        FILE_LOAD_CREATE_PALETTE | FILE_LOAD_SEQUENCE_NONE | FILE_LOAD_ONE_FRAME,
        config));

      if (fop && !fop->hasError()) {
        fop->operate(nullptr);
        fop->postLoad();

        if (fop->document() && fop->document()->sprite() &&
            fop->document()->sprite()->palette(frame_t(0))) {
          pal = std::make_unique<Palette>(*fop->document()->sprite()->palette(frame_t(0)));
        }

        delete fop->releaseDocument();
        fop->done();
      }
      break;
    }
  }

  if (pal)
    pal->setFilename(filename);

  return pal;
}

bool save_palette(const char* filename,
                  const Palette* pal,
                  int columns,
                  const gfx::ColorSpaceRef& cs)
{
  dio::FileFormat dioFormat = dio::detect_format_by_file_extension(filename);
  bool success = false;

  switch (dioFormat) {
    case dio::FileFormat::ACT_PALETTE: success = doc::file::save_act_file(pal, filename); break;

    case dio::FileFormat::COL_PALETTE: success = doc::file::save_col_file(pal, filename); break;

    case dio::FileFormat::GPL_PALETTE: success = doc::file::save_gpl_file(pal, filename); break;

    case dio::FileFormat::HEX_PALETTE: success = doc::file::save_hex_file(pal, filename); break;

    case dio::FileFormat::PAL_PALETTE: success = doc::file::save_pal_file(pal, filename); break;

    default:                           {
      FileFormat* ff = FileFormatsManager::instance()->getFileFormat(dioFormat);
      if (!ff || !ff->support(FILE_SUPPORT_SAVE))
        break;

      int w = (columns > 0 ? std::clamp(columns, 0, pal->size()) : pal->size());
      int h = (pal->size() / w) + (pal->size() % w > 0 ? 1 : 0);

      Context tmpContext;
      gfx::ColorSpaceRef colorSpace = (cs ? cs : gfx::ColorSpace::MakeNone());
      Doc* doc = tmpContext.documents().add(new Doc(Sprite::MakeStdSprite(
        ImageSpec((pal->size() <= 256 ? doc::ColorMode::INDEXED : doc::ColorMode::RGB),
                  w,
                  h,
                  0,
                  colorSpace),
        pal->size())));

      Sprite* sprite = doc->sprite();
      doc->sprite()->setPalette(pal, false);

      LayerImage* layer = static_cast<LayerImage*>(sprite->root()->firstLayer());
      layer->configureAsBackground();

      Image* image = layer->cel(frame_t(0))->image();
      image->clear(0);

      int x, y, c;
      for (y = c = 0; y < h; ++y) {
        for (x = 0; x < w; ++x) {
          if (doc->colorMode() == doc::ColorMode::INDEXED)
            image->putPixel(x, y, c);
          else
            image->putPixel(x, y, pal->entry(c));

          if (++c == pal->size())
            goto done;
        }
      }
    done:;

      doc->setFilename(filename);
      success = (save_document(&tmpContext, doc) == 0);

      doc->close();
      delete doc;
      break;
    }
  }

  return success;
}

} // namespace app
