/*
 * Copyright (C) 2012-2015 Robin Haberkorn
 *
 * 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 .
 */
#ifndef __INTERFACE_H
#define __INTERFACE_H
#include 
#include 
#include 
#include 
#include "undo.h"
namespace SciTECO {
/* avoid include dependency conflict */
class QRegister;
class Buffer;
class Cmdline;
extern sig_atomic_t sigint_occurred;
/**
 * Base class for all SciTECO views. This is a minimal
 * abstraction where the implementor only has to provide
 * a method for dispatching Scintilla messages.
 * Everything else is handled by other SciTECO classes.
 *
 * This interface employs the Curiously Recurring Template
 * Pattern (CRTP). To implement it, one must derive from
 * View. The methods to implement actually
 * have an "_impl" suffix so as to avoid infinite recursion
 * if an implementation is missing.
 * Externally however, the methods as given in this interface
 * may be called.
 *
 * The CRTP has a runtime overhead at low optimization levels
 * (additional non-inlined calls), but should provide a
 * significant performance boost when inlining is enabled.
 *
 * Note that not all methods have to be defined in the
 * class. Explicit template instantiation is used to outsource
 * base-class implementations to interface.cpp.
 */
template 
class View {
	inline ViewImpl &
	impl(void)
	{
		return *(ViewImpl *)this;
	}
	class UndoTokenMessage : public UndoTokenWithSize {
		ViewImpl &view;
		unsigned int iMessage;
		uptr_t wParam;
		sptr_t lParam;
	public:
		UndoTokenMessage(ViewImpl &_view, unsigned int _iMessage,
				 uptr_t _wParam = 0, sptr_t _lParam = 0)
				: view(_view), iMessage(_iMessage),
				  wParam(_wParam), lParam(_lParam) {}
		void
		run(void)
		{
			view.ssm(iMessage, wParam, lParam);
		}
	};
	class UndoTokenSetRepresentations : public
	      UndoTokenWithSize {
		ViewImpl &view;
	public:
		UndoTokenSetRepresentations(ViewImpl &_view)
		                           : view(_view) {}
		void
		run(void)
		{
			view.set_representations();
		}
	};
public:
	/*
	 * called after Interface initialization.
	 * Should call setup()
	 */
	inline void
	initialize(void)
	{
		impl().initialize_impl();
	}
	inline sptr_t
	ssm(unsigned int iMessage,
	    uptr_t wParam = 0, sptr_t lParam = 0)
	{
		return impl().ssm_impl(iMessage, wParam, lParam);
	}
	inline void
	undo_ssm(unsigned int iMessage,
		 uptr_t wParam = 0, sptr_t lParam = 0)
	{
		undo.push(new UndoTokenMessage(impl(), iMessage, wParam, lParam));
	}
	void set_representations(void);
	inline void
	undo_set_representations(void)
	{
		undo.push(new UndoTokenSetRepresentations(impl()));
	}
	inline void
	set_scintilla_undo(bool state)
	{
		ssm(SCI_EMPTYUNDOBUFFER);
		ssm(SCI_SETUNDOCOLLECTION, state);
	}
protected:
	void setup(void);
};
/**
 * Base class and interface of all SciTECO user interfaces
 * (e.g. Curses or GTK+).
 *
 * This uses the same Curiously Recurring Template Pattern (CRTP)
 * as the View interface above, as there is only one type of
 * user interface at runtime.
 */
template 
class Interface {
	inline InterfaceImpl &
	impl(void)
	{
		return *(InterfaceImpl *)this;
	}
	class UndoTokenShowView : public UndoTokenWithSize {
		ViewImpl *view;
	public:
		UndoTokenShowView(ViewImpl *_view)
		                 : view(_view) {}
		void run(void);
	};
	template 
	class UndoTokenInfoUpdate : public UndoTokenWithSize< UndoTokenInfoUpdate > {
		const Type *obj;
	public:
		UndoTokenInfoUpdate(const Type *_obj)
				   : obj(_obj) {}
		void run(void);
	};
protected:
	ViewImpl *current_view;
public:
	Interface() : current_view(NULL) {}
	/* default implementation */
	inline GOptionGroup *
	get_options(void)
	{
		return NULL;
	}
	/* expected to initialize Scintilla */
	inline void
	main(int &argc, char **&argv)
	{
		impl().main_impl(argc, argv);
	}
	enum MessageType {
		MSG_USER,
		MSG_INFO,
		MSG_WARNING,
		MSG_ERROR
	};
	inline void
	vmsg(MessageType type, const gchar *fmt, va_list ap)
	{
		impl().vmsg_impl(type, fmt, ap);
	}
	inline void
	msg(MessageType type, const gchar *fmt, ...) G_GNUC_PRINTF(3, 4)
	{
		va_list ap;
		va_start(ap, fmt);
		vmsg(type, fmt, ap);
		va_end(ap);
	}
	/* default implementation */
	inline void msg_clear(void) {}
	inline void
	show_view(ViewImpl *view)
	{
		impl().show_view_impl(view);
	}
	inline void
	undo_show_view(ViewImpl *view)
	{
		undo.push(new UndoTokenShowView(view));
	}
	inline ViewImpl *
	get_current_view(void)
	{
		return current_view;
	}
	inline sptr_t
	ssm(unsigned int iMessage, uptr_t wParam = 0, sptr_t lParam = 0)
	{
		return current_view->ssm(iMessage, wParam, lParam);
	}
	inline void
	undo_ssm(unsigned int iMessage,
	         uptr_t wParam = 0, sptr_t lParam = 0)
	{
		current_view->undo_ssm(iMessage, wParam, lParam);
	}
	/*
	 * NOTE: could be rolled into a template, but
	 * this way it is explicit what must be implemented
	 * by the deriving class.
	 */
	inline void
	info_update(const QRegister *reg)
	{
		impl().info_update_impl(reg);
	}
	inline void
	info_update(const Buffer *buffer)
	{
		impl().info_update_impl(buffer);
	}
	inline void
	undo_info_update(const QRegister *reg)
	{
		undo.push(new UndoTokenInfoUpdate(reg));
	}
	inline void
	undo_info_update(const Buffer *buffer)
	{
		undo.push(new UndoTokenInfoUpdate(buffer));
	}
	inline void
	cmdline_update(const Cmdline *cmdline)
	{
		impl().cmdline_update_impl(cmdline);
	}
	enum PopupEntryType {
		POPUP_PLAIN,
		POPUP_FILE,
		POPUP_DIRECTORY
	};
	inline void
	popup_add(PopupEntryType type,
	          const gchar *name, bool highlight = false)
	{
		impl().popup_add_impl(type, name, highlight);
	}
	inline void
	popup_show(void)
	{
		impl().popup_show_impl();
	}
	inline bool
	popup_is_shown(void)
	{
		return impl().popup_is_shown_impl();
	}
	inline void
	popup_clear(void)
	{
		impl().popup_clear_impl();
	}
	/* default implementation */
	inline bool
	is_interrupted(void)
	{
		return sigint_occurred != FALSE;
	}
	/* main entry point */
	inline void
	event_loop(void)
	{
		impl().event_loop_impl();
	}
	/*
	 * Interfacing to the external SciTECO world
	 */
protected:
	void stdio_vmsg(MessageType type, const gchar *fmt, va_list ap);
public:
	void process_notify(SCNotification *notify);
};
} /* namespace SciTECO */
#ifdef INTERFACE_GTK
#include "interface-gtk.h"
#elif defined(INTERFACE_CURSES)
#include "interface-curses.h"
#else
#error No interface selected!
#endif
namespace SciTECO {
/* object defined in main.cpp */
extern InterfaceCurrent interface;
extern template class View;
extern template class Interface;
} /* namespace SciTECO */
#endif