// These headers are not all needed but I had a hard time figuring out
// the header structure when there is an interface to X-windows so I just
// copided the headers in an existing application that compiled correctly
#include "help.h"
#include <InterViews/bitmap.h>
#include <InterViews/canvas.h>
#include <InterViews/color.h>
#include <InterViews/cursor.h>
#include <InterViews/display.h>
#include <InterViews/event.h>
#include <InterViews/handler.h>
#include <InterViews/glyph.h>
#include <InterViews/hit.h>
#include <InterViews/selection.h>
#include <InterViews/session.h>
#include <InterViews/style.h>
#include <InterViews/window.h>
#include <xdrv.h>
#include <IV-X11/Xlib.h>
#include <IV-X11/Xutil.h>
#include <IV-X11/Xext.h>
#include <IV-X11/xbitmap.h>
#include <IV-X11/xcanvas.h>
#include <IV-X11/xcursor.h>
/* #include <IV-X11/xdisplay.h> */
#include <IV-X11/xevent.h>
#include <IV-X11/xselection.h>
#include <IV-X11/xwindow.h>
#include <OS/host.h>
#include <OS/list.h>
#include <OS/math.h>
#include <OS/string.h>
#include <OS/table.h>
#include <X11/Xatom.h>

#include <InterViews/window.h>
#include <InterViews/enter-scope.h>
#include <string.h>
#include <strstream.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include "mkstr.h"
#include "slist.h"
#include "com_input.h"
#include "cgidbg.h"
#include "dsp_app.h"
#include "environ.h"
#include "xk_tab.h"
#include "playback.h"
#include "genmenu.h"


// following must be compatible with definitons in xk_tab.h 
const exit_window_action = XK_built_in_ ;
const print_window_action = exit_window_action+1 ;
const print_window_raise_action = print_window_action + 1 ;
const print_display_action = print_window_raise_action + 1 ;
const last_generic_action = print_display_action  ;
typedef FocusSelector::generic_action compiler_bug ;
compiler_bug FocusSelector::the_generic_actions[]  = {
    &FocusSelector::exit_window,
    &FocusSelector::print_window,
    &FocusSelector::print_window_raise,
    &FocusSelector::print_display
};



void FocusSelector::window(ivWindow * win, MenuKeyboard * key, int del)
{
	prompt_window = win ;
	deletable = del ;
	if (key) {
		the_window_name = key->name();
		return ;
	}
	// if menu keyboard removal is from there
	removed_flag = 0 ;
	// Following is a kludge we should change the structure of how the
	// information window contents is updated
	if (!strcmp(DspApplication::info_window,window_name())) {
		removed_flag = 1 ;
		the_window_name = DspApplication::info_window,window_name();
		return ;
			
	}
	the_window_name = ManagedKeyboards::manager()->add(this);
}

/*
 * static void print_window_info(ivWindow *win)
 * {
 *	TheLog << "Window is " << (void *) win << " : " ;
 *	if (!win) return ;
 *	osString temp ;
 *	ivStyle * style = win->style();
 *	if (style) if (style->find_attribute("name",temp)) TheLog << temp.string(); 
 *	TheLog << "\n" ;
 * }
 */


FocusSelector::~FocusSelector()
{
	// LogOut << "~FocusSelector " << (void *) this << "\n" ;
	// if (window_name()) LogOut << "`" << window_name() << ".\n" ;

	if (!prompt_window) return ;
	if (!removed_flag) ManagedKeyboards::manager()->remove(this);
	removed_flag = 1 ;
}

void FocusSelector::exit_window()
{
	 if (prompt_window && deletable) {
		// LogOut << "FocusSelector::exit_window\n" ;
		// if (window_name()) LogOut << "`" << window_name() << ".\n" ;
		if (window_name())	DspApplication::record_action(exit_window_action,
			generic_action_name,name());
		if (!removed_flag) ManagedKeyboards::manager()->remove(this);
		removed_flag = 1 ;
		DspApplication::root_window()->set_window_to_unmap(prompt_window);
		return ;
	}
	HelpOut << "This window cannot be deleted.\n" ;
	*Output << "You can inconisize it or exit the application.\n" ;
}


static int is_legal_file_name(const char * name)
{
	if (!name) return 0 ;
	for (const char * pt = name; *pt ; pt++) if (!isgraph(*pt)) return 0 ;
	return 1 ;
}

