/*
 *  L3afpad - GTK+ based simple text editor
 *  Copyright (C) 2004-2005 Tarot Osuji
 *
 *  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 2 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, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "l3afpad.h"
#include <gdk/gdkkeysyms.h>
#include <string.h>

static gint keyval;
static gboolean view_scroll_flag = FALSE;

extern GtkClipboard *selection_primary;
gchar *selection_primary_str = NULL;

gint get_current_keyval(void)
{
	return keyval;
}

void clear_current_keyval(void)
{
	keyval = 0;
}
/*
gboolean scroll_to_cursor(GtkTextBuffer *buffer, gdouble within_margin)
{
	GtkTextIter iter;

//	gtk_text_buffer_get_start_iter(buffer, &iter);
	gtk_text_buffer_get_iter_at_mark(buffer, &iter,
		gtk_text_buffer_get_insert(buffer));
	return gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(pub->mw->view),
		&iter, within_margin, FALSE, 0.5, 0.5);
}
*/
void scroll_to_cursor(GtkTextBuffer *buffer, gdouble within_margin)
{
	gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(pub->mw->view),
		gtk_text_buffer_get_insert(buffer),
		within_margin, FALSE, 0, 0);
}

gint check_text_modification(void)
{
	gchar *basename, *str;
	gint res;

	if (gtk_text_buffer_get_modified(pub->mw->buffer)) {
		basename = get_file_basename(pub->fi->filename, FALSE);
		str = g_strdup_printf(_("Save changes to '%s'?"), basename);
		g_free(basename);
		res = run_dialog_message_question(pub->mw->window, str);
		g_free(str);
		switch (res) {
		case GTK_RESPONSE_NO:
			return 0;
		case GTK_RESPONSE_YES:
			if (!on_file_save())
				return 0;
		}
		return -1;
	}

	return 0;
}

#if 0
static gint check_preedit(GtkWidget *view)
{
	gint cursor_pos;

	gtk_im_context_get_preedit_string(
		GTK_TEXT_VIEW(view)->im_context, NULL, NULL, &cursor_pos);

	return cursor_pos;
}
#endif

static gboolean check_selection_bound(GtkTextBuffer *buffer)
{
	GtkTextIter start, end;
	gchar *str, *p;

	if (gtk_text_buffer_get_selection_bounds(buffer, &start, &end)) {
		str = gtk_text_iter_get_text(&start, &end);
		p = strchr(str, '\n');
		g_free(str);
		if (p)
			return TRUE;
	}

	return FALSE;
}

static gboolean cb_key_press_event(GtkWidget *view, GdkEventKey *event)
{
	GtkTextBuffer *buffer;
	GtkTextMark *mark;
	GtkTextIter iter;
	GdkRectangle prev_rect;

#if 0
	if (check_preedit(view))
		return FALSE;
#endif

	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
	mark = gtk_text_buffer_get_insert(buffer);
	gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
	gtk_text_view_get_iter_location(GTK_TEXT_VIEW(view), &iter, &prev_rect);

	keyval = 0;
//g_print("key-press-event: 0x%X\n", event->keyval);
	switch (event->keyval) {
	case GDK_Up:		// Try [Shift]+[Down]. it works bad.
	case GDK_Down:
		if (gtk_text_view_move_mark_onscreen(GTK_TEXT_VIEW(view), mark)) {
			GdkRectangle iter_rect;
			gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
			gtk_text_view_get_iter_location(GTK_TEXT_VIEW(view), &iter, &iter_rect);
			if (iter_rect.y < prev_rect.y) {
				gtk_text_view_get_line_at_y(GTK_TEXT_VIEW(view), &iter,
					iter_rect.y - iter_rect.height, NULL);
				gtk_text_buffer_move_mark(buffer, mark, &iter);
			}
			if (!(event->state & GDK_SHIFT_MASK)) {
				gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
				gtk_text_buffer_place_cursor(buffer, &iter);
			}
			return TRUE;
		}
		break;
	case GDK_Page_Up:
	case GDK_Page_Down:
		if (gtk_text_view_move_mark_onscreen(GTK_TEXT_VIEW(view), mark)) {
			GdkRectangle visible_rect, iter_rect;
			gint pos = 0;
			gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(view), &visible_rect);
			gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
			gtk_text_view_get_iter_location(GTK_TEXT_VIEW(view), &iter, &iter_rect);
			if (iter_rect.y < prev_rect.y)
				pos = 1;
			if (event->keyval == GDK_Page_Up)
				gtk_text_view_get_line_at_y(GTK_TEXT_VIEW(view), &iter,
					iter_rect.y - visible_rect.height + iter_rect.height, NULL);
			else
				gtk_text_view_get_line_at_y(GTK_TEXT_VIEW(view), &iter,
					iter_rect.y + visible_rect.height - iter_rect.height, NULL);
			gtk_text_buffer_move_mark(buffer, mark, &iter);
			gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(view),
				mark, 0, TRUE, 0, pos);
			if (!(event->state & GDK_SHIFT_MASK)) {
				gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
				gtk_text_buffer_place_cursor(gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)), &iter);
			}
			return TRUE;
		}
		break;
	case GDK_Return:
		if (indent_get_state()) {
			indent_real(view);
			return TRUE;
		}
		break;
	case GDK_Tab:
		if (event->state & GDK_CONTROL_MASK) {
			indent_toggle_tab_width(view);
			return TRUE;
		}
	case GDK_ISO_Left_Tab:
		if (event->state & GDK_SHIFT_MASK)
			indent_multi_line_unindent(gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)));
		else if (!check_selection_bound(gtk_text_view_get_buffer(GTK_TEXT_VIEW(view))))
			break;
		else
			indent_multi_line_indent(gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)));
		return TRUE;
	}
	keyval = event->keyval;
	if ((event->state & GDK_CONTROL_MASK)
		|| (event->keyval == GDK_Control_L)
		|| (event->keyval == GDK_Control_R)) {
		keyval = keyval + 0x10000;
//g_print("=================================================\n");
	}

	return FALSE;
}

