/**
 *******************************************************************************
 * @file CodeView.cpp
 * @brief Editeur De N'ours : Code Viewer Widget
 *			This is an abstraction
 * @author Edouard DUPIN
 * @date 05/01/2011
 * @par Project
 * Edn
 *
 * @par Copyright
 * Copyright 2010 Edouard DUPIN, all right reserved
 *
 * This software is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY.
 *
 * Licence summary : 
 *    You can modify and redistribute the sources code and binaries.
 *    You can send me the bug-fix
 *    You can not earn money with this Software (if the source extract from Edn
 *        represent less than 50% of original Sources)
 * Term of the licence in in the file licence.txt.
 *
 *******************************************************************************
 */

#include "CodeView.h"

#include "tools_debug.h"
#include "tools_globals.h"
#include "Display.h"
#include "CodeView.h"
#include "BufferManager.h"
#include "ColorizeManager.h"
#include "ClipBoard.h"
#include "SearchData.h"


CodeView::CodeView(void) : MsgBroadcast("Code View", EDN_CAT_WORK_AREA)
{
	// Init link with the buffer Manager
	m_bufferManager = BufferManager::getInstance();
	m_colorManager = ColorizeManager::getInstance();

	static int32_t staticIntLocal = 100;
	m_displayUniqueId = staticIntLocal++;

	m_bufferID = -1;
	m_bufferManager->Get(m_bufferID)->AnchorAdd(m_displayUniqueId);
	m_buttunOneSelected = false;
	m_shawableAreaX = 0;
	m_shawableAreaY = 0;

	m_widget = gtk_drawing_area_new();
	gtk_widget_set_size_request( m_widget, 200, 100);

	gtk_widget_add_events( m_widget,
	                          GDK_KEY_PRESS_MASK
	                        | GDK_BUTTON_PRESS_MASK
	                        | GDK_BUTTON_RELEASE_MASK
	                        | GDK_POINTER_MOTION_MASK
	                        | GDK_POINTER_MOTION_HINT_MASK);
#	ifdef USE_GTK_VERSION_3_0
	g_object_set(m_widget,"can-focus", TRUE, NULL);
#	elif defined( USE_GTK_VERSION_2_0 )
	GTK_WIDGET_SET_FLAGS(m_widget, GTK_CAN_FOCUS);
#	endif
	// Remove double-buffering ==> in the current case we can not get the previous display...
	gtk_widget_set_double_buffered(m_widget, false);
	
	// Focus Event
	g_signal_connect(       G_OBJECT(m_widget), "focus_in_event",       G_CALLBACK(CB_focusGet),         this);
	g_signal_connect(       G_OBJECT(m_widget), "focus_out_event",      G_CALLBACK(CB_focusLost),        this);
	// Keyboard Event
	g_signal_connect_after( G_OBJECT(m_widget), "key_press_event",      G_CALLBACK(CB_keyboardEvent),    this);
	g_signal_connect_after( G_OBJECT(m_widget), "key_release_event",    G_CALLBACK(CB_keyboardEvent),    this);
	// Mouse Event
	g_signal_connect(       G_OBJECT(m_widget), "button_press_event",   G_CALLBACK(CB_mouseButtonEvent), this);
	g_signal_connect(       G_OBJECT(m_widget), "button_release_event", G_CALLBACK(CB_mouseButtonEvent), this);
	g_signal_connect(       G_OBJECT(m_widget), "motion_notify_event",  G_CALLBACK(CB_mouseMotionEvent), this);
	g_signal_connect(       G_OBJECT(m_widget), "scroll-event",         G_CALLBACK(CB_mouseScrollEvent), this);
	// Display Event
	g_signal_connect(       G_OBJECT(m_widget), "realize",              G_CALLBACK(CB_displayInit),      this);
#	ifdef USE_GTK_VERSION_3_0
	g_signal_connect(       G_OBJECT(m_widget), "draw",                 G_CALLBACK(CB_displayDraw),      this);
#	elif defined( USE_GTK_VERSION_2_0 )
	g_signal_connect(       G_OBJECT(m_widget), "expose_event",         G_CALLBACK(CB_displayDraw),      this);
#	endif
}

CodeView::~CodeView(void)
{

}

GtkWidget * CodeView::GetMainWidget(void)
{
	return m_widget;
}


