#include <IV-look/kit.h>
#include <InterViews/event.h>
#include <InterViews/action.h>
#include <InterViews/background.h>
#include <InterViews/layout.h>
#include <InterViews/session.h>
#include <InterViews/color.h>
#include <InterViews/border.h>
#include <InterViews/style.h>
#include <InterViews/window.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <string.h>
#include <ctype.h>
#include "iv_graph.h"
#include "dsp_app.h"
#include "genmenu.h"
#include "cgidbg.h"
#include "mkstr.h"
#include "portable.h"
#include "xk_tab.h"
#include "xdrv.h"
#include "hot_button.h"
#include "playback.h"
#include "help.h"

const max_key_select = 512 ;
class ActionWindowMenuView ;

class WindowMenuView: public WindowMenu {
	void check_release();
	MenuView * the_menu_view ;
	char the_key_selected[max_key_select+1] ;
	int recorded_flag ;
	virtual Action * set_action(PullDownEntry * i) ;
	ActionWindowMenuView * current_action ;
public:
	WindowMenuView(MenuView  * gr):
		recorded_flag(0),
		WindowMenu(),
		the_menu_view(gr){the_key_selected[0]=0;}
	void help_introduction();
	void help_menu_codes();
	void help_menu_database();
	void help_editing();
	void help_abort();
	void help_save();

	void focus_dpp();
	void focus_menu();
	void focus_buttons();
	void create_default();
	void create_instance();

    void select_home();
    void select_end();
    void select_left();
    void select_right();
    void select_up();
    void select_down();
	void select_none();

	void clear_selected();

	void do_command();
	void append_statement_history();
	void command_history();
	void statement_history();
	void cond_help_paragraph();
	void help_paragraph();
	void help_file();

	void peel_off_menu();
	void raise_windows();

	void freeze_dsp();
	void thaw_dsp();
	void print_win();
	void print_win_raise();
	void print_display();

	void play_actions();
	void pause_actions();
	void abort_play();
	void record_actions();
	void record_off();
	void record_flush();

	virtual const char * mouse_selected();
	virtual const char * key_selected();

	virtual void act_select(const char * sel) {the_menu_view->act_select(sel);}
	virtual void act_deselect() {the_menu_view->act_deselect();}
	virtual int direct_act_failed(int32 act, const char * select);
	int test_clear_action_failed()
		{return the_menu_view->test_clear_action_failed();}
	void reset_action(ActionWindowMenuView *act=0);
	void set_action_failed() {the_menu_view->set_action_failed();}

	int recorded() {return recorded_flag;}
	void save_key_select();
	void clear_key_select();
	void record() ;
};

void WindowMenuView::reset_action(ActionWindowMenuView *act)
{
	the_menu_view->reset_action_failed();
	recorded_flag=0;
	current_action = act ;
}

int WindowMenuView::direct_act_failed(int32 act, const char * select)
{
/*
 * LogOut << "WindowMenuView::direct_act_failed(" << hex << act <<
 *		dec << ", " << select << ")\n" ;
 */
	
    DspApplication::check_processing();
	the_menu_view->set_fail_select(select);
    int did = keyboard()->top()->execute(act,1);
	the_menu_view->clear_fail_select();
	if (did) DspApplication::record_action(act,select,keyboard()->name(),
		"direct action failed");
	return did ;
}


void WindowMenuView::save_key_select()
{
	const char * sel = mouse_selected();
	if (!sel) return ;
	if (!*sel) return ;
	strncpy(the_key_selected,sel,max_key_select);
	the_key_selected[max_key_select] = '\0' ;
}

void WindowMenuView::clear_key_select()
{
	the_key_selected[0]  = '\0' ;
}


void WindowMenuView::clear_selected()
{
	if (the_menu_view) the_menu_view->clear_selected();
}

const char * WindowMenuView::key_selected()
{
	const char * ret =  mouse_selected();
	if (!ret) if (the_key_selected[0]) ret = the_key_selected ;
	return ret ;
}

const char * WindowMenuView::mouse_selected()
{
	HotButton * select = the_menu_view->selection();
	if (!select) return 0 ;
	return select->name();
}