static char * make_file_name(const char * name)
{
	if (!name) return 0 ;
	const char * suf = ".xwd" ;
	char * ret = new char[strlen(name)+strlen(suf)+1];
	char * dest = ret ;
    for (const char * pt = name; *pt ; pt++) if (isgraph(*pt)) *dest++ = *pt ;
		else *dest++ = '_' ;
	if (dest - ret < 4) {
		delete dest ;
		return 0 ;
	}
	*dest = 0 ;
	strcat(dest,suf);
	return ret ;
}

static void print_window(ivWindow * win, StringList * list =0) ;

const char * FocusSelector::printing_window = 0 ;

int FocusSelector::currently_printing(int warn)
{
	if (!printing_window) return 0 ;
	if (warn) {
		StringList strings ;
		strings.Append(Concatenate("A window is being printed as"));
		strings.Append(Concatenate("`",printing_window,"'."));
		strings.Append(Concatenate(
			"Most user input is disallowed until this has completed."));
		DspApplication::information(strings,DspApplication::menu_window(),1);
	}
	return 1 ;
}


void do_print_window(const char * msg, InputType, void * sel)
{
	if (!sel) return ;
	FocusSelector * focus = (FocusSelector *) sel ;
	focus->set_prompt(msg);
	FocusSelector::set_printing_window(msg);
	return ;
}


const char * FocusSelector::window_name()
{
	if (the_window_name) return the_window_name ;
	if (prompt_window) {
		osString temp ;
		ivStyle * style = prompt_window->style();
		if (style) if (style->find_attribute("name",temp))
			the_window_name = temp.string();
	}
	return the_window_name ;
}

void FocusSelector::print_window_check_prompt()
{
	if (!prompt_flag) return ;
	if (prompt_message) if (*prompt_message == '\03') {
		HelpOut << "User aborted printing window.\n" ;
		return ;
	}
	if (!is_legal_file_name(prompt_message)) {
		StringList * lst = new StringList ;
		lst->Append(Concatenate("`",prompt_message,"'"));
		lst->Append(Concatenate("is not an allowed file name.\n"));
		lst->Append(Concatenate(
			"The name must contain only nonblank printable characters.\n"));
		print_window_kernel(lst);
		return ;
	}
	const buf_size = 30 ;
	char buf[buf_size] ;
	if (!display_flag) {
		strstream id_num(buf,buf_size);
		for (int i = 0 ; i < buf_size; i++) buf[i]= '\0' ;
		ivWindowRep * rep = 0;
		if (prompt_window) rep = prompt_window->ivWindow::rep();
		if (!rep) {
			HelpOut << "Cannot get window ID to print window.\n" ;
			return ;
		}
		if (raise_flag) if (prompt_window) prompt_window->raise();
		id_num << "0x" << hex << rep->xwindow_ ;
		// LogOut << "id-num is `" << buf << "'.\n" ;
		// LogOut << "id-num is 0x" << hex << rep->xwindow_ << dec << "'.\n" ;
	
	}
	int i ;
	for (i =0;i < 100;i++) while(DspApplication::event_check());
    sleep(1);
    for (i =0;i < 100;i++) while(DspApplication::event_check());
    sleep(1);
    for (i =0;i < 100;i++) while(DspApplication::event_check());
    sleep(1);
    for (i =0;i < 100;i++) while(DspApplication::event_check());
	int child_id = fork();
	if (child_id < 0) {
		char * message = 0 ;
		StringList string ;
		string.Append(Concatenate("Cannot do a fork to save window"));
		string.Append(Concatenate("`",printing_window,"'"));
		string.Append(Concatenate("with `xwd'. Window save aborted."));
		SystemErrorMessage(0,&message);
		string.Append(message);
		DspApplication::information(string,DspApplication::menu_window(),1);
		printing_window = 0 ;
		return ;
	} else if (child_id) {
		for (i =0;i < 100;i++) while(DspApplication::event_check());
		sleep(1);
		for (i =0;i < 100;i++) while(DspApplication::event_check());
		sleep(1);
		for (i =0;i < 100;i++) while(DspApplication::event_check());
		sleep(1);
		for (i =0;i < 100;i++) while(DspApplication::event_check());
		sleep(1);
		printing_window = 0 ;
		return ;
	}
	{ 
		static const char * xwd_name = 0 ;
		if (!xwd_name) {
			const char * x11_bin =  Environment::get("OPD_X_BIN");
			if (!x11_bin) x11_bin = "/usr/bin/X11" ;
			xwd_name = Concatenate(x11_bin,"/xwd");
		}
		if (display_flag) execl(xwd_name,"xwd","-root","-out",prompt_message,0);
		else execl(xwd_name,"xwd","-id",buf,"-out",prompt_message,0);
		exit(0);
	}
}