static gboolean cb_button_press_event(GtkWidget *view, GdkEventButton *event)
{
	GtkTextIter iter, start, end;
	gint x, y;

	GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));

	if ((event->button) == 3 && (event->type == GDK_BUTTON_PRESS)) {
		gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(view),
			gtk_text_view_get_window_type(GTK_TEXT_VIEW(view), event->window),
			(gint)event->x, (gint)event->y, &x, &y);
		gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(view), &iter, x, y);
		gtk_text_buffer_get_selection_bounds(buffer, &start, &end);
		if (!gtk_text_iter_in_range(&iter, &start, &end))
			gtk_text_buffer_place_cursor(buffer, &iter);
	}

	// backup and restore the clipboard
	gchar *current_clipboard_str = gtk_clipboard_wait_for_text(selection_primary);
	if (((current_clipboard_str == NULL) || (current_clipboard_str[0]=='\0')) &&
	    selection_primary_str && selection_primary_str[0]) {
		gtk_clipboard_set_text(selection_primary, selection_primary_str, -1);
	}
	else {
		g_free(selection_primary_str);
		selection_primary_str = g_strdup(current_clipboard_str);
	}

	return FALSE;
}

static void cb_modified_changed(GtkTextBuffer *buffer, GtkWidget *view)
{
	gboolean modified_flag, exist_flag = FALSE;
	gchar *filename, *title;

	modified_flag = gtk_text_buffer_get_modified(buffer);
	filename = get_file_basename(pub->fi->filename, TRUE);
	if (modified_flag)
		title = g_strconcat("*", filename, NULL);
	else {
		title = g_strdup(filename);
		undo_reset_modified_step(buffer);
	}
	g_free(filename);
	gtk_window_set_title(GTK_WINDOW(gtk_widget_get_toplevel(view)), title);
	g_free(title);
	if (pub->fi->filename)
		exist_flag = g_file_test(
			g_filename_to_utf8(pub->fi->filename, -1, NULL, NULL, NULL),
			G_FILE_TEST_EXISTS);
	menu_sensitivity_from_modified_flag(modified_flag || !exist_flag);
}

void force_call_cb_modified_changed(GtkWidget *view)
{
	cb_modified_changed(gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)), view);
}

void force_block_cb_modified_changed(GtkWidget *view)
{
	g_signal_handlers_block_by_func(G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(view))),
		G_CALLBACK(cb_modified_changed), view);
}