void WindowMenuView::check_release()
{
	// LogOut << "WindowMenuView::check_release\n" ;
	the_menu_view->menu_keyboard()->check_release();
	// LogOut << "WindowMenuView::check_release exit\n" ;
}

typedef void (WindowMenuView::*ActionType)();
class ActionWindowMenuView: public RecAction {
	WindowMenuView * gr ;
	ActionType the_action ;
public:
	ActionWindowMenuView(MenuKeyboard * key, WindowMenuView * g,
		PullDownEntry& desc, ActionType a);
	ActionWindowMenuView(ActionType a):RecAction(0,0),the_action(a){}
	void force_execute();
	void execute();
	ActionType action() {return the_action;}
};

void ActionWindowMenuView::force_execute()
{
	gr->reset_action(this);
	gr->save_key_select();
	(gr->*the_action)();
	if (!gr->test_clear_action_failed()) gr->record();
	gr->clear_key_select();
	gr->reset_action();
	gr->reset_action();
}


Action * WindowMenuView::set_action(PullDownEntry * i)
{
	ActionWindowMenuView * temp = (ActionWindowMenuView *) i->base_action();
	ActionType act = temp->action();
	MenuKeyboard * key = keyboard();
	ActionWindowMenuView * this_action = new ActionWindowMenuView(key,
		this, *i, act);
	i->action(this_action);
	return this_action ;
}

ActionWindowMenuView::ActionWindowMenuView(MenuKeyboard * key,
	WindowMenuView * g, PullDownEntry& desc, ActionType a):
		RecAction(key,&desc),
		gr(g),
		the_action(a)
{
	Resource::ref(this);
}

void ActionWindowMenuView::execute()
{
    if (!DspApplication::user_input_allowed()) return ;
	gr->reset_action(this);
	gr->save_key_select();
    (gr->*the_action)();
    if (!gr->test_clear_action_failed()) gr->record();
	gr->clear_key_select();
	gr->reset_action();
}


#define declare_WindowMenuView_member(name) \
static ActionWindowMenuView name (&(WindowMenuView::name)) ; \
void WindowMenuView::name()

declare_WindowMenuView_member(help_introduction)
{
	DspApplication::display_help_file("introd");
	check_release();
}

declare_WindowMenuView_member(help_menu_codes)
{
	DspApplication::display_help_file("menu_codes");
	check_release();
}

declare_WindowMenuView_member(help_menu_database)
{
	DspApplication::display_help_file("menu_database");
	check_release();
}

declare_WindowMenuView_member(help_save)
{
	DspApplication::display_help_file("save_win");
	check_release();
}

declare_WindowMenuView_member(help_abort)
{
	DspApplication::display_help_file("abort");
	check_release();
}

declare_WindowMenuView_member(help_editing)
{
	DspApplication::display_help_file("menu_editing");
	check_release();
}

declare_WindowMenuView_member(focus_dpp)
{
    DspApplication::root_window()->focus_dpp();
    check_release();
}

declare_WindowMenuView_member(focus_menu)
{
    DspApplication::root_window()->focus_menu();
    check_release();
}

declare_WindowMenuView_member(focus_buttons)
{
    DspApplication::root_window()->focus_buttons();
    check_release();
}

declare_WindowMenuView_member(select_home)
{
    the_menu_view->select_home();
    check_release();
}

declare_WindowMenuView_member(select_end)
{
    the_menu_view->select_end();
    check_release();
}

declare_WindowMenuView_member(select_left)
{
    the_menu_view->select_left();
    check_release();
}

declare_WindowMenuView_member(select_right)
{
    the_menu_view->select_right();
    check_release();
}

declare_WindowMenuView_member(select_up)
{
    the_menu_view->select_up();
    check_release();
}

declare_WindowMenuView_member(select_down)
{
    the_menu_view->select_down();
    check_release();
}

declare_WindowMenuView_member(select_none)
{
    the_menu_view->select_none();
    check_release();
}


declare_WindowMenuView_member(peel_off_menu)
{
	check_release();
	the_menu_view->peel_off_menu();
}

declare_WindowMenuView_member(raise_windows)
{
	check_release();
	the_menu_view->raise_windows();
}

declare_WindowMenuView_member(freeze_dsp)
{
	check_release();
	DspApplication::root_window()->hard_freeze();
}