void FocusSelector::print_window_raise()
{
	// LogOut << "FocusSelector::print_window_raise\n" ;
	if (currently_printing()) return ;
	if (prompt_window) {
		if (window_name())	DspApplication::record_action(print_window_raise_action,
			generic_action_name,name());
		raise_flag = 1 ;
		display_flag = 0 ;
		print_window_kernel();
	}
	else HelpOut << "This window cannot be saved.\n" ;
}
void FocusSelector::print_window()
{
	// LogOut << "FocusSelector::print_window\n" ;
	if (currently_printing()) return ;
	if (prompt_window) {
		if (window_name())	DspApplication::record_action(print_window_action,
			generic_action_name,name());
		raise_flag = 0 ;
		display_flag = 0 ;
		print_window_kernel();
	}
	else HelpOut << "This window cannot be saved.\n" ;
}

void FocusSelector::print_display()
{
	if (currently_printing()) return ;
	raise_flag = 0 ;
	display_flag = 1 ;
	print_window_kernel();
}

void FocusSelector::print_window_kernel(StringList * list)
{
	// LogOut << "FocusSelector::print_window_kernel\n" ;
	if (!list) list = new StringList ;
	StringList& strings = *list ;
	// HelpOut << "XWindow is is 0x" << hex << rep->xwindow_ << dec << "\n" ;
	ivStyle * style = prompt_window->style();
	const char * file_name = 0 ;
	char * delete_name = 0 ;
	const char * win_name = 0 ;
	const char * Specify = 0 ;
	if (display_flag) {
		file_name = "Xdisplay.xwd" ;
		Specify = "Specify file to write `xwd' display image" ;
	} else {
		osString temp ;
		if (win_name = window_name()) {
			delete_name = make_file_name(win_name);
			file_name = delete_name ;
		}
		if (!delete_name) if (style->find_attribute("iconName",temp))
			delete_name = make_file_name(temp.string());
		if (delete_name) file_name = delete_name ;
		else file_name = "xwindow.xwd" ;
		Specify = "Specify file to write `xwd' window image" ;
	}
	if (HelpDo.All()) {
		strings.Append(Concatenate("You will hear two `beeps' if the window is saved correctly."));
		strings.Append(Concatenate("Do not change any windows until after these tones."));
	}
	if (win_name) {
		strings.Append(Concatenate(Specify));
		strings.Append(Concatenate("for `",win_name,"' to:"));
	} else strings.Append(Concatenate(Specify," to:"));
	clear_prompt();
	DspApplication::prompt(strings,::do_print_window,file_name,this,
		prompt_window);
	print_window_check_prompt();
	delete delete_name ;
	delete list ;
}



const char * FocusSelector::generic_action_name  = "generic_window_action" ;

void FocusSelector::generic_window_action(int act, const char * window)
{
/*
 *	LogOut << "FocusSelector::generic_window_action(0x" << hex << act <<
 *		dec << ", " << window << ")\n" ;
 */
	if (act < XK_built_in_ || act > last_generic_action) return ;
	int limit = 100000 ;
	FocusSelector * sel  = 0 ;
	while (!sel && limit--) {
		while(DspApplication::check_processing());
		sel = ManagedKeyboards::manager()->find_focus(window);
	}
	if (!sel) {
		StringList strings ;
		strings.Append(Concatenate("Cannot find window"));
		strings.Append(Concatenate("`",window,"'"));
		strings.Append(Concatenate("Generic window command aborted."));
		DspApplication::information(strings,DspApplication::menu_window(),
			1);
		// LogOut << "FocusSelector::generic_window_action aborted\n" ;
		return ;
	}
	{
		generic_action the_act = the_generic_actions[act - XK_built_in_];
		(sel->*the_act)() ;
		if (act == print_window_action) sleep(3);
		DspApplication::check_wait_for_dsp();
        DspApplication::cond_check_packet_read();
	}
}



