// This may look like C code, but it is really -*- C++ -*-
//
// Copyright Bob Friesenhahn, 1999, 2000, 2001, 2002, 2003
// Copyright Dirk Lemstra 2013-2018
//
// Color Implementation
//

#define MAGICKCORE_IMPLEMENTATION
#define MAGICK_PLUSPLUS_IMPLEMENTATION 1

#include "Magick++/Include.h"
#include <string>

#include "Magick++/Color.h"
#include "Magick++/Exception.h"

using namespace std;

MagickPPExport int Magick::operator == (const Magick::Color &left_,
  const Magick::Color &right_)
{
#if defined(MAGICKCORE_HDRI_SUPPORT)
  return((left_.isValid() == right_.isValid()) &&
    (fabs(left_.quantumRed()-right_.quantumRed()) < MagickEpsilon) &&
    (fabs(left_.quantumGreen()-right_.quantumGreen()) < MagickEpsilon) &&
    (fabs(left_.quantumBlue()-right_.quantumBlue()) < MagickEpsilon));
#else
  return((left_.isValid() == right_.isValid()) &&
    (left_.quantumRed() == right_.quantumRed()) &&
    (left_.quantumGreen() == right_.quantumGreen()) &&
    (left_.quantumBlue() == right_.quantumBlue()));
#endif
}

MagickPPExport int Magick::operator != (const Magick::Color &left_,
  const Magick::Color &right_)
{
  return(!(left_ == right_));
}

MagickPPExport int Magick::operator > (const Magick::Color &left_,
  const Magick::Color &right_)
{
  return(!(left_ < right_ ) && (left_ != right_ ));
}

MagickPPExport int Magick::operator < ( const Magick::Color &left_,
  const Magick::Color &right_)
{
  if(left_.quantumRed() < right_.quantumRed())
    return(true);
  if(left_.quantumRed() > right_.quantumRed())
    return(false);
  if(left_.quantumGreen() < right_.quantumGreen())
    return(true);
  if(left_.quantumGreen() > right_.quantumGreen())
    return(false);
  if(left_.quantumBlue() < right_.quantumBlue())
    return(true);
  return(false);
}

MagickPPExport int Magick::operator >= (const Magick::Color &left_,
  const Magick::Color &right_)
{
  return((left_ > right_) || (left_ == right_));
}

MagickPPExport int Magick::operator <= ( const Magick::Color &left_,
  const Magick::Color &right_)
{
  return((left_ < right_) || (left_ == right_));
}

Magick::Color::Color(void)
  : _pixel(new PixelInfo),
    _isValid(false),
    _pixelOwn(true),
    _pixelType(RGBAPixel)
{
  initPixel();

  setAlpha(TransparentAlpha);
}

Magick::Color::Color(const Magick::Quantum red_,const Magick::Quantum green_,
  const Quantum blue_)
  : _pixel(new PixelInfo),
    _isValid(true),
    _pixelOwn(true),
    _pixelType(RGBPixel)
{
  initPixel();

  quantumAlpha(OpaqueAlpha);
  quantumBlack(0);
  quantumBlue(blue_);
  quantumGreen(green_);
  quantumRed(red_);
}

Magick::Color::Color(const Magick::Quantum red_,const Magick::Quantum green_,
  const Magick::Quantum blue_, const Magick::Quantum alpha_)
  : _pixel(new PixelInfo),
    _isValid(true),
    _pixelOwn(true),
    _pixelType(RGBPixel)
{
  initPixel();

  quantumAlpha(alpha_);
  quantumBlack(0);
  quantumBlue(blue_);
  quantumGreen(green_);
  quantumRed(red_);
  if (alpha_ != OpaqueAlpha)
    _pixelType=RGBAPixel;
}

Magick::Color::Color(const Magick::Quantum cyan_,const Magick::Quantum magenta_,
  const Magick::Quantum yellow_,const Magick::Quantum black_,
  const Magick::Quantum alpha_)
  : _pixel(new PixelInfo),
    _isValid(true),
    _pixelOwn(true),
    _pixelType(CMYKPixel)
{
  initPixel();

  quantumAlpha(alpha_);
  quantumBlack(black_);
  quantumBlue(yellow_);
  quantumGreen(magenta_);
  quantumRed(cyan_);
  if (alpha_ != OpaqueAlpha)
    _pixelType=CMYKAPixel;
}

