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

//#define DJ_DBG
#include "djColor.h"
#include <djMath.h>

namespace dj {

Color::Color() :
  PixelData(Vec2i(1), Pixel())
{
  zero();
}

Color::Color(const Pixel & pixel) :
  PixelData(Vec2i(1), pixel)
{
  zero();
}

Color::Color(PIXEL_FORMAT format, PIXEL_TYPE type) :
  PixelData(Vec2i(1), Pixel(format, type))
{
  zero();
}

Color::Color(PixelF32 l) :
  PixelData(Vec2i(1), Pixel(PIXEL_L, PIXEL_F32))
{
  f32(l, 0);
}

Color::Color(PixelF32 l, PixelF32 a) :
  PixelData(Vec2i(1), Pixel(PIXEL_LA, PIXEL_F32))
{
  f32(l, 0);
  f32(a, 1);
}

Color::Color(PixelF32 r, PixelF32 g, PixelF32 b) :
  PixelData(Vec2i(1), Pixel(PIXEL_RGB, PIXEL_F32))
{
  f32(r, 0);
  f32(g, 1);
  f32(b, 2);
}

Color::Color(PixelF32 r, PixelF32 g, PixelF32 b, PixelF32 a) :
  PixelData(Vec2i(1), Pixel(PIXEL_RGBA, PIXEL_F32))
{
  f32(r, 0);
  f32(g, 1);
  f32(b, 2);
  f32(a, 3);
}

void Color::set(const Pixel & in)
{
  Color tmp(in);
  convert(*this, &tmp);
  *this = tmp;
}

void Color::set(PIXEL_FORMAT format, PIXEL_TYPE type)
{
  Color tmp(format, type);
  convert(*this, &tmp);
  *this = tmp;
}

void Color::set(const Vec2i &, const Pixel & pixel)
{
  PixelData::set(Vec2i(1), pixel);
}

void Color::u8(PixelU8 in, int channel)
{
  DJ_ASSERT(channel >= 0 && channel < pixelSize(format()));
  uint8_t * p = data();
  DJ_ASSERT(p);
  switch (type()) {
    case PIXEL_U8:
      (reinterpret_cast<PixelU8 *>(p))[channel] = in;
      break;
    case PIXEL_U16:
      (reinterpret_cast<PixelU16 *>(p))[channel] = pixelU8ToU16(in);
      break;
    case PIXEL_F32:
      (reinterpret_cast<PixelF32 *>(p))[channel] = pixelU8ToF32(in);
      break;
  }
}

void Color::u16(PixelU16 in, int channel)
{
  DJ_ASSERT(channel >= 0 && channel < pixelSize(format()));
  uint8_t * p = data();
  DJ_ASSERT(p);
  switch (type()) {
    case PIXEL_U8:
      (reinterpret_cast<PixelU8 *>(p))[channel] = pixelU16ToU8(in);
      break;
    case PIXEL_U16:
      (reinterpret_cast<PixelU16 *>(p))[channel] = in;
      break;
    case PIXEL_F32:
      (reinterpret_cast<PixelF32 *>(p))[channel] = pixelU16ToF32(in);
      break;
  }
}

void Color::f32(PixelF32 in, int channel)
{
  DJ_ASSERT(channel >= 0 && channel < pixelSize(format()));
  uint8_t * p = data();
  DJ_ASSERT(p);
  switch (type()) {
    case PIXEL_U8:
      (reinterpret_cast<PixelU8 *>(p))[channel] = pixelF32ToU8(in);
      break;
    case PIXEL_U16:
      (reinterpret_cast<PixelU16 *>(p))[channel] = pixelF32ToU16(in);
      break;
    case PIXEL_F32:
      (reinterpret_cast<PixelF32 *>(p))[channel] = in;
      break;
  }
}

PixelU8 Color::u8(int channel) const
{
  DJ_ASSERT(channel >= 0 && channel < pixelSize(format()));
  const uint8_t * const p = data();
  DJ_ASSERT(p);
  switch (type()) {
    case PIXEL_U8:
      return
        (reinterpret_cast<const PixelU8 *>(p))[channel];
    case PIXEL_U16:
      return
        pixelU16ToU8((reinterpret_cast<const PixelU16 *>(p))[channel]);
    case PIXEL_F32:
      return
        pixelF32ToU8((reinterpret_cast<const PixelF32 *>(p))[channel]);
  }
  return 0;
}

PixelU16 Color::u16(int channel) const
{
  DJ_ASSERT(channel >= 0 && channel < pixelSize(format()));
  const uint8_t * const p = data();
  DJ_ASSERT(p);
  switch (type()) {
    case PIXEL_U8:
      return
        pixelU8ToU16((reinterpret_cast<const PixelU8 *>(p))[channel]);
    case PIXEL_U16:
      return
        (reinterpret_cast<const PixelU16 *>(p))[channel];
    case PIXEL_F32:
      return
        pixelF32ToU16((reinterpret_cast<const PixelF32 *>(p))[channel]);
  }
  return 0;
}

PixelF32 Color::f32(int channel) const
{
  DJ_ASSERT(channel >= 0 && channel < pixelSize(format()));
  const uint8_t * const p = data();
  DJ_ASSERT(p);
  switch (type()) {
    case PIXEL_U8:
      return
        pixelU8ToF32((reinterpret_cast<const PixelU8 *>(p))[channel]);
    case PIXEL_U16:
      return
        pixelU16ToF32((reinterpret_cast<const PixelU16 *>(p))[channel]);
    case PIXEL_F32:
      return
        (reinterpret_cast<const PixelF32 *>(p))[channel];
  }
  return 0.0;
}

bool Color::operator == (const Color & in) const
{
  if (in.pixel() != pixel()) return false;
  return memCompare(in.data(), data(), imageSize(*this)) == 0;
}

bool Color::operator != (const Color & in) const
  { return ! (in == *this); }

void convert(const Color & in, Color * out)
{
  pixelCopy(in.data(), out->data(), 1, in.pixel(), out->pixel());
}

const StrList deflateStr(const Color & in)
{
  StrList out;
  out += deflateStr(in.pixel());
  const int channels = pixelSize(in.format());
  switch (in.type()) {
    case PIXEL_U8:
      for (int i = 0; i < channels; ++i)
        out += deflateStr(int(in.u8(i)));
      break;
    case PIXEL_U16:
      for (int i = 0; i < channels; ++i)
        out += deflateStr(int(in.u16(i)));
      break;
    case PIXEL_F32:
      for (int i = 0; i < channels; ++i)
        out += deflateStr(double(in.f32(i)));
      break;
  }
  return out;
}

bool inflateStr(StrList & in, Color * out)
{
  DJ_ASSERT(out);
  
  Pixel pixel;
  if (! inflateStr(in, &pixel)) return false;
  const int channels = pixelSize(pixel.format());
  if (in.size() < channels) return false;
  out->set(pixel);
  switch (pixel.type()) {
    case PIXEL_U8:
      for (int i = 0; i < channels; ++i)
        out->u8(clamp(toInt(in.pop()), pixelU8Min, pixelU8Max), i);
      break;
    case PIXEL_U16:
      for (int i = 0; i < channels; ++i)
        out->u16(clamp(toInt(in.pop()), pixelU16Min, pixelU16Max), i);
      break;
    case PIXEL_F32:
      for (int i = 0; i < channels; ++i)
        out->f32(toFloat(in.pop()), i);
      break;
  }
  
  return true;
}

}
