/**
 * Copyright (c) 2011 prissi after code from http://hg.assembla.com/SDL_Clipboard
 *
 * Clipboard functions for copy and paste of text
 *
 * This file is part of the Simutrans project under the artistic licence.
 * (see licence.txt)
 */

#define NOMINMAX 1

#include "SDL.h"
#include "SDL_syswm.h"

#include <string.h>

#include "simsys.h"
#include "simgraph.h"
#include "simdebug.h"
#include "dataobj/translator.h"


// provide fallsback internal clipboard
#define MAX_SIZE (4096)
static char content[MAX_SIZE] = "";
static size_t content_length = 0;



/* Determine what type of clipboard we are using */
#if defined(_WIN32)
	#error "Use clipboard_w32.cc instead!"
#elif defined(X11_SCRAP) || defined(__unix__) \
		|| defined (_BSD_SOURCE) || defined (_SYSV_SOURCE) \
        || defined (__FreeBSD__) || defined (__MACH__) \
        || defined (__OpenBSD__) || defined (__NetBSD__) \
        || defined (__bsdi__) || defined (__svr4__) \
        || defined (bsdi) || defined (__SVR4) \
		&& !defined(__QNXNTO__) \
		&& !defined(OSX_SCRAP) \
		&& !defined(DONT_USE_X11_SCRAP)
    #define X11_SCRAP
#elif defined(__APPLE__) || defined(OSX_SCRAP)
	#error "Do not compile this file for Mac OS X. Use clipboard_OSX.m instead."
#else
	// just use the standard clipboard
	#warning "Unknown window manager for clipboard handling, Will be disabled"
#endif /* scrap type */


static bool is_init_copy_paste = false;
SDL_SysWMinfo info;


void init_copy_paste()
{
	SDL_SysWMinfo info;

	SDL_VERSION(&info.version);
	if( SDL_GetWMInfo(&info) ) {
		/* Save the information for later use */
		if(  info.subsystem == SDL_SYSWM_X11  ) {
			SDL_Display = info.info.x11.display;
			SDL_Window = info.info.x11.window;
			Lock_Display = info.info.x11.lock_func;
			Unlock_Display = info.info.x11.unlock_func;

			/* Enable the special window hook events */
			SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
//			SDL_SetEventFilter(clipboard_filter);

			retval = 0;
			is_init_copy_paste = true;
		}
		else {
			dbg->error( "init_copy_paste()", "not running on X11" );
		}
	}
}
	  
/**
 * Copy text to the clipboard
 * @param source : pointer to the start of the source text
 * @param length : number of character bytes to copy
 */
void dr_copy(const char *source, size_t length)
{
	if(  !is_init_copy_paste  ) {
		init_copy_paste();
	}

	if(  !is_init_copy_paste  ) {
		// fallback: internal clipboard
		tstrncpy( content, source, min(length,MAX_SIZE-1) );
	}
	else {
		// X11 copy
		Lock_Display();
		XChangeProperty( SDL_Display, DefaultRootWindow(SDL_Display), XA_CUT_BUFFER0, XA_STRING, 8, PropModeReplace, source, lenght );
		if(  XGetSelectionOwner(SDL_Display, XA_PRIMARY) != SDL_Window  ) {
			XSetSelectionOwner(SDL_Display, XA_PRIMARY, SDL_Window, CurrentTime);
		}
		Unlock_Display();
	}
}


/**
 * Paste text from the clipboard
 * @param target : pointer to the insertion position in the target text
 * @param max_length : maximum number of character bytes which could be inserted
 * @return number of character bytes actually inserted -> for cursor advancing
 */
size_t dr_paste(char *target, size_t max_length)
{
	if(  !is_init_copy_paste  ) {
		init_copy_paste();
	}

	if(  !is_init_copy_paste  ) {
		// fallback: internal clipboard

		// determine the number of bytes to be pasted
		if(  content_length<=max_length  ) {
			max_length = content_length;
		}
		else {
			// ensure that max_length aligns with logical character boundaries of clipboard content
			size_t tmp_length = 0;
			size_t byte_count;
			while(  tmp_length+(byte_count=get_next_char(content+tmp_length,0u))<=max_length  ) {
				tmp_length += byte_count;
			}
			max_length = tmp_length;
		}
		const size_t inserted_length = max_length;

		// move the text to free up space for inserting the clipboard content
		char *target_old_end = target + strlen(target);
		char *target_new_end = target_old_end + max_length;
		while(  target_old_end>=target  ) {
			*target_new_end-- = *target_old_end--;
		}

		// insert the clipboard content
		const char *content_ptr = content;
		while(  (max_length--)>0  ) {
			*target++ = *content_ptr++;
		}

		// return the inserted length for cursor advancing
		return inserted_length;
	}
	else {
		Window owner;
		Atom selection;
		Atom seln_type;
		int seln_format;
		unsigned long nbytes;
		unsigned long overflow;
		char *src;

		Lock_Display();
		owner = XGetSelectionOwner(SDL_Display, XA_PRIMARY);
		Unlock_Display();
		if( (owner == None) || (owner == SDL_Window) ) {
			owner = DefaultRootWindow(SDL_Display);
			selection = XA_CUT_BUFFER0;
		}
		else
		{
			int selection_response = 0;
			SDL_Event event;

			owner = SDL_Window;
			Lock_Display();
			selection = XInternAtom(SDL_Display, "SDL_SELECTION", False);
			XConvertSelection(SDL_Display, XA_PRIMARY, XA_STRING, selection, owner, CurrentTime);
			Unlock_Display();
			while( ! selection_response ) {
				SDL_WaitEvent(&event);
				if ( event.type == SDL_SYSWMEVENT ) {
					XEvent xevent = event.syswm.msg->event.xevent;
					if(  (xevent.type == SelectionNotify)  &&  (xevent.xselection.requestor == owner) ) {
						selection_response = 1;
					}
				}
			}
		}
		Lock_Display();
		if(  XGetWindowProperty(SDL_Display, owner, selection, 0, INT_MAX/4, False, format, &seln_type, &seln_format, &nbytes, &overflow, (unsigned char **)&src) == Success ) {
			inserted_length = min( nbytes, max_length );
			if ( seln_type == XA_STRING  &&  inserted_length>0  ) {
				tsrcnpy( target, src, inserted_length );
			}
			XFree(src);
		}
		Unlock_Display();
	}

	// return the inserted length for cursor advancing
	return inserted_length;
}