Magick::Color::Color(const char *color_)
  : _pixel(new PixelInfo),
    _isValid(true),
    _pixelOwn(true),
    _pixelType(RGBPixel)
{
  initPixel();

  // Use operator = implementation
  *this=color_;
}

Magick::Color::Color(const Magick::Color &color_)
  : _pixel(new PixelInfo),
    _isValid(color_._isValid),
    _pixelOwn(true),
    _pixelType(color_._pixelType)
{
  *_pixel=*color_._pixel;
}

Magick::Color::Color(const PixelInfo &color_)
  : _pixel(new PixelInfo),
    _isValid(true),
    _pixelOwn(true)
{
  *_pixel=color_;
  setPixelType(color_);
}

Magick::Color::Color(const std::string &color_)
  : _pixel(new PixelInfo),
    _isValid(true),
    _pixelOwn(true),
    _pixelType(RGBPixel)
{
  initPixel();

  // Use operator = implementation
  *this=color_;
}

Magick::Color::~Color(void)
{
  if (_pixelOwn)
    delete _pixel;

  _pixel=(PixelInfo *)NULL;
}

Magick::Color& Magick::Color::operator=(const Magick::Color& color_)
{
  // If not being set to ourself
  if (this != &color_)
    {
      // Copy pixel value
      *_pixel=*color_._pixel;

      // Validity
      _isValid=color_._isValid;

      // Copy pixel type
      _pixelType=color_._pixelType;
    }
  return(*this);
}

const Magick::Color& Magick::Color::operator=(const char *color_)
{
  *this=std::string(color_);
  return(*this);
}

const Magick::Color& Magick::Color::operator=(const MagickCore::PixelInfo &color_)
{
  *_pixel=color_;
  setPixelType(color_);

  return(*this);
}

const Magick::Color& Magick::Color::operator=(const std::string &color_)
{
  PixelInfo
    target_color;

  initPixel();
  GetPPException;
  if (QueryColorCompliance(color_.c_str(),AllCompliance,&target_color,
      exceptionInfo))
    {
      quantumAlpha(target_color.alpha);
      quantumBlack(target_color.black);
      quantumBlue(target_color.blue);
      quantumGreen(target_color.green);
      quantumRed(target_color.red);

      setPixelType(target_color);
    }
  else
    _isValid = false;
  ThrowPPException(false);

  return(*this);
}

Magick::Color::operator MagickCore::PixelInfo() const
{
  return *_pixel;
}

Magick::Color::operator std::string() const
{
  char
    colorbuf[MagickPathExtent];

  PixelInfo
    pixel;

  if (!isValid())
    return std::string("none");

  pixel.colorspace=(_pixelType == RGBPixel || _pixelType == RGBAPixel) ?
    sRGBColorspace : CMYKColorspace;
  pixel.depth=MAGICKCORE_QUANTUM_DEPTH;
  pixel.alpha=_pixel->alpha;
  pixel.alpha_trait=_pixel->alpha_trait;
  pixel.black=_pixel->black;
  pixel.blue=_pixel->blue;
  pixel.green=_pixel->green;
  pixel.red=_pixel->red;
  GetColorTuple(&pixel,MagickTrue,colorbuf);

  return(std::string(colorbuf));
}

bool Magick::Color::isFuzzyEquivalent(const Color &color_, const double fuzz_) const
{
  PixelInfo
    p,
    q;

  p=*_pixel;
  p.fuzz=fuzz_;
  q=*color_._pixel;
  q.fuzz=fuzz_;
  return (IsFuzzyEquivalencePixelInfo(&p, &q) != MagickFalse);
}

bool Magick::Color::isValid(void) const
{
  return(_isValid);
}

Magick::Color::PixelType Magick::Color::pixelType() const
{
  return(_pixelType);
}

