/*
 *  psychlops_figure_prototype.h
 *  Psychlops Standard Library (Universal)
 *
 *  Last Modified 2005/10/05 by Kenchi HOSOKAWA
 *  (C) 2005 Kenchi HOSOKAWA, Kazushi MARUYA, Takao SATO
 */

#ifndef HEADER_PSYCHLOPS_FIGURE_GABOR_PROTOTYPE
#define HEADER_PSYCHLOPS_FIGURE_GABOR_PROTOTYPE

#include <vector>

#include "../../../psychlops_core.h"



namespace Psychlops {

//	double gaussian(double sigma, double x);


namespace StimulusTemplate {

	class Gabor : virtual public Figure {
		public:
		Length wavelength;
		Angle orientation, phase;
		double contrast;

		protected:
		double sigma_;
		double left_, top_;
		int width_, height_;
		public:
		int sigma_index_;


		public:
		virtual ~Gabor();
		virtual Gabor& set(double freq, double sigma, double contrast = 0.5, double orient = 0.0, double phs=0.0) = 0;
		virtual Gabor& set(Length freq, Length sigma, double contrast = 0.5, Angle orient = 0.0*degree, Angle phs=0.0*degree) = 0;

		virtual Gabor& setContrast(double cont);
		virtual Gabor& setWavelength(Length length);
		virtual Gabor& setOrientation(Angle orient);
		virtual Gabor& setPhase(Angle phs);

		virtual Gabor& shift(const double x, const double y, const double z = 0.0) = 0;
		virtual Gabor& centering(const double x, const double y, const double z = 0.0) = 0;
		virtual Gabor& centering(const Point &po) = 0;
		virtual Gabor& centering(const Drawable &target = *Drawable::prime) = 0;
		virtual const Point getDatum() const;

		virtual Gabor& draw(Drawable &target = *Drawable::prime) = 0;
		virtual Gabor& draw(int x, int y, Drawable &target = *Drawable::prime) = 0;
	};

}

namespace Figures {

	int substructImages(Image &result, const Image &s1, const Image &s2, const double factor);
	void drawImageCoordinateTuner(Image &img);

	void drawGrating(Image &img, int width, int height, double wavelength, double contrast, double orientation, double phase);
	void drawGaussian(Image &img, double sigma, double factor);
	void drawGabor(Image &img, double sigma, double wavelength, double contrast, double orientation, double phase);


	class GaborBase : public Rectangle {
	public:
		Color peak[2];

		Length wavelength;
		double orientation, phase;
		double contrast;

	public:
		GaborBase();
		virtual GaborBase& setSigma(double sigma);
		virtual GaborBase& setSigma(Length sigma);
		virtual GaborBase& setWave(double wavelen, double cont = 0.5, double orient = 0.0, double phs=0.0);
		virtual GaborBase& setWave(Length wavelen, double cont = 0.5, Angle orient = 0.0*degree, Angle phs=0.0*degree);
		virtual GaborBase& cache(DrawableWithCache &target = *DrawableWithCache::prime) = 0;
		virtual GaborBase& draw(Drawable &target = *DrawableWithCache::prime) = 0;
		virtual void to(Image &dest, Canvas &media = *Display::the_canvas) = 0;
	};


	class ImageGabor : public GaborBase {
	protected:
		struct Key
		{
			double data[5];
			const bool operator <(Key rhs) const;
			const bool operator >(Key rhs) const;
		};
		std::map<Key, Image*> cache_list;
		Image normal_;

	public:
		ImageGabor();
		virtual ImageGabor& cache(DrawableWithCache &target = *DrawableWithCache::prime);
		virtual ImageGabor& draw(Drawable &target = *DrawableWithCache::prime);
		virtual void to(Image &dest, Canvas &media = *Display::the_canvas);
	};

}

	class QuickGabor : public StimulusTemplate::Gabor {
		class Instance {
			friend class QuickGabor;
			protected:
			int referenced_count_;
			int size_;
			Length sigma_;
			Image *envelope_;
			Rectangle *carrier_;
			bool instantiated;
			void set(double sigma);
			void setEnvelope();
			void setCarrier();
			public:
			Instance();
			~Instance();
			void release();
			Instance& draw(int x, int y, Length frequency, Angle orientation, Angle phase, double consrast, Drawable &target);
		};
		static std::vector<Instance *> instance_;

		int setInstance(double sigma);
		void release();

		public:
		QuickGabor();
		// it is not good to call virtual function from constructors.
		QuickGabor(double freq, double sigma, double contrast = 0.5, double orient = 0.0, double phs=0.0);
		QuickGabor(Length freq, Length sigma, double contrast = 0.5, Angle orient = 0.0*degree, Angle phs=0.0*degree);
		virtual ~QuickGabor();
		virtual QuickGabor& set(double freq, double sigma, double contrast = 0.5, double orient = 0.0, double phs=0.0);
		virtual QuickGabor& set(Length freq, Length sigma, double contrast = 0.5, Angle orient = 0.0*degree, Angle phs=0.0*degree);

		virtual QuickGabor& setDatum(const Point &po);
		virtual QuickGabor& shift(const double x, const double y, const double z = 0.0);
		virtual QuickGabor& centering(const double x, const double y, const double z = 0.0);
		virtual QuickGabor& centering(const Point &po);
		virtual QuickGabor& centering(const Drawable &target = *Drawable::prime);

		virtual QuickGabor& draw(Drawable &target = *Drawable::prime);
		virtual QuickGabor& draw(int x, int y, Drawable &target = *Drawable::prime);
	};

}


#endif