declare_WindowMenuView_member(thaw_dsp)
{
	check_release();
	DspApplication::root_window()->thaw();
}

declare_WindowMenuView_member(print_display)
{
	check_release();
	DspApplication::root_window()->FocusSelector::print_display();
}

declare_WindowMenuView_member(print_win_raise)
{
	check_release();
	DspApplication::root_window()->MenuView::print_window_raise();
}

declare_WindowMenuView_member(print_win)
{
	check_release();
	DspApplication::root_window()->MenuView::print_window();
}

declare_WindowMenuView_member(create_default)
{
	check_release();
	the_menu_view->create_default();
}

declare_WindowMenuView_member(create_instance)
{
	check_release();
	the_menu_view->create_instance();
}

declare_WindowMenuView_member(abort_play)
{
	check_release();
	ManagedKeyboards::manager()->abort_play();
}

declare_WindowMenuView_member(play_actions)
{
	check_release();
	record();
	ManagedKeyboards::manager()->play_actions();
}

declare_WindowMenuView_member(pause_actions)
{
	check_release();
	ManagedKeyboards::manager()->play_pause();
}

declare_WindowMenuView_member(record_actions)
{
	check_release();
	record();
	ManagedKeyboards::manager()->record_actions();
}

declare_WindowMenuView_member(record_off)
{
	check_release();
	ManagedKeyboards::manager()->record_off();
}

declare_WindowMenuView_member(record_flush)
{
	check_release();
	ManagedKeyboards::manager()->record_flush();
}



declare_WindowMenuView_member(do_command)
{
	check_release();
	the_menu_view->do_command();
}

declare_WindowMenuView_member(append_statement_history)
{
	check_release();
	DspApplication::root_window()->append_statement_history();
}

declare_WindowMenuView_member(command_history)
{
	check_release();
	DspApplication::root_window()->command_history();
}

declare_WindowMenuView_member(statement_history)
{
	check_release();
	DspApplication::root_window()->statement_history();
}

declare_WindowMenuView_member(help_paragraph)
{
	check_release();
	the_menu_view->help_paragraph();
}

declare_WindowMenuView_member(cond_help_paragraph)
{
	check_release();
	the_menu_view->cond_help_paragraph();
}

declare_WindowMenuView_member(help_file)
{
	check_release();
	the_menu_view->help_file();
}

void WindowMenuView::record()
{
	if (!recorded_flag) if (current_action) {
		current_action->record();
		recorded_flag = 1 ;
	}
}




#define EN PullDownDescribe::none
#define MD X_Character::down
#define MS X_Character::shift
#define MC X_Character::ctrl
#define MA X_Character::any
#define ML X_Character::left
#define MM X_Character::middle
#define MR X_Character::right

static PullDownDescribe help_menu[] = {
	{"introduction for new users", XK_F6,  &help_introduction},
	{"pull down menu codes", XK_F7,&help_menu_codes},
	{"menu data base", XK_F8,&help_menu_database},
	{"editing networks", XK_F9, &help_editing},
	{"aborting commands and deleting windows", XK_F10, &help_abort},
	{"saving window images", XK_F11, &help_save},
	{0}
};

static PullDownDescribe select_menu[] = {
	{"keyboard entry for DSP++ statements", XK_c_ +  XK_D, &focus_dpp},
	{"keyboard entry for menu data base", XK_c_ +  XK_M, &focus_menu},
	{"keyboard entry of menu shortcut keys", XK_Escape, &focus_buttons}, 
    {"select upper left button", XK_Home, &select_home},
    {"select lower right button", XK_End, &select_end},
    {"move selection left",XK_Left,&select_left},
    {"move selection right",XK_Right,&select_right},
    {"move selection up",XK_Up,&select_up},
    {"move selection down",XK_Down,&select_down},
    {"selection off",XK_s_ + XK_End,&select_none},
    {0}
};