void Magick::Color::isValid(bool valid_)
{
  if (bool(valid_) == bool(isValid()))
    return;

  if (!_pixelOwn)
    {
      _pixel=new PixelInfo;
      _pixelOwn=true;
    }

  _isValid=valid_;

  initPixel();
}

void Magick::Color::quantumAlpha(const Magick::Quantum alpha_)
{
  setAlpha(alpha_);
  _isValid=true;
}

Magick::Quantum Magick::Color::quantumAlpha(void) const
{
  return(_pixel->alpha);
}

void Magick::Color::quantumBlack(const Magick::Quantum black_)
{
  _pixel->black=black_;
  _isValid=true;
}

Magick::Quantum Magick::Color::quantumBlack(void) const
{
  return(_pixel->black);
}

void Magick::Color::quantumBlue(const Magick::Quantum blue_)
{
  _pixel->blue=blue_;
  _isValid=true;
}

Magick::Quantum Magick::Color::quantumBlue(void) const
{
  return(_pixel->blue);
}

void Magick::Color::quantumGreen(const Magick::Quantum green_)
{
  _pixel->green=green_;
  _isValid=true;
}

Magick::Quantum Magick::Color::quantumGreen(void) const
{
  return(_pixel->green);
}

void Magick::Color::quantumRed(const Magick::Quantum red_)
{
  _pixel->red=red_;
  _isValid=true;
}

Magick::Quantum Magick::Color::quantumRed(void) const
{
  return _pixel->red;
}

Magick::Color::Color(PixelType pixelType_)
  : _pixel(new PixelInfo),
    _isValid(false),
    _pixelOwn(true),
    _pixelType(pixelType_)
{
  initPixel();
}

Magick::Color::Color(PixelInfo* rep_,PixelType pixelType_)
  : _pixel(rep_),
    _isValid(true),
    _pixelOwn(false),
    _pixelType(pixelType_)
{
}

void Magick::Color::pixel(PixelInfo *rep_,PixelType pixelType_)
{
  if (_pixelOwn)
    delete _pixel;

  _pixel=rep_;
  _pixelOwn=false;
  _isValid=true;
  _pixelType=pixelType_;
}

Magick::Quantum Magick::Color::scaleDoubleToQuantum(const double double_)
{
  return(static_cast<Magick::Quantum>(double_*QuantumRange));
}

double Magick::Color::scaleQuantumToDouble(const Magick::Quantum quantum_)
{
#if (MAGICKCORE_QUANTUM_DEPTH < 32) && (MAGICKCORE_SIZEOF_FLOAT_T != MAGICKCORE_SIZEOF_DOUBLE || !defined(MAGICKCORE_HDRI_SUPPORT))
  return(static_cast<double>(QuantumScale*quantum_));
#else
  return(QuantumScale*quantum_);
#endif
}

void Magick::Color::initPixel()
{
  MagickCore::GetPixelInfo((MagickCore::Image *) NULL, _pixel);
  if (_pixelType == CMYKPixel || _pixelType == CMYKAPixel)
    _pixel->colorspace=CMYKColorspace;
}

void Magick::Color::setAlpha(const Magick::Quantum alpha_)
{
  _pixel->alpha=alpha_;
  if (alpha_ == OpaqueAlpha)
    {
      _pixel->alpha_trait=UndefinedPixelTrait;
      if (_pixelType == RGBAPixel)
        _pixelType=RGBPixel;
      else if (_pixelType == CMYKAPixel)
        _pixelType=CMYKPixel;
    }
  else
    {
      _pixel->alpha_trait=BlendPixelTrait;
      if (_pixelType == RGBPixel)
        _pixelType=RGBAPixel;
      else if (_pixelType == CMYKPixel)
        _pixelType=CMYKAPixel;
    }
}

void Magick::Color::setPixelType(const PixelInfo &color_)
{
  if (color_.colorspace == CMYKColorspace)
    _pixelType=color_.alpha_trait != UndefinedPixelTrait ? CMYKAPixel :
      CMYKPixel;
  else
    _pixelType=color_.alpha_trait != UndefinedPixelTrait ? RGBAPixel :
      RGBPixel;
}

