/*------------------------------------------------------------------------------
 src/lib/djWidget/colorWidget.cxx

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

//#define DJ_DBG
#include "djColorWidget.h"
#include <djImageOpGen.h>
//#include <djLabelWidget.h>
#include <djButtonWidget.h>
#include <djEnumWidget.h>
#include <djNumWidget.h>
#include <djSliderWidget.h>
#include <djPixelWidget.h>
#include <FL/fl_draw.H>
#include <FL/Fl_Image.H>

namespace dj {

class _SliderColorWidget {
public:

  _SliderColorWidget();
  
  enum BACKGROUND { L, R, G, B };
  void background(BACKGROUND);
  BACKGROUND background() const { return _background; }
  
protected:

  void drawBackground(const Box2i &);

private:

  BACKGROUND _background;
  Image _image;
};

class _IntSliderColorWidget :
  public IntSliderWidget, public  _SliderColorWidget
{
private:
  void drawBackground(const Box2i & in)
    { _SliderColorWidget::drawBackground(in); }
};
class _FloatSliderColorWidget :
  public FloatSliderWidget, public  _SliderColorWidget
{
private:
  void drawBackground(const Box2i & in)
    { _SliderColorWidget::drawBackground(in); }
};

_SliderColorWidget::_SliderColorWidget() :
  _background(BACKGROUND(0))
{}
  
void _SliderColorWidget::background(BACKGROUND in)
{
  _background = in;
  _image.set(Vec2i(), Pixel());
}

void _SliderColorWidget::drawBackground(const Box2i & in)
{
  const int x = in.x(), y = in.y();
  const int w = in.width(), h = in.height();
  
  if (_image.size() != Vec2i(w, 1)) {

    Color color1(0.0), color2(1.0);
    switch (_background) {
      case L: break;
      case R: color2 = Color(1.0, 0.0, 0.0); break;
      case G: color2 = Color(0.0, 1.0, 0.0); break;
      case B: color2 = Color(0.0, 0.0, 1.0); break;
    }
    
    _image = ImageOpGradient::work(
      Vec2i(w, 1), Pixel(PIXEL_RGB, PIXEL_U8),
      Vec2i(), Vec2i(w - 1, 0),
      color1, color2
    );
  }
  
  Fl_RGB_Image imageFl(_image.data(), _image.width(), _image.height());
  for (int _y = 0; _y < h; ++_y)
    imageFl.draw(x, y + _y);
}

ColorWidget::ColorWidget() :
  _value(PIXEL_RGB, PIXEL_U8),
  _floatMax(1.0),
  _callbacks(true)
{
  // Widgets.
  //
  //List<LabelWidget *> labelGroup;
  for (int i = 0; i < pixelFormatMax; ++i) {
    //_labelWidget[i] = new LabelWidget;
    //labelGroup += _labelWidget[i];
    _intWidget[i] = new IntWidget;
    _intSliderWidget[i] = new _IntSliderColorWidget;
    _floatWidget[i] = new FloatWidget;
    _floatSliderWidget[i] = new _FloatSliderColorWidget;
  }
  //for (int i = 0; i < pixelFormatMax; ++i)
  //  _labelWidget[i]->labelGroup(labelGroup);
  _pixelWidget = new PixelWidget;
  
  // Layout.
  //
  sizePolicy(STRETCH, FIXED);
  VLayout * layout = new VLayout(this);
  layout->margin(0);
  for (int i = 0; i < pixelFormatMax; ++i) {
    HLayout * channelLayout = new HLayout(layout);
    channelLayout->margin(0);
    //channelLayout->add(_labelWidget[i]);
    channelLayout->add(_intWidget[i]);
    channelLayout->add(_intSliderWidget[i]);
    channelLayout->add(_floatWidget[i]);
    channelLayout->add(_floatSliderWidget[i]);
  }
  HLayout * toolLayout = new HLayout(layout);
  toolLayout->margin(0);
  toolLayout->stretchAdd();
  toolLayout->add(_pixelWidget);
  
  // Initialize.
  //
  widgetUpdate();
  valueUpdate();
  
  // Callbacks.
  //
  for (int i = 0; i < pixelFormatMax; ++i) {
    _intWidget[i]->sig().set(this, intCallback);
    _intSliderWidget[i]->sig().set(this, intSliderCallback);
    _floatWidget[i]->sig().set(this, floatCallback);
    _floatSliderWidget[i]->sig().set(this, floatSliderCallback);
  }
  _pixelWidget->sig().set(this, pixelCallback);
}

void ColorWidget::set(const Color & in)
{
  if (in == _value) return;
  
  DJ_DBG_FNC("dj::ColorWidget::set");
  DJ_DBG_PRN("in = " << in);
  
  const bool update = in.pixel() != _value.pixel();
  _value = in;
  _sig.emit(_value);
  if (update) widgetUpdate();
  valueUpdate();
}

void ColorWidget::floatMax(double in)
{
  if (in == _floatMax) return;
  
  DJ_DBG_FNC("dj::ColorWidget::floatMax");
  DJ_DBG_PRN("in = " << in);
  
  _floatMax = in;
  widgetUpdate();
  valueUpdate();
}

void ColorWidget::intCallback(int)
{
  if (! _callbacks) return;
  for (int i = 0; i < pixelSize(_value.format()); ++i)
    switch (_value.type()) {
      case PIXEL_U8: _value.u8(_intWidget[i]->get(), i); break;
      case PIXEL_U16: _value.u16(_intWidget[i]->get(), i); break;
      case PIXEL_F32: _value.f32(_floatWidget[i]->get(), i); break;
    }
  _sig.emit(_value);
  valueUpdate();
}

void ColorWidget::intSliderCallback(int)
{
  if (! _callbacks) return;
  for (int i = 0; i < pixelSize(_value.format()); ++i)
    switch (_value.type()) {
      case PIXEL_U8: _value.u8(_intSliderWidget[i]->get(), i); break;
      case PIXEL_U16: _value.u16(_intSliderWidget[i]->get(), i); break;
      case PIXEL_F32: _value.f32(_floatSliderWidget[i]->get(), i); break;
    }
  _sig.emit(_value);
  valueUpdate();
}

void ColorWidget::floatCallback(double) { intCallback(0); }
void ColorWidget::floatSliderCallback(double) { intSliderCallback(0); }

void ColorWidget::pixelCallback(const Pixel & in)
{
  if (! _callbacks) return;
  Color tmp(in);
  convert(_value, &tmp);
  set(tmp);
}

void ColorWidget::widgetUpdate()
{
  //DJ_DBG_FNC("dj::ColorWidget::widgetUpdate");
  //DJ_DBG_PRN("value = " << _value);
  
  _callbacks = false;
  
  const int channels = pixelSize(_value.format());

  // Value range.
  //
  double range [] = { 0.0, 1.0 };
  switch (_value.type()) {
    case PIXEL_U8:
      range[0] = pixelU8Min;
      range[1] = pixelU8Max;
      break;
    case PIXEL_U16:
      range[0] = pixelU16Min;
      range[1] = pixelU16Max;
      break;
    case PIXEL_F32: break;
  }
  double floatMax [pixelFormatMax];
  for (int i = 0; i < channels; ++i)
    floatMax[i] = _floatMax;
  
  // Slider background.
  //
  _SliderColorWidget::BACKGROUND background [pixelFormatMax];
  switch (_value.format()) {
    case PIXEL_L:
      background[0] = _SliderColorWidget::L;
      break;
    case PIXEL_LA:
      background[0] = _SliderColorWidget::L;
      background[1] = _SliderColorWidget::L;
      break;
    case PIXEL_RGB:
      background[0] = _SliderColorWidget::R;
      background[1] = _SliderColorWidget::G;
      background[2] = _SliderColorWidget::B;
      break;
    case PIXEL_RGBA:
      background[0] = _SliderColorWidget::R;
      background[1] = _SliderColorWidget::G;
      background[2] = _SliderColorWidget::B;
      background[3] = _SliderColorWidget::L;
      break;
  }
  
  // Labels/tooltips.
  //
  //static const Str labels0 [] = {
  //  "L", "A"
  //};
  static const Str tooltips0 [] = {
    "Luminance channel.",
    "Alpha channel."
  };
  //static const Str labels1 [] = {
  //  "R", "G", "B", "A"
  //};
  static const Str tooltips1 [] = {
    "Red channel.",
    "Green channel.",
    "Blue channel.",
    "Alpha channel."
  };
  //const Str * labels [pixelFormatMax];
  const Str * tooltips [pixelFormatMax];
  switch (_value.format()) {
    case PIXEL_L:
    case PIXEL_LA:
      for (int i = 0; i < channels; ++i) {
        //labels[i] = &labels0[i];
        tooltips[i] = &tooltips0[i];
      }
      break;
    case PIXEL_RGB:
    case PIXEL_RGBA: {
      for (int i = 0; i < channels; ++i) {
        //labels[i] = &labels1[i];
        tooltips[i] = &tooltips1[i];
      }
    } break;
  }
  
  // Set.
  //
  for (int i = 0; i < channels; ++i) {
  
    _intWidget[i]->range(int(range[0]), int(range[1]));
    _intSliderWidget[i]->range(int(range[0]), int(range[1]));
    //_floatWidget[i]->range(range[0], floatMax[i]);
    _floatSliderWidget[i]->range(range[0], floatMax[i]);
    
    _intSliderWidget[i]->background(background[i]);
    _floatSliderWidget[i]->background(background[i]);
    
    //_labelWidget[i]->set(*labels[i]);
    _intWidget[i]->tooltip(*tooltips[i]);
    _intSliderWidget[i]->tooltip(*tooltips[i]);
    _floatWidget[i]->tooltip(*tooltips[i]);
    _floatSliderWidget[i]->tooltip(*tooltips[i]);
  }
  
  // Show/hide.
  //
  for (int i = 0; i < pixelFormatMax; ++i) {
    //if (i < channels) _labelWidget[i]->show();
    //else _labelWidget[i]->hide();
    switch (_value.type()) {
      case PIXEL_U8:
      case PIXEL_U16:
        if (i < channels) {
          _intWidget[i]->show();
          _intSliderWidget[i]->show();
        } else {
          _intWidget[i]->hide();
          _intSliderWidget[i]->hide();
        }
        _floatWidget[i]->hide();
        _floatSliderWidget[i]->hide();
        break;
      case PIXEL_F32:
        _intWidget[i]->hide();
        _intSliderWidget[i]->hide();
        if (i < channels) {
          _floatWidget[i]->show();
          _floatSliderWidget[i]->show();
        } else {
          _floatWidget[i]->hide();
          _floatSliderWidget[i]->hide();
        }
        break;
    }
  }
  
  _callbacks = true;
  layoutUpdate();
}

void ColorWidget::valueUpdate()
{
  //DJ_DBG_FNC("dj::ColorWidget::valueUpdate");
  //DJ_DBG_PRN("value = " << _value);
  
  _callbacks = false;
  
  for (int i = 0; i < pixelSize(_value.format()); ++i) {
    switch (_value.type()) {
      case PIXEL_U8:
        _intWidget[i]->set(_value.u8(i));
        _intSliderWidget[i]->set(_value.u8(i));
        break;
      case PIXEL_U16:
        _intWidget[i]->set(_value.u16(i));
        _intSliderWidget[i]->set(_value.u16(i));
        break;
      case PIXEL_F32:
        _floatWidget[i]->set(_value.f32(i));
        _floatSliderWidget[i]->set(_value.f32(i));
        break;
    }
  }
  
  _pixelWidget->set(_value.pixel());
  
  _callbacks = true;
}

}