void CodeView::OnMessage(int32_t id, int32_t dataID)
{
	switch (id)
	{
		case EDN_MSG__CURRENT_CHANGE_BUFFER_ID:
			//EDN_INFO("Select a new Buffer ... " << dataID);
			m_bufferManager->Get(m_bufferID)->AnchorRm(m_displayUniqueId);
			m_bufferID = dataID;
			m_bufferManager->Get(m_bufferID)->AnchorAdd(m_displayUniqueId);
			// request the dispplay of the curent Editor
			SendMessage(EDN_MSG__BUFFER_CHANGE_CURRENT, m_bufferID);
			break;
		case EDN_MSG__CURRENT_SAVE:
			SendMessage(EDN_MSG__BUFF_ID_SAVE, m_bufferID);
			break;
		case EDN_MSG__CURRENT_SAVE_AS:
			SendMessage(EDN_MSG__GUI_SHOW_SAVE_AS, m_bufferID);
			break;
		case EDN_MSG__CURRENT_REMOVE_LINE:
			m_bufferManager->Get(m_bufferID)->RemoveLine();
			break;
		case EDN_MSG__CURRENT_SELECT_ALL:
			m_bufferManager->Get(m_bufferID)->SelectAll();
			break;
		case EDN_MSG__CURRENT_UN_SELECT:
			m_bufferManager->Get(m_bufferID)->SelectNone();
			break;
		case EDN_MSG__CURRENT_COPY:
			if (dataID == -1) {
				dataID = COPY_STD;
			}
			m_bufferManager->Get(m_bufferID)->Copy(dataID);
			break;
		case EDN_MSG__CURRENT_CUT:
			if (dataID == -1) {
				dataID = COPY_STD;
			}
			m_bufferManager->Get(m_bufferID)->Cut(dataID);
			break;
		case EDN_MSG__CURRENT_PASTE:
			if (dataID == -1) {
				dataID = COPY_STD;
			}
			m_bufferManager->Get(m_bufferID)->Paste(dataID);
			break;
		case EDN_MSG__CURRENT_FIND_PREVIOUS:
			{
				Edn::String myDataString;
				SearchData::GetSearch(myDataString);
				m_bufferManager->Get(m_bufferID)->Search(myDataString, true, SearchData::GetCase(), SearchData::GetWrap(), SearchData::GetRegExp() );
			}
			break;
		case EDN_MSG__CURRENT_FIND_NEXT:
			{
				Edn::String myDataString;
				SearchData::GetSearch(myDataString);
				m_bufferManager->Get(m_bufferID)->Search(myDataString, false, SearchData::GetCase(), SearchData::GetWrap(), SearchData::GetRegExp() );
			}
			break;
		case EDN_MSG__CURRENT_REPLACE:
			{
				Edn::String myDataString;
				SearchData::GetReplace(myDataString);
				m_bufferManager->Get(m_bufferID)->Replace(myDataString);
			}
			break;
		case EDN_MSG__CURRENT_REPLACE_ALL:
			break;
		case EDN_MSG__CURRENT_CLOSE:
			SendMessage(EDN_MSG__BUFF_ID_CLOSE, m_bufferID);
			break;
		case EDN_MSG__CURRENT_UNDO:
			m_bufferManager->Get(m_bufferID)->Undo();
			break;
		case EDN_MSG__CURRENT_REDO:
			m_bufferManager->Get(m_bufferID)->Redo();
			break;
		case EDN_MSG__CURRENT_GOTO_LINE:
			if (dataID<0) {
				dataID = 0;
			}
			m_bufferManager->Get(m_bufferID)->JumpAtLine(dataID);
			break;
		case EDN_MSG__REFRESH_DISPLAY:
			break;
		case EDN_MSG__CURRENT_SET_CHARSET:
			m_bufferManager->Get(m_bufferID)->SetCharset((charset_te)dataID);
			break;
		case EDN_MSG__USER_DISPLAY_CHANGE:
			// Redraw all the display ... Done under ...
			break;
	}
	// Force redraw of the widget
	gtk_widget_queue_draw_area(m_widget, 0, 0, m_shawableAreaX, m_shawableAreaY);
	
}


#define COUNT_TIME plop