Magick::ColorCMYK::ColorCMYK(void)
  : Color(CMYKPixel)
{
}

Magick::ColorCMYK::ColorCMYK(const Magick::Color &color_)
  : Color(color_)
{
}

Magick::ColorCMYK::ColorCMYK(const double cyan_,const double magenta_,
  const double yellow_,const double black_)
  : Color(CMYKPixel)
{
  cyan(cyan_);
  magenta(magenta_);
  yellow(yellow_);
  black(black_);
}

Magick::ColorCMYK::ColorCMYK(const double cyan_,const double magenta_,
  const double yellow_,const double black_,const double alpha_)
  : Color(CMYKAPixel)
{
  cyan(cyan_);
  magenta(magenta_);
  yellow(yellow_);
  black(black_);
  alpha(alpha_);
}

Magick::ColorCMYK::~ColorCMYK(void)
{
}

Magick::ColorCMYK& Magick::ColorCMYK::operator=(const Magick::Color& color_)
{
  *static_cast<Magick::Color*>(this)=color_;
  return(*this);
}

void Magick::ColorCMYK::alpha(const double alpha_)
{
  quantumAlpha(scaleDoubleToQuantum(alpha_));
}

double Magick::ColorCMYK::alpha(void) const
{
  return(scaleQuantumToDouble(quantumAlpha()));
}

void Magick::ColorCMYK::black(const double black_)
{
  quantumBlack(scaleDoubleToQuantum(black_));
}

double Magick::ColorCMYK::black(void) const
{
  return(scaleQuantumToDouble(quantumBlack()));
}

void Magick::ColorCMYK::cyan(const double cyan_)
{
  quantumRed(scaleDoubleToQuantum(cyan_));
}

double Magick::ColorCMYK::cyan(void) const
{
  return(scaleQuantumToDouble(quantumRed()));
}

void Magick::ColorCMYK::magenta(const double magenta_)
{
  quantumGreen(scaleDoubleToQuantum(magenta_));
}

double Magick::ColorCMYK::magenta(void) const
{
  return(scaleQuantumToDouble(quantumGreen()));
}

void Magick::ColorCMYK::yellow(const double yellow_)
{
  quantumBlue(scaleDoubleToQuantum(yellow_));
}

double Magick::ColorCMYK::yellow(void) const
{
  return(scaleQuantumToDouble(quantumBlue()));
}

Magick::ColorCMYK::ColorCMYK(PixelInfo *rep_,PixelType pixelType_)
  : Color(rep_,pixelType_)
{
}

Magick::ColorGray::ColorGray(void)
  : Color(RGBPixel)
{
}

Magick::ColorGray::ColorGray(const Magick::Color & color_)
  : Color(color_)
{
}

Magick::ColorGray::ColorGray(double shade_)
  : Color(scaleDoubleToQuantum(shade_),scaleDoubleToQuantum(shade_),
          scaleDoubleToQuantum(shade_))
{
}

Magick::ColorGray::~ColorGray()
{
}

void Magick::ColorGray::shade(double shade_)
{
  Quantum gray=scaleDoubleToQuantum(shade_);
  quantumRed(gray);
  quantumGreen(gray);
  quantumBlue(gray);
}

double Magick::ColorGray::shade(void) const
{
  return(scaleQuantumToDouble(quantumGreen()));
}

Magick::ColorGray& Magick::ColorGray::operator=(const Magick::Color& color_)
{
  *static_cast<Magick::Color*>(this)=color_;
  return(*this);
}

Magick::ColorGray::ColorGray(PixelInfo *rep_,PixelType pixelType_)
: Color(rep_,pixelType_)
{
}

Magick::ColorHSL::ColorHSL(void)
  : Color(RGBPixel)
{
}

Magick::ColorHSL::ColorHSL(const Magick::Color &color_)
  : Color(color_)
{
}

Magick::ColorHSL::ColorHSL(const double hue_,const double saturation_,
  const double lightness_)
  : Color(RGBPixel)
{
  double
    blue,
    green,
    red;

  ConvertHSLToRGB(hue_,saturation_,lightness_,&red,&green,&blue);

  quantumRed(red);
  quantumGreen(green);
  quantumBlue(blue);
}

