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

namespace dj {

inline int pixelSize(PIXEL_TYPE in)
{
  static int tmp [] = { 1, 2, 4 };
  return tmp[in];
}

inline int pixelSize(PIXEL_FORMAT in)
{
  static int tmp [] = { 1, 2, 3, 4, 3, 4 };
  return tmp[in];
}

inline int pixelSize(const Pixel & in)
{
  return pixelSize(in.format()) * pixelSize(in.type());
}

inline Pixel::Pixel() :
  _format(PIXEL_FORMAT(0)), _type(PIXEL_TYPE(0))
{}

inline Pixel::Pixel(PIXEL_FORMAT format, PIXEL_TYPE type)
{
  set(format, type);
}

inline void Pixel::set(PIXEL_FORMAT format, PIXEL_TYPE type)
{
  _format = format;
  _type = type;
}

inline void Pixel::format(PIXEL_FORMAT in)
{
  _format = in;
}

inline void Pixel::type(PIXEL_TYPE in)
{
  _type = in;
}

inline bool operator == (const Pixel & a, const Pixel & b)
  { return a.format() == b.format() && a.type() == b.type(); }
inline bool operator != (const Pixel & a, const Pixel & b)
  { return ! (a == b); }

inline PixelU16 pixelU8ToU16(PixelU8 in)
{
  // XXX Shifting doesn't preserve max values.
  //
  //return in << 8;

  static PixelU16 lut [pixelU8Max + 1];
  static bool init = true;
  if (init) {
    for (int i = 0; i <= pixelU8Max; ++i)
      lut[i] = PixelU16(i / float(pixelU8Max) * pixelU16Max);
    init = false;
  }
  return lut[in];
}

inline PixelF32 pixelU8ToF32(PixelU8 in)
{
  //return in / PixelF32(pixelU8Max);
  
  static PixelF32 lut [pixelU8Max + 1];
  static bool init = true;
  if (init) {
    for (int i = 0; i <= pixelU8Max; ++i)
      lut[i] = i / PixelF32(pixelU8Max);
    init = false;
  }
  return lut[in];
}

inline PixelU8 pixelU16ToU8(PixelU16 in)
{
  return in >> 8;
}

inline PixelF32 pixelU16ToF32(PixelU16 in)
{
  //return in / PixelF32(pixelU16Max);
  
  static PixelF32 lut [pixelU16Max + 1];
  static bool init = true;
  if (init) {
    for (int i = 0; i <= pixelU16Max; ++i)
      lut[i] = i / PixelF32(pixelU16Max);
    init = false;
  }
  return lut[in];
}

inline PixelU8 pixelF32ToU8(PixelF32 in)
{
  //DJ_DBG_FNC("dj::pixelF32ToU8");
  //DJ_DBG_PRN("in = " << in);
  
  return PixelU8(clamp(in, 0.0f, 1.0f) * pixelU8Max);

  /*// XXX Ill floating point conversion. Assumes IEEE floats: sign (1 bit)
  // + exponent (8 bits) + mantissa (23 bits).
  //
  // References:
  //
  // * http://www.stereopsis.com/FPU.html

  // Transform to get a constant exponent of 127 for 0.0 to 1.0 range.
  //
  const PixelF32 tmp = in + 1.0f;
  const uint32_t bits = *(const uint32_t *)&tmp;
  //DJ_DBG_PRN(_bits(tmp));
  //DJ_DBG_PRN(_bits(bits));

  // Bail on negative numbers.
  //
  if (bits >> 31) return 0;

  // Exponent:
  //
  // * < 127 - Input <= 0.0.
  // * 127 - Input between 0.0 and 1.0. Mantissa becomes 23 bit integer.
  // * > 127 - Input >= 1.0.
  //
  const uint8_t exp = (bits >> 23) & 0xff;
  //DJ_DBG_PRN("exp = " << exp);
  if (127 == exp) return (bits >> 15) & 0xff;
  else if (exp > 127) return pixelU8Max;
  else return 0;*/
}

inline PixelU16 pixelF32ToU16(PixelF32 in)
{
  return PixelU16(clamp(in, 0.0f, 1.0f) * pixelU16Max);

  /*// XXX See above.
  //
  const PixelF32 tmp = in + 1.0f;
  const uint32_t bits = *(const uint32_t *)&tmp;
  if (bits >> 31) return 0;
  const uint8_t exp = (bits >> 23) & 0xff;
  if (127 == exp) return (bits >> 7) & 0xffff;
  else if (exp > 127) return pixelU16Max;
  else return 0;*/
}

inline void rgbToHsv(const double in [3], double out [3])
{
  const double & _min = min(in[0], min(in[1], in[2]));
  const double & _max = max(in[0], max(in[1], in[2]));
  out[2] = _max;
  out[1] = (! compare(_max, 0.0)) ? (1.0 - _min / _max) : 0.0;
  const double v = (_max - _min) * 6.0;
  if (_max == _min) {
    out[0] = 0.0;
  } else if (in[0] == _max) {
    if (in[1] >= in[2]) out[0] = (in[1] - in[2]) / v;
    else out[0] = 1.0 + (in[1] - in[2]) / v;
  } else if (in[1] == _max) {
    out[0] = 1.0 / 3.0 + (in[2] - in[0]) / v;
  } else if (in[2] == _max) {
    out[0] = 2.0 / 3.0 + (in[0] - in[1]) / v;
  }
}

inline void hsvToRgb(const double in [3], double out [3])
{
  const double h = mod(in[0] * 6.0, 6.0);
  const int i = floor(h);
  const double f = h - i;
  const double p = in[2] * (1.0 - in[1]);
  const double q = in[2] * (1.0 - (in[1] * f));
  const double t = in[2] * (1.0 - (in[1] * (1.0 - f)));
  switch (i) {
    case 0: out[0] = in[2]; out[1] = t; out[2] = p; break;
    case 1: out[0] = q; out[1] = in[2]; out[2] = p; break;
    case 2: out[0] = p; out[1] = in[2]; out[2] = t; break;
    case 3: out[0] = p; out[1] = q; out[2] = in[2]; break;
    case 4: out[0] = t; out[1] = p; out[2] = in[2]; break;
    case 5: out[0] = in[2]; out[1] = p; out[2] = q; break;
  }
}

}