gboolean CodeView::CB_displayDraw( GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
	CodeView * self = reinterpret_cast<CodeView*>(data);

	//EDN_INFO("displayDraw_cb");
#	ifdef USE_GTK_VERSION_3_0
	GtkAllocation allocation;
	gtk_widget_get_allocation(widget, &allocation);
	EDN_INFO("GTK+ request a diplay of : "<< allocation.width <<"px * "<< allocation.height <<"px");
	bool needRedrawAll = false;
	if (self->m_shawableAreaX != allocation.width) {
		needRedrawAll = true;
		self->m_shawableAreaX = allocation.width;
	}
	if (self->m_shawableAreaY != allocation.height) {
		needRedrawAll = true;
		self->m_shawableAreaY = allocation.height;
	}
#	elif defined( USE_GTK_VERSION_2_0)
	EDN_INFO("GTK+ request a diplay of : "<< widget->allocation.width <<"px * "<< widget->allocation.height <<"px");
	bool needRedrawAll = false;
	if (self->m_shawableAreaX != widget->allocation.width) {
		needRedrawAll = true;
		self->m_shawableAreaX = widget->allocation.width;
	}
	if (self->m_shawableAreaY != widget->allocation.height) {
		needRedrawAll = true;
		self->m_shawableAreaY = widget->allocation.height;
	}
#	endif
	EDN_INFO("Edn request a display of : " << self->m_shawableAreaX << "px * "<<  self->m_shawableAreaY<<"px");

	// Get the color Manager :
	ColorizeManager *myColorManager = NULL;
	myColorManager = ColorizeManager::getInstance();
	
	DrawerManager monDrawer(widget, self->m_shawableAreaX, self->m_shawableAreaY);
	Buffer * tmpBuf = self->m_bufferManager->Get(self->m_bufferID);

	// set cursor : 
	/*
	GdkCursor plop;
	plop.GSEAL = GDK_PENCIL;
	gdk_window_set_cursor(gtk_widget_get_window(self->m_widget), &plop);
	*/
	#ifdef COUNT_TIME
		GTimeVal timeStart;
		g_get_current_time(&timeStart);
	#endif
	bufferAnchor_ts anchor;
	
	tmpBuf->AnchorSetSize(self->m_displayUniqueId, self->m_shawableAreaX, self->m_shawableAreaY);
	bool enableToWrite = tmpBuf->AnchorGet(self->m_displayUniqueId, anchor);
	while (true == enableToWrite) {
		tmpBuf->DrawLine(monDrawer, anchor);
		enableToWrite = tmpBuf->AnchorNext(anchor);
	}
	monDrawer.Flush();
	
	// Need to clean the end of windows (sometimes)...
	/*
	if (iii<lineIdEnd+1) {
		int32_t positionY = letterHeight * (iii - m_displayStart.y - 1);
		drawer.Rectangle(myColorManager->Get(COLOR_CODE_BASIC_BG), 0, positionY, drawer.GetWidth(), letterHeight*(lineIdEnd+1-iii) );
	}
	*/
	#ifdef COUNT_TIME
		GTimeVal timeStop;
		g_get_current_time(&timeStop);
		EDN_DEBUG("Display Generation = " << timeStop.tv_usec - timeStart.tv_usec << " micro-s ==> " << (timeStop.tv_usec - timeStart.tv_usec)/1000. << "ms");
	#endif
	return TRUE;
}

// sur : émis lors du premier affichage de la GtkDrawingArea
gboolean CodeView::CB_displayInit( GtkWidget *widget, gpointer data)
{
	CodeView * self = reinterpret_cast<CodeView*>(data);
#	ifdef USE_GTK_VERSION_3_0
	GtkAllocation allocation;
	gtk_widget_get_allocation(widget, &allocation);
	EDN_INFO("GTK+ request a diplay of : "<< allocation.width <<"px * "<< allocation.height <<"px");
	self->m_shawableAreaX = allocation.width;
	self->m_shawableAreaY = allocation.height;
#	elif defined( USE_GTK_VERSION_2_0)
	EDN_INFO("GTK+ request a diplay of : "<< widget->allocation.width <<"px * "<< widget->allocation.height <<"px");
	self->m_shawableAreaX = widget->allocation.width;
	self->m_shawableAreaY = widget->allocation.height;
#	endif
	gtk_widget_queue_draw( widget );
	return TRUE;
}

gint CodeView::CB_focusGet(	GtkWidget *widget, GdkEventFocus *event, gpointer data)
{
	CodeView * self = reinterpret_cast<CodeView*>(data);
#	ifdef USE_GTK_VERSION_2_0
	GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
#	endif
	self->SendMessage(EDN_MSG__BUFFER_CHANGE_CURRENT, self->m_bufferID);
	EDN_INFO("Focus - In");
	return FALSE;
}

gint CodeView::CB_focusLost(	GtkWidget *widget, GdkEventFocus *event, gpointer data)
{
	//CodeView * self = reinterpret_cast<CodeView*>(data);
	
#	ifdef USE_GTK_VERSION_2_0
	GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
#	endif
	EDN_INFO("Focus - out");
	return FALSE;
}