Magick::ColorHSL::~ColorHSL()
{
}

Magick::ColorHSL& Magick::ColorHSL::operator=(const Magick::Color& color_)
{
  *static_cast<Magick::Color*>(this) = color_;
  return(*this);
}

void Magick::ColorHSL::hue(const double hue_)
{
  double
    hue,
    lightness,
    saturation;

  double
    blue,
    green,
    red;

  ConvertRGBToHSL(quantumRed(),quantumGreen(),quantumBlue(),&hue,&saturation,
    &lightness);

  hue=hue_;

  ConvertHSLToRGB(hue,saturation,lightness,&red,&green,&blue);

  quantumRed(ClampToQuantum(red));
  quantumGreen(ClampToQuantum(green));
  quantumBlue(ClampToQuantum(blue));
}

double Magick::ColorHSL::hue(void) const
{
  double
    hue,
    lightness,
    saturation;

  ConvertRGBToHSL(quantumRed(),quantumGreen(),quantumBlue(),&hue,&saturation,
    &lightness);

  return(hue);
}

void Magick::ColorHSL::lightness (const double lightness_)
{
  double
    hue,
    lightness,
    saturation;

  double
    blue,
    green,
    red;

  ConvertRGBToHSL(quantumRed(),quantumGreen(),quantumBlue(),&hue,&saturation,
    &lightness);

  lightness=lightness_;

  ConvertHSLToRGB(hue,saturation,lightness,&red,&green,&blue);

  quantumRed(ClampToQuantum(red));
  quantumGreen(ClampToQuantum(green));
  quantumBlue(ClampToQuantum(blue));
}

double Magick::ColorHSL::lightness (void) const
{
  double
    hue,
    lightness,
    saturation;

  ConvertRGBToHSL(quantumRed(),quantumGreen(),quantumBlue(),&hue,&saturation,
    &lightness);

  return(lightness);
}

void Magick::ColorHSL::saturation(const double saturation_)
{
  double
    hue,
    lightness,
    saturation;

  double
    blue,
    green,
    red;

  ConvertRGBToHSL(quantumRed(),quantumGreen(),quantumBlue(),&hue,&saturation,
    &lightness);

  saturation=saturation_;

  ConvertHSLToRGB(hue,saturation,lightness,&red,&green,&blue);

  quantumRed(ClampToQuantum(red));
  quantumGreen(ClampToQuantum(green));
  quantumBlue(ClampToQuantum(blue));
}

double Magick::ColorHSL::saturation(void) const
{
  double
    hue,
    lightness,
    saturation;

  ConvertRGBToHSL(quantumRed(),quantumGreen(),quantumBlue(),&hue,&saturation,
    &lightness);

  return(saturation);
}

Magick::ColorMono::ColorMono(void)
  : Color(RGBPixel)
{
}

Magick::ColorMono::ColorMono(const bool mono_)
  : Color((mono_ ? QuantumRange : 0),(mono_ ? QuantumRange : 0),
          (mono_ ? QuantumRange : 0))
{
}

Magick::ColorMono::ColorMono(const Magick::Color &color_)
  : Color(color_)
{
}

Magick::ColorMono::~ColorMono()
{
}

Magick::ColorMono& Magick::ColorMono::operator=(const Magick::Color& color_)
{
  *static_cast<Magick::Color*>(this)=color_;
  return(*this);
}

void Magick::ColorMono::mono(bool mono_)
{
  quantumRed(mono_ ? QuantumRange : 0);
  quantumGreen(mono_ ? QuantumRange : 0);
  quantumBlue(mono_ ? QuantumRange : 0);
}

bool Magick::ColorMono::mono(void) const
{
  return(quantumGreen() == 0);
}

Magick::ColorMono::ColorMono(PixelInfo *rep_,PixelType pixelType_)
  : Color(rep_,pixelType_)
{
}

Magick::ColorRGB::ColorRGB(void)
  : Color(RGBPixel)
{
}

