// Aseprite
// Copyright (C) 2001-2016  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/document.h"
#include "app/file/file.h"
#include "app/file/file_format.h"
#include "app/file/file_formats_manager.h"
#include "base/path.h"
#include "base/string.h"
#include "doc/cel.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 "docio/detect_format.h"

#include <cstring>

namespace app {

using namespace doc;

std::string get_readable_palette_extensions()
{
  std::string buf = get_readable_extensions();
  buf += ",col,gpl,hex,pal";
  return buf;
}

std::string get_writable_palette_extensions()
{
  std::string buf = get_writable_extensions();
  buf += ",col,gpl,hex,pal";
  return buf;
}

Palette* load_palette(const char* filename)
{
  docio::FileFormat docioFormat = docio::detect_format(filename);
  Palette* pal = nullptr;

  switch (docioFormat) {

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

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

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

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

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

      base::UniquePtr<FileOp> fop(
        FileOp::createLoadDocumentOperation(
          nullptr, filename,
          FILE_LOAD_SEQUENCE_NONE |
          FILE_LOAD_ONE_FRAME));

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

        if (fop->document() &&
            fop->document()->sprite() &&
            fop->document()->sprite()->palette(frame_t(0))) {
          pal = new 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)
{
  docio::FileFormat docioFormat = docio::detect_format_by_file_extension(filename);
  bool success = false;

  switch (docioFormat) {

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

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

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

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

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

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

      app::Context tmpContext;
      doc::Document* doc = tmpContext.documents().add(
        w, h, (pal->size() <= 256 ? doc::ColorMode::INDEXED:
                                    doc::ColorMode::RGB), 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;
}

}
