/*------------------------------------------------------------------------------
 src/lib/djImage/pixelData.cxx
 
 Copyright (c) 2004-2006 Darby Johnston.
 All rights reserved.
 Use is subject to license terms.
------------------------------------------------------------------------------*/

//#define DJ_DBG
#include "djPixelData.h"
#include <string.h>

namespace dj {

PixelDataInfo::PixelDataInfo(const Vec2i & size, const Pixel & pixel) :
  _size(size), _pixel(pixel)
{}

void PixelDataInfo::set(const Vec2i & size, const Pixel & pixel)
{
  _size = size;
  _pixel = pixel;
}

void PixelDataInfo::size(const Vec2i & in) { _size = in; }
void PixelDataInfo::pixel(const Pixel & in) { _pixel = in; }
void PixelDataInfo::format(PIXEL_FORMAT in) { _pixel.format(in); }
void PixelDataInfo::type(PIXEL_TYPE in) { _pixel.type(in); }

void PixelData::init()
{
  _data = 0;
  _pixelSize = 0;
  _imageSize = 0;
}

PixelData::PixelData()
{
  init();
}

PixelData::PixelData(const PixelData & in) :
  PixelDataInfo(in)
{
  init();
  set(in._size, in._pixel);
  memCopy(in._data, _data, imageSize(in));
}

PixelData::PixelData(const PixelDataInfo & in)
{
  init();
  set(in.size(), in.pixel());
}

PixelData::PixelData(const Vec2i & size, const Pixel & pixel)
{
  init();
  set(size, pixel);
}

PixelData::~PixelData() { del(); }

PixelData & PixelData::operator = (const PixelData & in)
{
  if (&in != this) {
    set(in._size, in._pixel);
    memCopy(in._data, _data, imageSize(in));
  }
  return *this;
}

//static uint8_t _tmp [2048 * 2048 * 4 * 4];

void PixelData::set(const Vec2i & size, const Pixel & pixel)
{
  DJ_DBG_FNC("dj::PixelData::set");
  DJ_DBG_PRN("size = " << size);
  DJ_DBG_PRN("pixel = " << pixel);
  
  const int _imageSizePrev = _imageSize;

  PixelDataInfo::set(size, pixel);
  
  _pixelSize = pixelSize(*this);
  _imageSize = imageSize(*this);
  DJ_DBG_PRN("image size = " << _imageSize);

  if (_imageSize != _imageSizePrev) {
    del();
    if (_imageSize > 0) {

      // XXX Memory alignment?
      //
      //_data =
      //  reinterpret_cast<uint8_t *>(new int [_imageSize / sizeof(int) + 1]);
      _data = new uint8_t [_imageSize];
      //_data = _tmp;
      DJ_DBG_PRN("data = " << int(_data));

      //memZero(_data, _imageSize);
    }
  }
}

void PixelData::zero()
{
  memZero(_data, _imageSize);
}

uint8_t * PixelData::data(const Vec2i & in)
{
  DJ_ASSERT(in.x() >= 0 && in.x() < _size.x());
  DJ_ASSERT(in.y() >= 0 && in.y() < _size.y());
  
  //DJ_DBG_FNC("dj::PixelData::data");
  //DJ_DBG_PRN("in = " << in);
  
  return _data + (in.y() * _size.x() + in.x()) * _pixelSize;
}

uint8_t * PixelData::data(int x, int y)
{
  DJ_ASSERT(x >= 0 && x < _size.x());
  DJ_ASSERT(y >= 0 && y < _size.y());

  //DJ_DBG_FNC("dj::PixelData::data");
  //DJ_DBG_PRN("in = " << x << " " << y);

  return _data + (y * _size.x() + x) * _pixelSize;
}

const uint8_t * PixelData::data(const Vec2i & in) const
{
  DJ_ASSERT(in.x() >= 0 && in.x() < _size.x());
  DJ_ASSERT(in.y() >= 0 && in.y() < _size.y());

  //DJ_DBG_FNC("dj::PixelData::data");
  //DJ_DBG_PRN("in = " << in);

  return _data + (in.y() * _size.x() + in.x()) * _pixelSize;
}

const uint8_t * PixelData::data(int x, int y) const
{
  DJ_ASSERT(x >= 0 && x < _size.x());
  DJ_ASSERT(y >= 0 && y < _size.y());

  //DJ_DBG_FNC("dj::PixelData::data");
  //DJ_DBG_PRN("in = " << x << " " << y);

  return _data + (y * _size.x() + x) * _pixelSize;
}

void PixelData::del()
{
  if (_data) {
    delete [] _data;
    _data = 0;
  }
}

int pixelSize(const PixelDataInfo & in)
{
  return pixelSize(in.pixel());
}

int scanlineSize(const PixelDataInfo & in)
{
  return in.width() * pixelSize(in);
}

int planeSize(const PixelDataInfo & in)
{
  return in.width() * in.height() * pixelSize(in.type());
}

int imageSize(const PixelDataInfo & in)
{
  return in.height() * scanlineSize(in);
}

void pixelCopy(const PixelData & in, PixelData * out, const Vec2i & position)
{
  DJ_ASSERT(out);
  DJ_DBG_FNC("dj::pixelCopy");
  DJ_DBG_PRN("in = " << in);
  DJ_DBG_PRN("out = " << *out);
  DJ_DBG_PRN("position = " << position);
  
  // Setup.
  //
  const Box2i ix = intersect(Box2i(out->size()), Box2i(position, in.size()));
  DJ_DBG_PRN("intersect = " << ix);
  if (! areaIsValid(ix)) return;

  // Work.
  //
  const int px = position.x(), py = position.y();
  const int x0 = ix.x0(), y0 = ix.y0(), y1 = ix.y1();
  const int w = ix.width();
  for (int y = y0; y <= y1; ++y)
    pixelCopy(
      in.data(x0 - px, y - py), out->data(x0, y),
      w, in.pixel(), out->pixel()
    );
}

Dbg & operator << (Dbg & dbg, const PixelData & in)
  { return dbg << in.size() << " " << in.pixel(); }

}