gint CodeView::CB_keyboardEvent(GtkWidget *widget, GdkEventKey *event, gpointer data)
{
	CodeView * self = reinterpret_cast<CodeView*>(data);
	
	char Utf8Out[10];
	bool controlKey;
	bool moveKey;
	int32_t key;
	// Convert input key : 
	ConvertInput(event, Utf8Out, controlKey, moveKey, key);
	
	if(event->type == GDK_KEY_PRESS) {
		if(false==controlKey) {
			self->m_bufferManager->Get(self->m_bufferID)->AddChar(Utf8Out);
			gtk_widget_queue_draw( widget );
		} else if (true == moveKey) {
			self->m_bufferManager->Get(self->m_bufferID)->cursorMove(key);
			gtk_widget_queue_draw( widget );
		}
	}
	return true;
}


gint CodeView::CB_mouseButtonEvent(GtkWidget *widget, GdkEventButton *event, gpointer data)
{

	CodeView * self = reinterpret_cast<CodeView*>(data);
	
	// get focus on the widget
	gtk_widget_grab_focus(widget);
	if (event->button == 1) {
		if (event->type == GDK_BUTTON_PRESS) {
			//EDN_INFO("mouse-event BT1  ==> One Clicked %d, %d", (uint32_t)event->x, (uint32_t)event->y);
			self->m_bufferManager->Get(self->m_bufferID)->MouseEvent(event->x, event->y);
			gtk_widget_queue_draw( widget );
			self->m_buttunOneSelected = true;
		}else if (event->type == GDK_2BUTTON_PRESS) {
			//EDN_INFO("mouse-event BT1  ==> Double Clicked %d, %d", (uint32_t)event->x, (uint32_t)event->y);
			self->m_bufferManager->Get(self->m_bufferID)->MouseEventDouble();
			gtk_widget_queue_draw( widget );
			self->m_buttunOneSelected = true;
		}else if (event->type == GDK_3BUTTON_PRESS) {
			//EDN_INFO("mouse-event BT1  ==> Triple Clicked");
			self->m_bufferManager->Get(self->m_bufferID)->MouseEventTriple();
			gtk_widget_queue_draw( widget );
			self->m_buttunOneSelected = true;
		}else if (event->type == GDK_BUTTON_RELEASE) {
			//EDN_INFO("mouse-event BT1  ==> Realease");
			self->m_buttunOneSelected = false;
			self->m_bufferManager->Get(self->m_bufferID)->Copy(COPY_MIDDLE_BUTTON);
		}
	} else if (event->button == 2) {
		if (event->type == GDK_BUTTON_PRESS) {
			EDN_INFO("mouse-event BT2 PRESS");
			self->m_bufferManager->Get(self->m_bufferID)->MouseEvent(event->x, event->y);
			self->m_bufferManager->Get(self->m_bufferID)->Paste(COPY_MIDDLE_BUTTON);
			gtk_widget_queue_draw(widget);
		}
	} else if (event->button == 3) {
		if (event->type == GDK_BUTTON_PRESS) {
			EDN_INFO("mouse-event BT3 PRESS");
		}
	} else {
		EDN_INFO("mouse-event BT" << event->type <<" PRESS");
	}
	return true;
}



gint CodeView::CB_mouseMotionEvent( GtkWidget *widget, GdkEventMotion *event, gpointer data)
{
	CodeView * self = reinterpret_cast<CodeView*>(data);
	
	if (true == self->m_buttunOneSelected) {
		int x, y;
		GdkModifierType state;

		if (event->is_hint) {
			gdk_window_get_pointer(event->window, &x, &y, &state);
		} else {
			x = event->x;
			y = event->y;
			state = (GdkModifierType)event->state;
		}
		if (x<0) {
			x = 0;
		}
		if (y<0) {
			y = 0;
		}
		//EDN_INFO("mouse-motion BT1 %d, %d", x, y);
		self->m_bufferManager->Get(self->m_bufferID)->MouseSelectFromCursorTo(x, y);
		gtk_widget_queue_draw( widget );
	}
	return true;
}


gint CodeView::CB_mouseScrollEvent( GtkWidget *widget, GdkEventScroll *event, gpointer data)
{
	CodeView * self = reinterpret_cast<CodeView*>(data);
	
	if (event->direction == GDK_SCROLL_UP)
	{
		// up code
		//EDN_INFO("mouse-event GDK_SCROLL_UP");
		self->m_bufferManager->Get(self->m_bufferID)->ScrollUp();
		gtk_widget_queue_draw( widget );
	}
	else if (event->direction == GDK_SCROLL_DOWN)
	{
		// down code
		//EDN_INFO("mouse-event GDK_SCROLL_DOWN");
		self->m_bufferManager->Get(self->m_bufferID)->ScrollDown();
		gtk_widget_queue_draw( widget );
	} else {
		EDN_INFO("mouse-event SCROLL");
	}

	return true;
}





