#include <ctype.h>
#include <string.h>
#include "prmpt_diag.h"
#include <IV-look/field.h>
#include <InterViews/target.h>
#include <InterViews/window.h>
#include <InterViews/layout.h>
#include <IV-look/kit.h>
#include <OS/string.h>
#include "slist.h"
#include "cgidbg.h"
#include "dspguicom.h"
#include "mkstr.h"
#include "dsp_app.h"
#include "xdrv.h"

implementActionCallback(PromptImpl)

implementActionCallback(InformationImpl)

declareFieldEditorCallback(PromptImpl)

implementFieldEditorCallback(PromptImpl)


void PromptImpl::cancel()
{
	// LogOut << "PromptImpl::cancel()\n";
	static char buf[2] ;
	buf[0] = '\03' ; // control C
	buf[1] = '\0' ;
	if (prompt) (prompt->GetClient())(buf,prompt->GetType(),
		prompt->GetObject());
	dismiss(false);
}

void InformationImpl::ok()
{
	dismiss(true);
}

static const char * find_dot_suffix(const char * def)
{
	if (!def) return 0 ;
	if (!*def) return 0 ;
	int len = strlen(def) ;
	for (const char * pt = def+len-1; pt > def;pt++) if (*pt == '.') break ;
	if (def + len - pt > 2) return pt ;
	return 0 ;
}

void PromptImpl::dismiss(boolean accept)
{
	ivDialog::dismiss(accept);
	DspApplication::prompt_active(0);
}


void PromptImpl::use_file_chooser()
{
	dismiss(true);
	const char * pattern = "*" ;
	char * delete_pattern = 0 ;
	const char * tail = find_dot_suffix(response_default);
	if (tail) pattern = delete_pattern = Concatenate("*",tail);
	char * name = (char *) DspApplication::get_file_name(buf,
		response_default,pattern,win);
	const char * str = "\03" ;
	if (name) if (*name) str = name ;
	if (prompt) (prompt->GetClient())(str,prompt->GetType(),
        prompt->GetObject());
	delete delete_pattern ;
}

void PromptImpl::accept_string()
{
	const char * str = editor->text()->string() ;
	const limit = 60 ;
	static char * buf = 0 ;
	static const char * warn[3] ;
	int ix = 0 ;
	warn[ix] = 0 ;
	if (strlen(str) > limit) {
		if (!buf) buf = new char[limit+1] ;
		strncpy(buf,str,limit);
		buf[limit] = '\0' ;
		str = buf ;
		*Output + OutputCppHelp <<
            "Warning: input string truncated to " << limit << " characters.\n" ;
		warn[ix++] = "Input string is too long, it has been truncated.\n";
	}
	for (const char *  pt = str; *pt; pt++) if (*pt == '"') {
		if (!buf) buf = new char[limit+1] ;
		if (str != buf) strcpy(buf,str);
		str = buf ;
		int quotes = 0 ;
		for (char *  pt = buf; *pt; pt++) if (*pt == '"') *pt = ' ',quotes++;

		if (quotes == 1) {
			*Output + OutputCppHelp <<
				"Warning: quote mark (\") replaced with blank in input string.\n" ;
		}
		else {
			*Output + OutputCppHelp << "Warning: " << quotes <<
				" quote marks (\") replaced with blanks in input string.\n" ;
		}
		warn[ix++] =
			"One of more quote marks (\") replaced with blank in input string.";
		break ;
	}
		
/*
 *	LogOut << "PromptImpl::accept_string string is " << (void *)
 *		str << ", `" << str << "'\n" ;
 */
	if (prompt) (prompt->GetClient())(str,prompt->GetType(),
		prompt->GetObject());
	if (warn[0]) {
		warn[ix++] = 0 ;
		DspApplication::warn_info_window(1.5*4*72.0,72.,warn);
	}
	dismiss(true);
}

void PromptImpl::accept_editor(ivFieldEditor*)
{
	accept_string();
}

void PromptImpl::cancel_editor(ivFieldEditor*)
{
	cancel();
}


static long strings_too_short(StringList * strings)
{
	long total = 0 ;
	const limit = 10 ;
	if (strings) {
		StringListIterator Next(*strings);
		const char * ck;
		while (ck=Next()) {
			total+= strlen(ck) ;
		}
	}
	if (total <= limit) {
		TheLog << "****************run_dialog too small\n" ;
		return 1 ;
	}
	return 0 ;
}

int InformationImpl::run_dialog(StringList * strings, Window * win)
{
	if (strings_too_short(strings)) return 0 ;
	ivGlyph * glyph = layout().vbox();	
	char * str ;
    if (strings) while (str = strings->Get()) {
		// LogOut << "Removing control from `" << str << "'\n" ;
		DspApplication::remove_control(str);
		if (str) if (*str) glyph->append(layout().rmargin(kit().fancy_label(
			str), 5.0, fil, 0.0));
        delete str ;
    }
	glyph->append(layout().vglue(15.0, 0.0, 12.0));
	osString ok_string("OK");

	glyph->append(layout().hbox(
		layout().hglue(10.0),
		layout().vcenter(kit().default_button(ok_string, accept)),
		layout().hglue(10.0)
	)
	);
	
	ivMonoGlyph * mono = layout().back(
        layout().vcenter(kit().outset_frame(layout().margin(glyph, 5.0)), 1.0),
        new ivTarget(0, TargetPrimitiveHit));
	body(mono);
    if (!win) win = root_view.get_active()->get_window();
	return post_for(win);
}

