
using System;
using Gtk;
using Cairo;
using Tao.OpenGl;

namespace FSpot.Editors {
	public abstract class Editor {
		protected PhotoImageView view;
		protected Gtk.Window controls;
		
		public event EventHandler Done;

		public Editor (PhotoImageView view)
		{
			SetView (view);
		}

		protected virtual string GetTitle ()
		{
			return String.Empty;
		}

		protected virtual void SetView (PhotoImageView view)
		{
			if (controls != null)
				controls.Destroy ();

			controls = null;

			this.view = view;
			if (view == null)
				return;

			Widget w = CreateControls ();

			if (w != null) {
#if false
				ControlOverlay c = new ControlOverlay (view);
				c.AutoHide = false;
				w.ShowAll ();
				c.Add (w);
				c.Visibility = ControlOverlay.VisibilityType.Full;
				controls = c;
#else
				Window win = new Window (String.Format ("{0}", GetTitle ()));
				win.TransientFor = (Gtk.Window) view.Toplevel;
				win.Add (w);
				win.ShowAll ();
				win.DeleteEvent += delegate { Destroy (); };
				controls = win;
#endif
			}
			
		}

		protected virtual Widget CreateControls ()
		{
			return null;
		}

		public void Destroy ()
		{
			Close ();
		}

		protected virtual void Close ()
		{
			
			if (controls != null)
				controls.Destroy ();

			if (Done != null)
				Done (this, EventArgs.Empty);

			SetView (null);
		}
	}

	public class GlEditor : Editor {
		protected GlTransition transition;
		protected Scale scale;
		protected Texture texture;

		public GlEditor (PhotoImageView view) : base (view)
		{
			transition = new GlTransition.Flip ();
		}

		protected override Widget CreateControls ()
		{
			scale = new HScale (0, 1, 0.01);
			scale.ValueChanged += HandleValueChanged;
			scale.WidthRequest = 250;

			return scale;
		}

		protected override void SetView (PhotoImageView value)
		{
			if (view != null) {
				view.ExposeEvent -= ExposeEvent;
				view.QueueDraw ();
			}

			base.SetView (value);

			if (value == null)
				return;

			view.ExposeEvent += ExposeEvent;
			view.QueueDraw ();
		}

		[GLib.ConnectBefore]
		public virtual void ExposeEvent (object sender, ExposeEventArgs args)
		{
			view.Glx.MakeCurrent (view.GdkWindow);
			Gl.glEnable (Gl.GL_CONVOLUTION_2D);
			Gdk.Color c = view.Style.Background (view.State);
			Gl.glClearColor (c.Red / (float) ushort.MaxValue,
					 c.Blue / (float) ushort.MaxValue, 
					 c.Green / (float) ushort.MaxValue, 
					 1.0f);

			if (texture == null) {
				float [] kernel = new float [] { .25f, .5f, .25f,
								 .5f, 1f, .5f,
								 .25f, .5f, .25f};

#if false
				bool supported = GlExtensionLoader.LoadExtension ("GL_ARB_imaging");
				if (!supported) {
					System.Console.WriteLine ("GL_ARB_imaging not supported");
					return;
				}	
#else
				GlExtensionLoader.LoadAllExtensions ();
#endif
				
				Gl.glConvolutionParameteri (Gl.GL_CONVOLUTION_2D,
							    Gl.GL_CONVOLUTION_BORDER_MODE,
							    Gl.GL_REPLICATE_BORDER);

				Gl.glConvolutionFilter2D (Gl.GL_CONVOLUTION_2D,
							  Gl.GL_INTENSITY, 
							  3, 
							  3,
							  Gl.GL_INTENSITY,
							  Gl.GL_FLOAT, 
							  kernel);

				texture = new Texture (view.CompletePixbuf ());
			}

			Gl.glShadeModel(Gl.GL_FLAT);

			Gl.glColor3f(1.0f, 1.0f, 1.0f);
			
			Gl.glEnable (Gl.GL_DEPTH_TEST);
			Gl.glEnable (Gl.GL_NORMALIZE);
			Gl.glShadeModel (Gl.GL_FLAT);
			Gl.glEnable (Gl.GL_TEXTURE_RECTANGLE_ARB);
			Gl.glClear (Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);

			

			transition.Draw (view.Allocation, texture, texture);
			
			view.Glx.SwapBuffers (view.GdkWindow);
			args.RetVal = true;
			Gl.glDisable (Gl.GL_CONVOLUTION_2D);
		}

		private void HandleValueChanged (object sender, System.EventArgs args)
		{
			transition.Percent = (float) scale.Value;
			view.QueueDraw ();
		}

		protected override void Close ()
		{
			if (texture != null && view != null && view.Glx != null && view.GdkWindow != null) {
				view.Glx.MakeCurrent (view.GdkWindow);
				texture.Dispose ();
			}
			
			base.Close ();
		}
	}

	public class EffectEditor : Editor {
		protected IEffect effect;
		protected Widgets.ImageInfo info;
		bool double_buffer;

		public EffectEditor (PhotoImageView view) : base (view)
		{
		}
		
		protected override void SetView (PhotoImageView value)
		{
			if (view != null) {
				view.ExposeEvent -= ExposeEvent;
				view.QueueDraw ();
				view.DoubleBuffered = double_buffer;
			}

			base.SetView (value);

			if (view == null)
				return;

			info = new Widgets.ImageInfo (view.CompletePixbuf ());

			double_buffer = (view.WidgetFlags & WidgetFlags.DoubleBuffered) == WidgetFlags.DoubleBuffered;
			view.DoubleBuffered = true;
			view.ExposeEvent += ExposeEvent;
			view.QueueDraw ();
		}

		[GLib.ConnectBefore]
		public virtual void ExposeEvent (object sender, ExposeEventArgs args)
		{
			Context ctx = Widgets.CairoUtils.CreateContext (view.GdkWindow);
			Gdk.Color c = view.Style.Background (view.State);
			ctx.Source = new SolidPattern (c.Red / (float) ushort.MaxValue,
						       c.Blue / (float) ushort.MaxValue, 
						       c.Green / (float) ushort.MaxValue);

			ctx.Paint ();

			effect.OnExpose (ctx, view.Allocation);
			
			args.RetVal = true;
		}

		protected override void Close ()
		{
			base.Close ();

			if (effect != null)
				effect.Dispose ();
			effect = null;
			
			if (info != null)
				info.Dispose ();
			info = null;
			
		}
	}
}