static PullDownDescribe database_menu[] = {
	{"execute command (or display menu)", XK_Return, &do_command, 0, EN, ML},
	{"peel off menu",XK_p, &peel_off_menu, 0, EN, MC + ML},
	{"display help paragraph", XK_space, &help_paragraph, 0, EN,MD + MR},
	{"display help file", XK_question, &help_file, 0, EN, MR},
	{"raise network and display window",XK_r, &raise_windows, 0, EN, MC + MM},

	{"create DSP++ history window", XK_c_ + XK_H,&statement_history},
	{"create command history window", XK_c_ + XK_h, &command_history},
	{"append information window to DSP++ history",XK_c_ + XK_a,
		&append_statement_history},
	{"display help paragraph if help all", XK_c_ + XK_space, &cond_help_paragraph, 0, EN,MD + ML},
	// above is for the mouse action
	{0}
};



static PullDownDescribe editing_menu[] = {
	{"create default instance, edit object", XK_e, &create_default,0,
		EN, MS + ML},
	{"create instance", XK_i, &create_instance, 0, EN, MS + MR},
	{"freeze DSP process", XK_F12, &freeze_dsp},
	{"thaw DSP process", XK_s_ + XK_F12, &thaw_dsp},
	{"save window (works for all windows)", XK_c_ + '*', &print_win_raise},
	{"save window, do not raise first", XK_c_ + '&', &print_win},
	{"save display (X root window)", XK_c_ + '^', &print_display},
	{0}
};

static PullDownDescribe actions_menu[] = {
	{"playback actions", XK_c_ + XK_P, &play_actions},
	{"pause between actions", XK_c_ + XK_W, &pause_actions},

	// if you change the following code chage FocusSelector::common_keystroke
	{"playback abort", XK_c_ + XK_Delete, &abort_play},
	{"record actions", XK_R, &record_actions},
	{"record off",	XK_c_ + XK_R, &record_off},
	{"record close", XK_c_ +  XK_F, &record_flush},
	{0}
};

/*
 * static PullDownDescribe other_menu[] = {
 *	{0}
 * };
 */



static PullDownDescribe root_main_menu[] = {
	{"Help",XK_F1,nil,help_menu},
	{"Select",XK_F2,nil,select_menu},
	{"Data base",XK_F3,nil,database_menu},
	{"Edit and control",XK_F4,nil,editing_menu},
	{"Actions",XK_F5,nil,actions_menu},
	{0}
};

static PullDownDescribe root_menu_tree = {0,0,0,root_main_menu};

MenuKeyboard * MenuView::build_menu_keyboard(WidgetKit& kit,
	const LayoutKit& layout, Style * style, MenuView& )
{
	// LogOut << "MenuView::build_menu\n" ;
	// PullDownEntry * entry = new PullDownEntry(root_menu_tree);
	keyboard_interface = new WindowMenuView(this) ;
	return new MenuKeyboard(kit,layout, style,keyboard_interface,
		root_menu_tree);
}

void MenuView::new_selection(HotButton * selected)
{
	if (!selected) return ;
	set_selection(selected);
}

void MenuView::clear_selected()
{
	clear_mouse_selection();
	select_none();
}

void MenuView::set_selection(HotButton * selected)
{
	if (the_selection) the_selection->unselect();
	the_selection = selected ;
	if (the_selection) the_selection->select();
}

HotButton * MenuView::selection(int record_action_or_fail)
{
	HotButton * but = 0 ;
	if (prefer_mouse) if (the_mouse_selection) but =  the_mouse_selection;
	if (!but) if (the_selection) but =  the_selection ;
	if (!but) but = the_mouse_selection ;
	if (record_action_or_fail) {
		if (!but) action_failed = 1 ;
		else  keyboard_interface->record();
	}
	return but ;
}

void MenuViewLine::clear_mouse_selection()
{
	menu_window.clear_mouse_selection();
}

void MenuViewLine::set_mouse_selection(HotButton * selected)
{
	menu_window.set_mouse_selection(selected);
}

HotButton * MenuViewLine::find_selection(const char * sel)
{
	for (HotButton ** but = the_hot_buttons; *but ; but++)
		if (!strcmp((*but)->name(),sel)) return * but ;
	return 0 ;
}

void MenuView::act_select(const char * sel)
{
	// LogOut << "MenuView::act_select(" << sel << ")\n" ;
	clear_mouse_selection();
	if (the_selection) the_selection->unselect();
	the_selection = 0 ;
	the_mouse_selection = menu_stack.find_selection(sel);
	// LogOut << "MenuView::act_select " << (void *) the_mouse_selection << "\n" ;
}

