/*
	Cadabra: a field-theory motivated computer algebra system.
	Copyright (C) 2001-2011  Kasper Peeters <kasper.peeters@aei.mpg.de>

   This program 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, either version 3 of the
   License, or (at your option) any later version.

   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#ifndef widgets_hh__
#define widgets_hh__

#include <string>
#include <vector>
#include <set>
#include <errno.h>
#include <gtkmm/box.h>
#include <gtkmm/eventbox.h>
#include <gtkmm/image.h>
#include <gtkmm/textview.h>
#include <gtkmm/separator.h>
#include <gtkmm/actiongroup.h>
#include <sstream>

const int LINE_SPACING=3;

#include "../src/stopwatch.hh"

std::string trim(const std::string& s);

/// TeXEngine is a singleton which is used to convert LaTeX strings
/// into Gdk::Pixbuf objects. This is a two-stage process: you first
/// 'check in' a string into the system, in exchange for a pointer to 
/// a TeXRequest object. When you are ready to retrieve the image,
/// call 'get_pixbuf'. 
///
/// If you need to generate images for more than one string, simply
/// check them all in and then call 'convert_all' before retrieving
/// the pixbufs.

class TeXEngine {
	public:
		class TeXException : public std::logic_error {
			public:
				TeXException(const std::string&);
		};

		class TeXRequest {
			public:
				TeXRequest();
				friend class TeXEngine;
			private:
				std::string               latex_string;
				std::string               start_wrap, end_wrap;
				bool                      needs_generating;
				Glib::RefPtr<Gdk::Pixbuf> pixbuf;
		};
		
		TeXEngine();
		~TeXEngine();

		/// Set the width and font size for all images to be generated.
		void set_geometry(int horizontal_pixels);
		void set_font_size(int font_size);
		std::vector<std::string> latex_packages;

		/// All checkin/checkout conversion routines.
		TeXRequest                *checkin(const std::string&,
													  const std::string& startwrap, const std::string& endwrap);
		TeXRequest                *modify(TeXRequest *, const std::string&);
		Glib::RefPtr<Gdk::Pixbuf>  get_pixbuf(TeXRequest *);
		void                       convert_all();
		void                       checkout(TeXRequest *);
		
	private:		
		static double millimeter_per_inch;

		std::set<TeXRequest *> requests;

		int                    horizontal_pixels_;
		int                    font_size_;

		void erase_file(const std::string&) const;
		void convert_one(TeXRequest*);
		void convert_set(std::set<TeXRequest *>&);

		std::string handle_latex_errors(const std::string&) const;
};


/// TeXBuffer is like a TextBuffer, but now for images
/// generated by TeX+dvipng. That is, for each cell in the
/// document, there is one TeXBuffer (and one TextBuffer);
/// the TeXView and TextView classes use these buffers to
/// get their content.

class TeXBuffer : public Glib::Object {
	public:
		TeXBuffer(Glib::RefPtr<Gtk::TextBuffer>, TeXEngine&);
		~TeXBuffer();
		
		void generate(const std::string& startwrap, const std::string& endwrap);
		void regenerate();

		static Glib::RefPtr<TeXBuffer> create(Glib::RefPtr<Gtk::TextBuffer>, TeXEngine&);
		Glib::RefPtr<Gdk::Pixbuf>      get_pixbuf();

		Glib::RefPtr<Gtk::TextBuffer>  tex_source;
		TeXEngine::TeXRequest         *tex_request;

	protected:
		TeXEngine &engine_;
};


/// TeXView is an image widget with some additional Gtk
/// structure; we use it to display images generated by TeX.
/// The actual pixbuf is generated by a TeXBuffer, which in 
/// turn uses a TeXEngine.

class TeXView : public Gtk::EventBox {
	public:
		TeXView(Glib::RefPtr<TeXBuffer>, int hmargin=25);

		Glib::RefPtr<TeXBuffer>   texbuf;

		Gtk::VBox                 vbox;
		Gtk::HBox                 hbox;
		Gtk::Image                image;

		void update_image();
		
	protected:
		virtual void on_show();
};


/// ExpressionInput is essentially a TextView with some
/// additional i/o logic.

class ExpressionInput : public Gtk::VBox {
	public:
		ExpressionInput(Glib::RefPtr<Gtk::TextBuffer>, const std::string& fontname, int hmargin=25);
		
		class exp_input_tv : public Gtk::TextView {
			public:
				exp_input_tv(Glib::RefPtr<Gtk::TextBuffer>);
				virtual bool on_key_press_event(GdkEventKey*);
				virtual bool on_expose_event(GdkEventExpose* event);

				sigc::signal1<bool, std::string> emitter;
				sigc::signal0<bool>              content_changed;
		};

		bool handle_button_press(GdkEventButton *);

		
		exp_input_tv               edit;
		Gtk::HBox                  hbox;
		Gtk::VSeparator            vsep;
};

/// TeXInput is a widget which can be used to edit and display
/// TeX input. Double-clicking on the graphical TeX version
/// toggles visibility of the edit box.

class TeXInput : public Gtk::VBox {
	public:
		TeXInput(Glib::RefPtr<Gtk::TextBuffer>, Glib::RefPtr<TeXBuffer>, const std::string& fontname);
		
		class exp_input_tv : public Gtk::TextView {
			public:
				exp_input_tv(Glib::RefPtr<Gtk::TextBuffer>);
				virtual bool on_key_press_event(GdkEventKey*);
				sigc::signal1<bool, std::string> emitter;

				bool is_modified;

				friend class TeXInput;
			protected:
				bool folded_away;
		};
		
		exp_input_tv  edit;

		bool is_folded() const;
		void set_folded(bool);

		TeXView  texview;
};

extern class TeXEngine tex_engine_main, tex_engine_help;

#endif
