// Aseprite Document Library
// Copyright (c) 2019-present Igara Studio S.A.
// Copyright (c) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.

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

#include "doc/cel.h"

#include "doc/grid.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/sprite.h"
#include "doc/tile.h"
#include "gfx/rect.h"

namespace doc {

Cel::Cel(frame_t frame, const ImageRef& image)
  : Object(ObjectType::Cel)
  , m_layer(NULL)
  , m_frame(frame)
  , m_data(new CelData(image))
{
  m_data->incRefs();
}

Cel::Cel(frame_t frame, const CelDataRef& celData)
  : Object(ObjectType::Cel)
  , m_layer(NULL)
  , m_frame(frame)
  , m_data(celData)
{
  m_data->incRefs();
}

Cel::~Cel()
{
}

// static
Cel* Cel::MakeCopy(const frame_t newFrame, const Cel* other)
{
  Cel* cel = new Cel(newFrame, ImageRef(Image::createCopy(other->image())));

  cel->setPosition(other->position());
  cel->setOpacity(other->opacity());
  cel->copyNonsharedPropertiesFrom(other);
  return cel;
}

// static
Cel* Cel::MakeLink(const frame_t newFrame, const Cel* other)
{
  Cel* cel = new Cel(newFrame, other->dataRef());
  cel->copyNonsharedPropertiesFrom(other);
  return cel;
}

void Cel::setFrame(frame_t frame)
{
  ASSERT(m_layer == NULL);
  m_frame = frame;
}

void Cel::setDataRef(const CelDataRef& celData)
{
  ASSERT(celData);
  if (m_data)
    m_data->decRefs();
  m_data = celData;
  if (m_data)
    m_data->incRefs();
}

void Cel::setPosition(int x, int y)
{
  setPosition(gfx::Point(x, y));
}

void Cel::setPosition(const gfx::Point& pos)
{
  m_data->setPosition(pos);
}

void Cel::setBounds(const gfx::Rect& bounds)
{
  m_data->setBounds(bounds);
}

void Cel::setBoundsF(const gfx::RectF& bounds)
{
  m_data->setBoundsF(bounds);
}

void Cel::setOpacity(int opacity)
{
  m_data->setOpacity(opacity);
}

void Cel::setZIndex(int zindex)
{
  m_zIndex = zindex;
}

Document* Cel::document() const
{
  ASSERT(m_layer);
  if (m_layer && m_layer->sprite())
    return m_layer->sprite()->document();
  else
    return NULL;
}

Sprite* Cel::sprite() const
{
  ASSERT(m_layer);
  if (m_layer)
    return m_layer->sprite();
  else
    return NULL;
}

Cel* Cel::link() const
{
  ASSERT(m_data);
  if (!m_data)
    return nullptr;

  if (links() > 0) {
    for (frame_t fr = 0; fr < m_frame; ++fr) {
      Cel* possible = m_layer->cel(fr);
      if (possible && possible->dataRef().get() == m_data.get())
        return possible;
    }
  }

  return NULL;
}

std::size_t Cel::links() const
{
  if (m_data)
    return std::max<std::size_t>(0, m_data->refs() - 1);
  return 0;
}

void Cel::suspendObject()
{
  if (m_data) {
    if (m_data->decRefs() == 0) {
      // Suspend the CelData only if this was the latest cel referencing it.
      m_data->suspendObject();
    }
  }

  Object::suspendObject();
}

void Cel::restoreObject()
{
  Object::restoreObject();

  if (m_data) {
    if (m_data->incRefs() == 1) {
      m_data->restoreObject();
    }
  }
}

void Cel::setParentLayer(Layer* layer)
{
  m_layer = layer;
  fixupImage();
}

Grid Cel::grid() const
{
  if (m_layer) {
    if (m_layer->isTilemap()) {
      doc::Grid grid = static_cast<LayerTilemap*>(m_layer)->tileset()->grid();
      grid.origin(grid.origin() + position());
      return grid;
    }
    else
      return m_layer->grid();
  }
  return Grid();
}

void Cel::copyNonsharedPropertiesFrom(const Cel* fromCel)
{
  setZIndex(fromCel->zIndex());
}

void Cel::fixupImage()
{
  // Change the mask color to the sprite mask color
  if (m_layer && image()) {
    image()->setMaskColor(
      (image()->pixelFormat() == IMAGE_TILEMAP) ? notile : m_layer->sprite()->transparentColor());
    ASSERT(m_data);
    m_data->adjustBounds(m_layer);
  }
}

} // namespace doc