static const char * find_default(StringList& str)
{
	const max_default_len = 80 ;
	static char ret[max_default_len+1] ;
	for (int i = 0 ; i < max_default_len+1 ;i++) ret[i] = '\0' ;

	const char * check_string = DspGuiCommon::default_prefix ;
	// const char * check_string = "Type RETURN to select the default value (" ;
	const char * terminate = DspGuiCommon::default_suffix ;
	// const char * terminate = ") for `X' or" ;
	int check_len = strlen(check_string);
	int terminate_len = strlen(terminate) ;
	int total_len = check_len + terminate_len ;

	// LogOut << "check_string = " << check_string << "\n" ;
	// LogOut << "terminate = " << terminate << "\n" ;
	StringListIterator Next(str);
	int done = 0 ;
	const char * str_elt ;
	while (str_elt = Next()) {
		for (const char * ck = str_elt; *ck; ck++) {
			if (strlen(ck) < total_len) break ;
			if (!strncmp(ck,check_string,check_len)) {
				done = 1 ;
				const char *cp_begin = ck + check_len ;
				for (const char * cp_end = cp_begin ; *cp_end; cp_end++)
					if (*cp_end == ')') break ;
				if (!*cp_end) break ;
				int length = cp_end - cp_begin ;
				if (length < 1) break ;
				if (length > max_default_len) break ;
				strncpy(ret,cp_begin,length) ;
				break;
			}
		}
		if (done) break ;
	}
	// LogOut << "find_default returning `" << ret << "'\n" ;
	return ret ;
}

int PromptImpl::run_dialog(UserPrompt* pr,StringList& strings,
	const char * abort, Window *wind)
{
	if (strings_too_short(&strings)) return  0 ;
	prompt = pr ;
	abort_message = abort ;

	response_default = prompt->default_string();
	if (!response_default) response_default = find_default(strings);
	editor->field(response_default);
	int length = 0;
	if (response_default) length = strlen(response_default);
	if (length) editor->select(0,length);

	ivGlyph * glyph = layout().vbox();	
	char * str ;
	int found_in = 0 ;
	int found_file = 0  ;
	buf[0] = '\0' ;
    while (str = strings.Get()) {
		DspApplication::remove_control(str);
		if (str) if (*str) {
			if (strstr(str,"FileName")) found_file = 1 ;
			if (strstr(str,"read") || strstr(str,"input")) found_in = 1 ;
			glyph->append(layout().rmargin(kit().fancy_label(
				str), 5.0, fil, 0.0));
			if (found_file && found_in && !buf[0]) {
				strncpy(buf,str,max_buf);
				buf[max_buf] = '\0' ;
			}
		}
        delete str ;
    }

	osString use_file_chooser = "Use file chooser?" ;
	Glyph * chooser = layout().hglue();
	if (found_in && found_file) {
		ivAction * accept = new ActionCallback(PromptImpl)(
        	this, &PromptImpl::use_file_chooser);
		chooser = layout().hbox(
			layout().hglue(10.0),
    	   	layout().vcenter(kit().default_button(use_file_chooser, accept)),
       		layout().hglue(10.0)
		);
	}
	glyph->append(layout().vglue(15.0, 0.0, 12.0));
	

	ivMonoGlyph * mono = layout().back(
        layout().vcenter(
			kit().outset_frame(
				layout().vbox(
					layout().margin(
						layout().vbox(
							glyph, layout().vglue(5.0) ,editor
						),5.0
					),chooser,layout().vspace(10)
				)
			), 1.0
		), new ivTarget(0, TargetPrimitiveHit)
	);
	body(mono);
	win = wind ;
	if (!win) win = root_view.get_menu_window();
	remove_all_input_handlers() ;
	append_input_handler(editor);
    focus(editor);
	DspApplication::prompt_active(1);
	return post_for(win);
}


PromptImpl::PromptImpl(RootMenuView& view, ivStyle& s):
		root_view(view),
		prompt(0),
		abort_message(0),
		ivDialog(0,&s),
		response_default(0)
{
	ivResource::ref(&s);
	editor = ivDialogKit::instance()->field_editor("", style(),
		new FieldEditorCallback(PromptImpl)(
		this, &PromptImpl::accept_editor,
		&PromptImpl::cancel_editor
		)
	);
	ivResource::ref(editor);

	ivGlyph * glyph = layout().vbox();	
    osString caption("");
    style()->find_attribute("caption", caption);
	if (caption.length()) glyph->append(layout().rmargin(kit().fancy_label(
		caption), 5.0, fil, 0.0));
	glyph->append(layout().vglue(5.0, 0.0, 2.0));
	glyph->append(editor);
	glyph->append(layout().vglue(15.0, 0.0, 12.0));

	ivAction * accept = new ActionCallback(PromptImpl)(
	 	this, &PromptImpl::accept_string);

	ivAction * cancel = new ActionCallback(PromptImpl)(
		this, &PromptImpl::cancel);

	osString accept_curr("OK");
	osString cancel_this("Cancel");

	glyph->append(layout().hbox(
		layout().hglue(10.0),
		layout().vcenter(kit().default_button(accept_curr, accept)),
		layout().hglue(10.0, 0.0, 5.0),
		layout().vcenter(kit().push_button(cancel_this, cancel)),
		layout().hglue(10.0)
	)
	);
	
	ivMonoGlyph * mono = layout().back(
        layout().vcenter(kit().outset_frame(layout().margin(glyph, 5.0)), 1.0),
        new ivTarget(0, TargetPrimitiveHit));
	body(mono);

}

InformationImpl::~InformationImpl()
{
	ivResource::unref(style());
}

PromptImpl::~PromptImpl()
{
	ivResource::unref(style());
}

InformationImpl::InformationImpl(RootMenuView& view, ivStyle& s):
		root_view(view),
		ivDialog(0,&s)
{
	ivResource::ref(&s);
	accept = new ActionCallback(InformationImpl)(
	 	this, &InformationImpl::ok);
}