Magick::ColorRGB::ColorRGB(const Magick::Color &color_)
  : Color(color_)
{
}

Magick::ColorRGB::ColorRGB(const double red_,const double green_,
  const double blue_)
  : Color(scaleDoubleToQuantum(red_),scaleDoubleToQuantum(green_),
          scaleDoubleToQuantum(blue_))
{
}

Magick::ColorRGB::ColorRGB(const double red_,const double green_,
  const double blue_,const double alpha_)
  : Color(scaleDoubleToQuantum(red_),scaleDoubleToQuantum(green_),
          scaleDoubleToQuantum(blue_),scaleDoubleToQuantum(alpha_))
{
}

Magick::ColorRGB::~ColorRGB(void)
{
}

Magick::ColorRGB& Magick::ColorRGB::operator=(const Magick::Color& color_)
{
  *static_cast<Magick::Color*>(this)=color_;
  return(*this);
}

void Magick::ColorRGB::alpha(const double alpha_)
{
  quantumAlpha(scaleDoubleToQuantum(alpha_));
}

double Magick::ColorRGB::alpha(void) const
{
  return(scaleQuantumToDouble(quantumAlpha()));
}

void Magick::ColorRGB::blue(const double blue_)
{
  quantumBlue(scaleDoubleToQuantum(blue_));
}

double Magick::ColorRGB::blue(void) const
{
  return(scaleQuantumToDouble(quantumBlue()));
}

void Magick::ColorRGB::green(const double green_)
{
  quantumGreen(scaleDoubleToQuantum(green_));
}

double Magick::ColorRGB::green(void) const
{
  return(scaleQuantumToDouble(quantumGreen()));
}

void Magick::ColorRGB::red(const double red_)
{
  quantumRed(scaleDoubleToQuantum(red_));
}

double Magick::ColorRGB::red(void) const
{
  return(scaleQuantumToDouble(quantumRed()));
}

Magick::ColorRGB::ColorRGB(PixelInfo *rep_,PixelType pixelType_)
  : Color(rep_,pixelType_)
{
}

Magick::ColorYUV::ColorYUV(void)
  : Color(RGBPixel)
{
}

Magick::ColorYUV::ColorYUV(const Magick::Color &color_)
  : Color(color_)
{
}

Magick::ColorYUV::ColorYUV(const double y_,const double u_,const double v_)
  : Color(RGBPixel)
{
  convert(y_, u_, v_);
}

Magick::ColorYUV::~ColorYUV(void)
{
}

Magick::ColorYUV& Magick::ColorYUV::operator=(const Magick::Color &color_)
{
  *static_cast<Magick::Color*>(this)=color_;
  return(*this);
}

void Magick::ColorYUV::u(const double u_)
{
  convert(y(), u_, v());
}

double Magick::ColorYUV::u(void) const
{
  return(scaleQuantumToDouble((-0.14740 * quantumRed()) - (0.28950 *
    quantumGreen()) + (0.43690 * quantumBlue())));
}

void Magick::ColorYUV::v(const double v_)
{
  convert(y(), u(), v_);
}

double Magick::ColorYUV::v(void) const
{
  return(scaleQuantumToDouble((0.61500 * quantumRed()) - (0.51500 *
    quantumGreen()) - (0.10000 * quantumBlue())));
}

void Magick::ColorYUV::y(const double y_)
{
  convert(y_, u(), v());
}

double Magick::ColorYUV::y ( void ) const
{
  return(scaleQuantumToDouble((0.29900 * quantumRed()) + (0.58700 *
    quantumGreen()) + (0.11400 * quantumBlue())));
}

void Magick::ColorYUV::convert(const double y_,const double u_,const double v_)
{
  quantumRed(scaleDoubleToQuantum(y_ + 1.13980 * v_));
  quantumGreen(scaleDoubleToQuantum(y_ - (0.39380 * u_) - (0.58050 * v_)));
  quantumBlue(scaleDoubleToQuantum(y_ + 2.02790 * u_));
}

Magick::ColorYUV::ColorYUV(PixelInfo *rep_,PixelType pixelType_)
  : Color(rep_,pixelType_)
{
}