void MenuView::act_deselect()
{
	clear_mouse_selection();
}

void MenuView::set_mouse_selection(HotButton * selected)
{
	the_mouse_selection = selected ;
}

void MenuView::select_home()
{
	new_selection(menu_stack.select_home());
}

void MenuView::select_end()
{
	new_selection(menu_stack.select_end());
}

void MenuView::select_left()
{
	if (!selection(1)) return ;
	new_selection(menu_stack.select_left(selection()));
}

void MenuView::select_right()
{
	if (!selection(1)) return ;
	new_selection(menu_stack.select_right(selection()));
}

void MenuView::select_up()
{
	if (!selection(1)) return ;
	new_selection(menu_stack.select_up(selection()));
}

void MenuView::select_down()
{
	if (!selection(1)) return ;
	new_selection(menu_stack.select_down(selection()));
}

void MenuView::select_none()
{
	if (!selection(1)) return ;
	set_selection(0);
}

void MenuView::do_command()
{
	if (!selection()) {
		if (the_fail_select) {
/*
 *			LogOut<<"MenuView::do_command, fail_sel `"<<
 *				the_fail_select<<"'\n";
 */
			ManagedKeyboards::manager()->record_menu_state();
			keyboard_interface->record();
			DspApplication::execute_menu_command(the_fail_select,
				ManagedKeyboards::manager()->menu_history_array());
		}
		action_failed = 1 ;
		return ;
	}
	DspApplication::record_menu_state(menu_stack,
		selection()->line());
	keyboard_interface->record();
	selection()->do_command();
}

void MenuView::cond_help_paragraph()
{
	if (!selection(1)) return ;
	if (!HelpDo.All()) return ;
	selection()->help_paragraph();
}
void MenuView::help_paragraph()
{
	if (!selection(1)) return ;
	selection()->help_paragraph();
}

void MenuView::help_file()
{
	if (!selection(1)) return ;
	selection()->help_file();
}

void MenuView::peel_off_menu()
{
	if (!selection(1)) return ;
	selection()->peel_off_menu();
}

void MenuView::raise_windows()
{
	if (!selection(1)) return ;
	selection()->raise_windows();
}


void MenuView::create_default()
{
	if (!selection()) {
		if (the_fail_select) {
/*
 *			LogOut<<"MenuView::create_default, fail_sel `"<<
 *				the_fail_select<<"'\n";
 */
			ManagedKeyboards::manager()->record_menu_state();
			keyboard_interface->record();
			DspApplication::create_default(the_fail_select);
		}
		action_failed = 1 ;
		return ;
	}
	ManagedKeyboards::manager()->record_menu_state();
	keyboard_interface->record();
	selection()->create_default();
}

void MenuView::create_instance()
{
	if (!selection()) {
		if (the_fail_select) {
/*
 *			LogOut<<"MenuView::create_instance, fail_sel `"<<
 *				the_fail_select<<"'\n";
 */
			ManagedKeyboards::manager()->record_menu_state();
			keyboard_interface->record();
			DspApplication::create_instance(the_fail_select);
		}
		action_failed = 1 ;
		return ;
	}
	ManagedKeyboards::manager()->record_menu_state();
	keyboard_interface->record();
	selection()->create_instance();
}


void MenuView::clear_fail_select()
{
	delete the_fail_select ;
	the_fail_select = 0 ;
}

void MenuView::set_fail_select(const char * name)
{
	int underscores = 0 ;
	the_fail_select = Concatenate(name);
	// LogOut << "MenuView::set_fail_select `" << the_fail_select << "'\n" ;
	for (char * pt =  the_fail_select + strlen(the_fail_select) -1 ;
	  pt > the_fail_select  ;pt--) if (*pt == '_') {
		*pt = '\0' ;
		if (++underscores > 1) break ;
	} else if (isdigit(*pt)) *pt ='\0' ;
	else break ;
	// LogOut << "MenuView::set_fail_select exit `" << the_fail_select << "'\n" ;
}

int MenuView::test_clear_action_failed()
{
	int ret = action_failed ;
	action_failed = 0 ;
	return ret ;
}

