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

//#define DJ_DBG
#include "djImageOpGen.h"

namespace dj {

ImageOpGradient::ImageOpGradient()
{
  _size = Vec2i(500, 500);
  _pixel = Pixel(PIXEL_RGB, PIXEL_U8);
  _position = Vec2i(0, 0);
  _position2 = Vec2i(499, 0);
  _color = Color(0.0);
  _color2 = Color(1.0);
}
  
const StrList ImageOpGradient::attrNameList() const
  { return imageOpGradientAttrStr(); }

bool ImageOpGradient::attr(const Str & in, const StrList & data)
{
  StrList tmp = data;
  if (compareCase(attrNameList()[ATTR_SIZE], in)) {
    if (! inflateStr(tmp, &_size)) return false;
  } else if (compareCase(attrNameList()[ATTR_PIXEL], in)) {
    if (! inflateStr(tmp, &_pixel)) return false;
  } else if (compareCase(attrNameList()[ATTR_POSITION], in)) {
    if (! inflateStr(tmp, &_position)) return false;
  } else if (compareCase(attrNameList()[ATTR_POSITION_2], in)) {
    if (! inflateStr(tmp, &_position2)) return false;
  } else if (compareCase(attrNameList()[ATTR_COLOR], in)) {
    if (! inflateStr(tmp, &_color)) return false;
  } else if (compareCase(attrNameList()[ATTR_COLOR_2], in)) {
    if (! inflateStr(tmp, &_color2)) return false;
  } else 
    return false;
  return true;
}

const StrList ImageOpGradient::attr(const Str & in) const
{
  StrList out;
  if (compareCase(attrNameList()[ATTR_SIZE], in))
    out += deflateStr(_size);
  else if (compareCase(attrNameList()[ATTR_PIXEL], in))
    out += deflateStr(_pixel);
  else if (compareCase(attrNameList()[ATTR_POSITION], in))
    out += deflateStr(_position);
  else if (compareCase(attrNameList()[ATTR_POSITION_2], in))
    out += deflateStr(_position2);
  else if (compareCase(attrNameList()[ATTR_COLOR], in)) out +=
    deflateStr(_color);
  else if (compareCase(attrNameList()[ATTR_COLOR_2], in))
    out += deflateStr(_color2);
  return out;
}

const PixelDataInfo ImageOpGradient::info(const InfoList &) const
{
  return PixelDataInfo(_size, _pixel);
}

const Image ImageOpGradient::work(const InputList &) const
{
  return work(_size, _pixel, _position, _position2, _color, _color2);
}

const Image ImageOpGradient::work(
  const Vec2i & size, const Pixel & pixel,
  const Vec2i & position, const Vec2i & position2,
  const Color & color, const Color & color2
) {
  DJ_DBG_FNC("dj::ImageOpGradient::work");
  DJ_DBG_PRN("size = " << size);
  DJ_DBG_PRN("pixel = " << pixel);
  DJ_DBG_PRN("position = " << position);
  DJ_DBG_PRN("position 2 = " << position2);
  DJ_DBG_PRN("color = " << color);
  DJ_DBG_PRN("color 2 = " << color2);
  
  // Setup.
  //
  Image out(size, pixel);
  const Vec2i a = position2 - position;
  const float length = dj::length(a);
  Color tmp, start, end;
  tmp.set(out.format(), PIXEL_F32);
  start.set(out.format(), PIXEL_F32);
  end.set(out.format(), PIXEL_F32);
  convert(color, &start);
  convert(color2, &end);
  const bool horizontal = position.y() == position2.y();
  DJ_DBG_PRN("horizontal = " << horizontal);

  // Work.
  //
  PixelF32 * tmpP = reinterpret_cast<PixelF32 *>(tmp.data());
  PixelF32 * startP = reinterpret_cast<PixelF32 *>(start.data());
  PixelF32 * endP = reinterpret_cast<PixelF32 *>(end.data());
  const int w = out.width(), h = out.height();
  const int channels = pixelSize(out.format());
  for (int y = 0; y < h; ++y)
    if (horizontal ? (0 == y) : 1) {
      for (int x = 0; x < w; ++x) {
      
        // Gradient position.
        //
        float v = 0.0f;
        if (! isZero(length)) {
          const Vec2i b = Vec2i(x, y) - position;
          const float projection = dot(a, b) / length;
          v = clamp(projection / length, 0.0f, 1.0f);
        }

        // Interpolate pixel.
        //
        for (int c = 0; c < channels; ++c)
          tmpP[c] = lerp(v, startP[c], endP[c]);
        out.pixel(tmp, x, y);
      }
      
    } else
      pixelCopy(out.data(0, 0), out.data(0, y), w, pixel, pixel);
  
  return out;
}

}