void force_unblock_cb_modified_changed(GtkWidget *view)
{
	g_signal_handlers_unblock_by_func(G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(view))),
		G_CALLBACK(cb_modified_changed), view);
}
/*
static void cb_mark_set(GtkTextBuffer *buffer, GtkTextIter *iter, GtkTextMark *mark)
{
	if (gtk_text_mark_get_name(mark))
{g_print(gtk_text_mark_get_name(mark));
}else g_print("|");
		menu_sensitivity_from_selection_bound(
			gtk_text_buffer_get_selection_bounds(buffer, NULL, NULL));
}
*/
static void cb_mark_changed(GtkTextBuffer *buffer)
{
	menu_sensitivity_from_selection_bound(
		gtk_text_buffer_get_selection_bounds(buffer, NULL, NULL));
}

static void cb_focus_event(GtkWidget *view, GdkEventFocus *event)
{
	if (!gtk_text_buffer_get_selection_bounds(gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)), NULL, NULL))
		gtk_text_mark_set_visible(
			gtk_text_buffer_get_selection_bound(
				gtk_text_view_get_buffer(GTK_TEXT_VIEW(view))), !event->in);
	if (event->in)
		menu_sensitivity_from_clipboard();
}
/*
static void cb_begin_user_action(GtkTextBuffer *buffer, GtkWidget *view)
{
	g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
		G_CALLBACK(cb_modified_changed), view);
//	g_print("begin-user-action\n");
}

static void cb_end_user_action(GtkTextBuffer *buffer, GtkWidget *view)
{
	g_signal_handlers_block_by_func(G_OBJECT(buffer),
		G_CALLBACK(cb_modified_changed), view);
	gtk_text_view_scroll_mark_onscreen(		// TODO: require?
		GTK_TEXT_VIEW(view),
		gtk_text_buffer_get_insert(buffer));
//	g_print("end-user-action\n");
}
*//*
static void cb_something(GtkTextBuffer *buffer, gchar *data)
{
	g_print("%s\n", data);
}
*/
void set_view_scroll(void)
{
	view_scroll_flag = TRUE;
}

static void cb_end_user_action(GtkTextBuffer *buffer, GtkWidget *view)
{
	if (view_scroll_flag) {
		gtk_text_view_scroll_mark_onscreen(		// TODO: require?
			GTK_TEXT_VIEW(view),
			gtk_text_buffer_get_insert(buffer));
		view_scroll_flag = FALSE;
	}
}

GtkWidget *create_text_view(void)
{
	GtkWidget *view;
	GtkTextBuffer *buffer;

	view = gtk_text_view_new();
	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));

//	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 1);
//	gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 1);

	g_signal_connect(G_OBJECT(view), "key-press-event",
		G_CALLBACK(cb_key_press_event), NULL);
	g_signal_connect(G_OBJECT(view), "button-press-event",
		G_CALLBACK(cb_button_press_event), NULL);
	g_signal_connect_after(G_OBJECT(view), "cut-clipboard",
		G_CALLBACK(menu_sensitivity_from_clipboard), NULL);
	g_signal_connect_after(G_OBJECT(view), "copy-clipboard",
		G_CALLBACK(menu_sensitivity_from_clipboard), NULL);
	g_signal_connect_after(G_OBJECT(view), "paste-clipboard",
		G_CALLBACK(set_view_scroll),
		gtk_text_buffer_get_insert(buffer));
/*	g_signal_connect_after(G_OBJECT(view), "paste-clipboard",
		G_CALLBACK(gtk_text_view_scroll_mark_onscreen),
		gtk_text_buffer_get_insert(buffer));*/
	g_signal_connect_after(G_OBJECT(view), "focus-in-event",
		G_CALLBACK(cb_focus_event), NULL);
	g_signal_connect_after(G_OBJECT(view), "focus-out-event",
		G_CALLBACK(cb_focus_event), NULL);

	g_signal_connect(G_OBJECT(buffer), "mark-set",
		G_CALLBACK(cb_mark_changed), NULL);
	g_signal_connect(G_OBJECT(buffer), "mark-deleted",
		G_CALLBACK(cb_mark_changed), NULL);
	g_signal_connect(G_OBJECT(buffer), "modified-changed",
		G_CALLBACK(cb_modified_changed), view);
	g_signal_connect_after(G_OBJECT(buffer), "end-user-action",
		G_CALLBACK(cb_end_user_action), view);
/*	g_signal_connect(G_OBJECT(buffer), "begin-user-action",
		G_CALLBACK(cb_begin_user_action), view);
	g_signal_connect_after(G_OBJECT(buffer), "end-user-action",
		G_CALLBACK(cb_end_user_action), view);
	cb_end_user_action(buffer, view);
*/
	linenum_init(view);

	return view;
}
