/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *
 * This file is part of NMM. 
 * 
 * NMM is free software; you can redistribute it and/or modify it    
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; only version 2 of the License.      
 * 
 * NMM is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 
 * License for more details.  
 * 
 * You should have received a copy of the GNU General Public License 
 * along with NMM; if not, write to the Free Software Foundation,    
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#ifndef MANIPULATORS_HPP
#define MANIPULATORS_HPP

#include <Magick++.h>

#include <assert.h>
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string>

//#include "nmm/NMM.hpp"

/*
 * Manipulators that alter the image size:
 *
 * chop
 * crop
 * frame
 * sample
 * scale
 * shear
 * transform
 * zoom
 */

namespace NMM {


// This macro is used to get the enumeration value from a string by searching an array of strings.
#define ENUM_FROM_STRING(s, array, type) \
{ \
  for (unsigned int i = 0; i < sizeof array; i++) { \
    if (s == array[i]) { \
      return (type)i; \
    } \
  } \
  return (type)0; \
}

  // The following arrays contain the strings for every needed enumeration.

  // This array corresponds to Magick::NoiseTypes.
  static const string NOISE_TYPES[] = {
    "UniformNoise",
    "GaussianNoise",
    "MultiplicativeGaussianNoise",
    "ImpulseNoise",
    "LaplacianNoise",
    "PoissonNoise" 
  };

  // This array corresponds to Magick::GravityTypes.
  static const string GRAVITY_TYPES[] = {
    "ForgetGravity",
    "NorthWestGravity",
    "NorthGravity",
    "NorthEastGravity",
    "WestGravity",
    "CenterGravity",
    "EastGravity",
    "SouthWestGravity",
    "SouthGravity",
    "SouthEastGravity"
  };

  // This array corresponds to Magick::ChannelTypes.
  static const string CHANNEL_TYPES[] = {
    "UndefinedChannel",
    "RedChannel",
    "CyanChannel",
    "GreenChannel",
    "MagentaChannel",
    "BlueChannel",
    "YellowChannel",
    "OpacityChannel",
    "BlackChannel",
    "MatteChannel"
  };

  // This array corresponds to Magick::PaintMethods.
  static const string PAINT_METHODS[] = {
    "PointMethod",
    "ReplaceMethod",
    "FloodfillMethod",
    "FillToBorderMethod",
    "ResetMethod"
  };

  // Converts a string to Magick::NoiseType.
  static inline Magick::NoiseType noiseType(const string& s) {
    ENUM_FROM_STRING(s, NOISE_TYPES, Magick::NoiseType);
  }

  // Converts a string to Magick::GravityType.
  static inline Magick::GravityType gravityType(const string& s) {
    ENUM_FROM_STRING(s, GRAVITY_TYPES, Magick::GravityType);
  }

  // Converts a string to Magick::CannelType.
  static inline Magick::ChannelType channelType(const string& s) {
    ENUM_FROM_STRING(s, CHANNEL_TYPES, Magick::ChannelType);
  }

  // Converts a string to Magick::PaintMethod.
  static inline Magick::PaintMethod paintMethod(const string& s) {
    ENUM_FROM_STRING(s, PAINT_METHODS, Magick::PaintMethod);
  }

  
  // Resizes the given resolution by considering geometry parameters like aspect ratio or scaling.
  // These additional parameters are only used by ImageMagick, when the Image ist explicitly resized
  // (the corresponding manipulations are sample and scale).
  static inline void resizeByGeometry(int &res_x, int& res_y, const Magick::Geometry& g)
  {
    if (g.percent()) {
      // Geometry width and height are specified as percentage.
      // Don't care about preserving the aspect ratio.
      res_x = g.width() * res_x / 100;
      res_y = g.height() * res_y / 100;
    }
    else {
      // Geometry width and height are specified as real sizes.
      if (g.aspect()) {
	// Don't care about preserving the aspect ratio.
	res_x = g.width();
	res_y = g.height();
      }
      else {
	// Preserve aspect ratio.
	if (res_x > res_y) {
	  res_y = g.width() * res_y / res_x;
	  res_x = g.width();
	}
	else {
	  res_x = g.height() * res_x / res_y;
	  res_y = g.height();
	}
      }
    }
  }

  //! The base class for all Manipulators. Every image manipulation method needs its own class derived from Manipulator.
  class Manipulator
  {
  public:
    virtual ~Manipulator() {}
    //! This abstract function has to be overwritten to implement the actual manipulation.
    virtual void manipulate(Magick::Image& image) = 0;

    //! Manipulators that alter the image size have to overwrite this function.
    virtual void manipulateSize(int& , int& )
    {}
  };

  // The following classes implement the ImageMagick manipulation routines.
  // Every class has its own constructor for the specification of manipulation parameters.
  // By implementing the manipulate function they can perform the wanted manipulation on the given image.
  // Manipulators that alter the image size have to overwrite the manipulateSize function.

  // -----------------------------------------------------------------
  class AdaptiveThresholdManipulator : public Manipulator
  {
  public:
    AdaptiveThresholdManipulator(unsigned int width,
				 unsigned int height,
				 unsigned int offset = 0)
      : __width(width), __height(height), __offset(offset)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.adaptiveThreshold(__width, __height, __offset);
    }

  private:
    unsigned int __width;
    unsigned int __height;
    unsigned int __offset;
  };


  // -----------------------------------------------------------------
  class AddNoiseManipulator : public Manipulator
  {
  public:
    AddNoiseManipulator(const string& noise_type)
      : __noise_type(noiseType(noise_type))
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.addNoise(__noise_type);
    }

  private:
    Magick::NoiseType __noise_type;
  };

  // -----------------------------------------------------------------
  class AnnotateManipulator : public Manipulator
  {
  public:
    AnnotateManipulator(const string& text,
			const string& bounding_area,
			const string& gravity,
			double degrees)
      : __text(text),
	__bounding_area(bounding_area),
	__gravity(gravityType(gravity)),
	__degrees(degrees)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.annotate(__text, __bounding_area, __gravity, __degrees);
    }

  private:
    string __text;
    Magick::Geometry __bounding_area;
    Magick::GravityType __gravity;
    double __degrees;
  };

  // -----------------------------------------------------------------
  class BlurManipulator : public Manipulator
  {
  public:
    BlurManipulator(double radius = 1, double sigma = 0.5)
      : __radius(radius), __sigma(sigma)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.blur(__radius, __sigma);
    }

  private:
    double __radius;
    double __sigma;
  };

  // -----------------------------------------------------------------
  class BorderManipulator : public Manipulator
  {
  public:
    BorderManipulator(const string& geometry = "6x6+0+0")
      : __geometry(geometry)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.border(__geometry);
    }

    virtual void manipulateSize(int& res_x, int& res_y)
    {
      res_x += 2 * __geometry.width();
      res_y += 2 * __geometry.height();
    }

  private:
    Magick::Geometry __geometry;
  };



  // -----------------------------------------------------------------
  class ChannelManipulator : public Manipulator
  {
  public:
    ChannelManipulator(const string& layer)
      : __layer(channelType(layer))
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.channel(__layer);
    }

  private:
    Magick::ChannelType __layer;
  };



  // -----------------------------------------------------------------
  class CharcoalManipulator : public Manipulator
  {
  public:
    CharcoalManipulator(double radius = 1, double sigma = 0.5)
      : __radius(radius), __sigma(sigma)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.charcoal(__radius, __sigma);
    }

  private:
    double __radius;
    double __sigma;
  };

  // -----------------------------------------------------------------
  class ChopManipulator : public Manipulator
  {
  public:
    ChopManipulator(const string& geometry)
      : __geometry(geometry)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.chop(__geometry);
    }

    virtual void manipulateSize(int& res_x, int& res_y)
    {
      res_x -= __geometry.width();
      res_y -= __geometry.height();

      if (res_x < 0) res_x = 0;
      if (res_y < 0) res_y = 0;
    }

  private:
    Magick::Geometry __geometry;
  };
  

  // -----------------------------------------------------------------
  class ColorizeManipulator : public Manipulator
  {
  public:
    ColorizeManipulator(unsigned int opacity_red,
			unsigned int opacity_green,
			unsigned int opacity_blue,
			double pen_color_r,
			double pen_color_g,
			double pen_color_b)
      : __opacity_red(opacity_red),
	__opacity_green(opacity_green),
	__opacity_blue(opacity_blue),
	__pen_color(pen_color_r, pen_color_g, pen_color_b)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.colorize(__opacity_red,
		     __opacity_green,
		     __opacity_blue,
		     __pen_color);
    }

  private:
    unsigned int __opacity_red;
    unsigned int __opacity_green;
    unsigned int __opacity_blue;
    Magick::ColorRGB __pen_color;
  };

  // -----------------------------------------------------------------
  class CommentManipulator : public Manipulator
  {
  public:
    CommentManipulator(const string& comment)
      : __comment(comment)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.comment(__comment);
    }

  private:
    string __comment;
  };


  // -----------------------------------------------------------------
  class ContrastManipulator : public Manipulator
  {
  public:
    ContrastManipulator(unsigned int sharpen)
      : __sharpen(sharpen)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.contrast(__sharpen);
    }

  private:
    unsigned int __sharpen;
  };
  
  
  // -----------------------------------------------------------------
  class CropManipulator : public Manipulator
  {
  public:
    CropManipulator(const string& geometry)
      : __geometry(geometry)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      //PRINTSIZE;
      image.crop(__geometry);
      //PRINTSIZE;
    }

    virtual void manipulateSize(int& res_x, int& res_y)
    {
      res_x = __geometry.width();
      res_y = __geometry.height();
    }

  private:
    Magick::Geometry __geometry;
  };


  // -----------------------------------------------------------------
  class CycleColormapManipulator : public Manipulator
  {
  public:
    CycleColormapManipulator(int amount)
      : __amount(amount)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.cycleColormap(__amount);
    }

  private:
    int __amount;
  };


  // -----------------------------------------------------------------
  class DespeckleManipulator : public Manipulator
  {
  public:
    DespeckleManipulator()
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.despeckle();
    }
  };


  // -----------------------------------------------------------------
  class EdgeManipulator : public Manipulator
  {
  public:
    EdgeManipulator(unsigned int radius = 0.0)
      : __radius(radius)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.edge(__radius);
    }

  private:
    unsigned int __radius;
  };


  // -----------------------------------------------------------------
  class EmbossManipulator : public Manipulator
  {
  public:
    EmbossManipulator(double radius = 1, double sigma = 0.5)
      : __radius(radius), __sigma(sigma)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.emboss(__radius, __sigma);
    }

  private:
    double __radius;
    double __sigma;
  };


  // -----------------------------------------------------------------
  class EnhanceManipulator : public Manipulator
  {
  public:
    EnhanceManipulator()
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.enhance();
    }
  };


  // -----------------------------------------------------------------
  class EqualizeManipulator : public Manipulator
  {
  public:
    EqualizeManipulator()
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.equalize();
    }

  private:
  };


  // -----------------------------------------------------------------
  class EraseManipulator : public Manipulator
  {
  public:
    EraseManipulator()
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.erase();
    }
  };


  // -----------------------------------------------------------------
  class FlipManipulator : public Manipulator
  {
  public:
    FlipManipulator()
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.flip();
    }
  };


  // -----------------------------------------------------------------
  class FloodFillColorManipulator : public Manipulator
  {
  public:
    FloodFillColorManipulator(unsigned int x, unsigned int y, double fill_color_r, double fill_color_g, double fill_color_b)
      : __x(x), __y(y), __fill_color(fill_color_r, fill_color_g, fill_color_b)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.floodFillColor(__x, __y, __fill_color);
    }

  private:
    unsigned int __x;
    unsigned int __y;
    Magick::ColorRGB __fill_color;
  };


  // -----------------------------------------------------------------
  class FloodFillOpacityManipulator : public Manipulator
  {
  public:
    FloodFillOpacityManipulator(const long& x, const long& y, unsigned int opacity, const string& method)
      : __x(x), __y(y), __opacity(opacity), __method(paintMethod(method))
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.floodFillOpacity(__x, __y, __opacity, __method);
    }

  private:
    long __x;
    long __y;
    unsigned int __opacity;
    Magick::PaintMethod __method;    
  };


  // -----------------------------------------------------------------
  class FlopManipulator : public Manipulator
  {
  public:
    FlopManipulator()
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.flop();
    }
  };


  // -----------------------------------------------------------------
  class FrameManipulator : public Manipulator
  {
  public:
    FrameManipulator(unsigned int width, unsigned int height, int x, int y, int inner_bevel = 0, int outer_bevel = 0)
      : __width(width), __height(height), __x(x), __y(y), __inner_bevel(inner_bevel), __outer_bevel(outer_bevel)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      //PRINTSIZE;
      image.frame(__width, __height, __x, __y);
      //PRINTSIZE;
    }

    virtual void manipulateSize(int& res_x, int& res_y)
    {
      res_x += 2 * __width;
      res_y += 2 * __height;
    }

  private:
    unsigned int __width;
    unsigned int __height;
    int __x;
    int __y;
    int __inner_bevel;
    int __outer_bevel;
  };


  // -----------------------------------------------------------------
  class GammaManipulator : public Manipulator
  {
  public:
    GammaManipulator(double gamma_red, double gamma_green, double gamma_blue)
      : __gamma_red(gamma_red), __gamma_green(gamma_green), __gamma_blue(gamma_blue)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.gamma(__gamma_red, __gamma_green, __gamma_blue);
    }

  private:
    double __gamma_red;
    double __gamma_green;
    double __gamma_blue;
  };


  // -----------------------------------------------------------------
  class GaussianBlurManipulator : public Manipulator
  {
  public:
    GaussianBlurManipulator(double width, double sigma)
      : __width(width), __sigma(sigma)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.gaussianBlur(__width, __sigma);
    }

  private:
    double __width;
    double __sigma;
  };


  // -----------------------------------------------------------------
  class ImplodeManipulator : public Manipulator
  {
  public:
    ImplodeManipulator(double factor)
      : __factor(factor)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.implode(__factor);
    }

  private:
    double __factor;
  };


  // -----------------------------------------------------------------
  class LabelManipulator : public Manipulator
  {
  public:
    LabelManipulator(const string& name)
      : __name(name)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.label(__name);
    }

  private:
    string __name;
  };


  // -----------------------------------------------------------------
  class MagnifyManipulator : public Manipulator
  {
  public:
    MagnifyManipulator()
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.magnify();
    }
  };


  // -----------------------------------------------------------------
  class MatteFloodfillManipulator : public Manipulator
  {
  public:
    MatteFloodfillManipulator(double target_r, double target_g, double target_b, unsigned int opacity, int x, int y, const string& method)
      : __target(target_r, target_g, target_b), __opacity(opacity), __x(x), __y(y), __method(paintMethod(method))
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.matteFloodfill(__target, __opacity, __x, __y, __method);
    }

  private:
    Magick::ColorRGB __target;
    unsigned int __opacity;
    int __x;
    int __y;
    Magick::PaintMethod __method;
  };


  // -----------------------------------------------------------------
  class MedianFilterManipulator : public Manipulator
  {
  public:
    MedianFilterManipulator(double radius = 0.0)
      : __radius(radius)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.medianFilter(__radius);
    }

  private:
    double __radius;
  };


  // -----------------------------------------------------------------
  class MinifyManipulator : public Manipulator
  {
  public:
    MinifyManipulator()
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.minify();
    }
  };


  // -----------------------------------------------------------------
  class ModulateManipulator : public Manipulator
  {
  public:
    ModulateManipulator(double brightness, double saturation, double hue)
      : __brightness(brightness), __saturation(saturation), __hue(hue)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.modulate(__brightness, __saturation, __hue);
    }

  private:
    double __brightness;
    double __saturation;
    double __hue;
  };


  // -----------------------------------------------------------------
  class NegateManipulator : public Manipulator
  {
  public:
    NegateManipulator()
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.negate();
    }
  };


  // -----------------------------------------------------------------
  class NormalizeManipulator : public Manipulator
  {
  public:
    NormalizeManipulator()
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.normalize();
    }
  };


  // -----------------------------------------------------------------
  class OilPaintManipulator : public Manipulator
  {
  public:
    OilPaintManipulator(unsigned int radius = 3)
      : __radius(radius)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.oilPaint(__radius);
    }

  private:
    unsigned int __radius;
  };


  // -----------------------------------------------------------------
  class OpacityManipulator : public Manipulator
  {
  public:
    OpacityManipulator(unsigned int opacity)
      : __opacity(opacity)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.opacity(__opacity);
    }

  private:
    unsigned int __opacity;
  };


  // -----------------------------------------------------------------
  class OpaqueManipulator : public Manipulator
  {
  public:
    OpaqueManipulator(double opaque_color_r, double opaque_color_g, double opaque_color_b, double pen_color_r, double pen_color_g, double pen_color_b)
      : __opaque_color(opaque_color_r, opaque_color_g, opaque_color_b),
	__pen_color(pen_color_r, pen_color_g, pen_color_b)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.opaque(__opaque_color, __pen_color);
    }

  private:

    Magick::ColorRGB __opaque_color;
    Magick::ColorRGB __pen_color;
  };


  // -----------------------------------------------------------------
  class QuantizeManipulator : public Manipulator
  {
  public:
    QuantizeManipulator(bool )
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.quantize();
    }
  };


  // -----------------------------------------------------------------
  class RaiseManipulator : public Manipulator
  {
  public:
    RaiseManipulator(const string& geometry, bool raisedFlag = false)
      : __geometry(geometry), __raisedFlag(raisedFlag)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.raise(__geometry, __raisedFlag);
    }

  private:
    Magick::Geometry __geometry;
    bool __raisedFlag;
  };


  // -----------------------------------------------------------------
  class ReduceNoiseManipulator : public Manipulator
  {
  public:
    ReduceNoiseManipulator(unsigned int order)
      : __order(order)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.reduceNoise(__order);
    }

  private:
    unsigned int __order;
  };


  // -----------------------------------------------------------------
  class RollManipulator : public Manipulator
  {
  public:
    RollManipulator(int columns, int rows)
      : __columns(columns), __rows(rows)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.roll(__columns, __rows);
    }

  private:
    int __columns;
    int __rows;
  };


  // -----------------------------------------------------------------
  class RotateManipulator : public Manipulator
  {
  public:
    RotateManipulator(double degrees)
      : __degrees(degrees)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.rotate(__degrees);
    }

    virtual void manipulateSize(int& res_x, int& res_y)
    {
      // original corner points (except (0,0), which will not be transformed))
      pair<int,int> points[] = {
	make_pair(res_x, 0), make_pair(res_x, res_y), make_pair(0, res_y)
      };

      // transform them
      float c = cosf(__degrees / 180 * 3.1415927);
      float s = sinf(__degrees / 180 * 3.1415927);

      for (int i = 0; i < 3; i++) {
	int x = points[i].first;
	int y = points[i].second;
	points[i].first = (int)(x * c - y * s);
	points[i].second = (int)(y * c + x * s);
      }

      // compute bounding box
      int min_x = 0;
      int min_y = 0;
      int max_x = 0;
      int max_y = 0;

      for (int i = 0; i < 3; i++) {
	int x = points[i].first;
	int y = points[i].second;
	if (x < min_x) min_x = x;
	if (y < min_y) min_y = y;
	if (x > max_x) max_x = x;
	if (y > max_y) max_y = y;
      }

      // get new image size
      res_x = max_x - min_x;
      res_y = max_y - min_y;
    }

  private:
    double __degrees;
  };


  // -----------------------------------------------------------------
  class SampleManipulator : public Manipulator
  {
  public:
    SampleManipulator(const string& geometry)
      : __geometry(geometry)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      image.sample(__geometry);
    }

    virtual void manipulateSize(int& res_x, int& res_y)
    {
      resizeByGeometry(res_x, res_y, __geometry);
    }

  private:
    Magick::Geometry __geometry;
  };


  // -----------------------------------------------------------------
  class ScaleManipulator : public Manipulator
  {
  public:
    ScaleManipulator(const string& geometry)
      : __geometry(geometry)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      //PRINTSIZE;
      image.scale(__geometry);
      //PRINTSIZE;
    }

    virtual void manipulateSize(int& res_x, int& res_y)
    {
      resizeByGeometry(res_x, res_y, __geometry);
    }

  private:
    Magick::Geometry __geometry;
  };


  // -----------------------------------------------------------------
  class SegmentManipulator : public Manipulator
  {
  public:
    SegmentManipulator(double cluster_threshold = 1.0, double smoothing_threshold = 1.5)
      : __cluster_threshold(cluster_threshold), __smoothing_threshold(smoothing_threshold)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.segment(__cluster_threshold, __smoothing_threshold);
    }

  private:
    double __cluster_threshold;
    double __smoothing_threshold;
  };


  // -----------------------------------------------------------------
  class ShadeManipulator : public Manipulator
  {
  public:
    ShadeManipulator(double azimuth = 30, double elevation = 30, bool color_shading = false)
      : __azimuth(azimuth), __elevation(elevation), __color_shading(color_shading)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.shade(__azimuth, __elevation, __color_shading);
    }

  private:
    double __azimuth;
    double __elevation;
    bool __color_shading;
  };


  // -----------------------------------------------------------------
  class SharpenManipulator : public Manipulator
  {
  public:
    SharpenManipulator(double radius = 1, double sigma = 0.5)
      : __radius(radius), __sigma(sigma)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.sharpen(__radius, __sigma);
    }

  private:
    double __radius;
    double __sigma;
  };


  // -----------------------------------------------------------------
  class ShaveManipulator : public Manipulator
  {
  public:
    ShaveManipulator(const string& geometry)
      : __geometry(geometry)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.shave(__geometry);
    }

  private:
    Magick::Geometry __geometry;
  };


  // -----------------------------------------------------------------
  class ShearManipulator : public Manipulator
  {
  public:
    ShearManipulator(double x_shear_angle, double y_shear_angle)
      : __x_shear_angle(x_shear_angle), __y_shear_angle(y_shear_angle)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      //PRINTSIZE;
      image.shear(__x_shear_angle, __y_shear_angle);
      //PRINTSIZE;
    }
 
    virtual void manipulateSize(int& res_x, int& res_y)
    {
      int add_x = (int)(2 * res_y * sin(__x_shear_angle / 180 * 3.1415927));
      int add_y = (int)(2 * res_x * sin(__y_shear_angle / 180 * 3.1415927));

      if (add_x < 0) add_x = 0;
      if (add_y < 0) add_y = 0;

      res_x += add_x;
      res_y += add_y;
    }

  private:
    double __x_shear_angle;
    double __y_shear_angle;
  };


  // -----------------------------------------------------------------
  class SolarizeManipulator : public Manipulator
  {
  public:
    SolarizeManipulator(double factor = 50.0)
      : __factor(factor)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.solarize(__factor);
    }

  private:
    double __factor;
  };


  // -----------------------------------------------------------------
  class SpreadManipulator : public Manipulator
  {
  public:
    SpreadManipulator(unsigned int amount = 3)
      : __amount(amount)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.spread(__amount);
    }

  private:
    unsigned int __amount;
  };


  // -----------------------------------------------------------------
  class SwirlManipulator : public Manipulator
  {
  public:
    SwirlManipulator(double degrees)
      : __degrees(degrees)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.swirl(__degrees);
    }

  private:
    double __degrees;
  };


  // -----------------------------------------------------------------
  class ThresholdManipulator : public Manipulator
  {
  public:
    ThresholdManipulator(double threshold)
      : __threshold(threshold)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.threshold(__threshold);
    }

  private:
    double __threshold;
  };


  // -----------------------------------------------------------------
  class TransformManipulator : public Manipulator
  {
  public:
    TransformManipulator(const string& image_geometry, const string& crop_geometry)
      : __image_geometry(image_geometry), __crop_geometry(crop_geometry)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      //PRINTSIZE;
      image.transform(__image_geometry, __crop_geometry);
      //PRINTSIZE;
    }

    virtual void manipulateSize(int& res_x, int& res_y)
    {
      res_x = __image_geometry.width();
      res_y = __image_geometry.height();
    }

  private:
    Magick::Geometry __image_geometry;
    Magick::Geometry __crop_geometry;
  };


  // -----------------------------------------------------------------
  class TransparentManipulator : public Manipulator
  {
  public:
    TransparentManipulator(double color_r, double color_g, double color_b)
      : __color(color_r, color_g, color_b)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.transparent(__color);
    }

  private:
    Magick::ColorRGB __color;
  };


  // -----------------------------------------------------------------
  class TrimManipulator : public Manipulator
  {
  public:
    TrimManipulator()
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.trim();
    }
  };


  // -----------------------------------------------------------------
  class UnsharpmaskManipulator : public Manipulator
  {
  public:
    UnsharpmaskManipulator(double radius, double sigma, double amount, double threshold)
      : __radius(radius), __sigma(sigma), __amount(amount), __threshold(threshold)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.unsharpmask(__radius, __sigma, __amount, __threshold);
    }

  private:
    double __radius;
    double __sigma;
    double __amount;
    double __threshold;
  };


  // -----------------------------------------------------------------
  class WaveManipulator : public Manipulator
  {
  public:
    WaveManipulator(double amplitude = 25.0, double wavelength = 150.0)
      : __amplitude(amplitude), __wavelength(wavelength)
    {}

    virtual void manipulate(Magick::Image& image)
    {

      image.wave(__amplitude, __wavelength);
    }

  private:
    double __amplitude;
    double __wavelength;
  };


  // -----------------------------------------------------------------
  class ZoomManipulator : public Manipulator
  {
  public:
    ZoomManipulator(const string& geometry)
      : __geometry(geometry)
    {}

    virtual void manipulate(Magick::Image& image)
    {
      
      //PRINTSIZE;
      image.zoom(__geometry);
      //PRINTSIZE;
    }

    virtual void manipulateSize(int& res_x, int& res_y)
    {
      resizeByGeometry(res_x, res_y, __geometry);
    }

  private:
    Magick::Geometry __geometry;
  };


}

#endif
