#include <stream.h>
#include "menstck.h"
#include "menu.h"
#include "xdrv.h"
#include "playback.h"
#include "hot_button.h"
#include <InterViews/leave-scope.h>
#include <Dispatch/leave-scope.h>

#include "cgidbg.h"
#include "help.h"
#include "dsp_app.h"

// void TestAlloc(const char *msg=0);

class MenuWindow ;

StackMenu::StackMenu(MenuViewLine * menu, const char * where, int param,
	int level,int in):
		menu_line(*menu),
		StackMenuBase(&(menu->get_menu()), where,param,level,in)
{
}

StackMenuBase::StackMenuBase(Menu * men, const char * where, int par, int lev,
	int in):
	the_level(lev),
	TheMenu(men),
	WhereFrom(where),
	in_menu_flag(in),
	parameter_index(par)
{
}

StackMenu::StackMenu(StackMenuBase * clone):
	menu_line(*(DspApplication::root_window()->root_line())),
	StackMenuBase(clone)
{
}

StackMenu::StackMenu(StackMenu * clone):
	menu_line(clone->view_line()),
	StackMenuBase(clone)
{
}

StackMenuBase::StackMenuBase(StackMenuBase * clone):
	the_level(clone->level()),
	TheMenu(clone->GetMenu()),
	WhereFrom(clone->GetWhereFrom()),
	in_menu_flag(0),
	parameter_index(clone->get_parameter_index())
{
	// LogOut << "parameter_index = " << parameter_index << "\n" ;
}

StackMenuBase::expanded() const
{
	return TheMenu->expanded() ;
}

int StackMenu::is_visible() const
{
	if (!is_in_menu()) return 0 ;
	return menu_line.is_visible();
}

void StackMenu::Dump() const
{
	TheLog << GetWhereFrom() << ", `" << TheMenu->GetHeader() << "'\nin: "
		<< is_in_menu() << ", gl: " << menu_line.glyph_index() <<
		", exp: " << expanded() <<
		", vis: " << menu_line.is_visible() <<  ", par: " <<
		parameter_index << ",lev: " << level() << " " <<
		(void *) GetMenu() << "\n" ;
}

void StackMenuBase::Dump() const
{
	TheLog << GetWhereFrom() << ", `" << TheMenu->GetHeader() << "'\nin: "
		<< is_in_menu() << " exp: " << expanded() <<
		", par ix: " << parameter_index << ", lev: " << level() << " "
		<< (void*) GetMenu() << "\n" ;
}

void StackMenu::find_command(MenuTreeTraverseObject& obj) const
{
	if (!is_visible()) return ;
	int index ;
	if ((index = TheMenu->command_index(obj.command())) < 0) return ;
	if (obj.found()) obj.duplicate();
	else obj.stack_menu(this,index);
}

void StackMenuBase::SetSelectColor(MenuWindow * MenWin, Menu * Previous,int Line)
{
/*
 *	Previous->GetMenuLine(MenuInit);
 *	MenuLine * Check;
 *	for (int Position = 0; Check = Previous->GetMenuLine(); Position++)
 *		if(!strcmp(Check->Command, GetWhereFrom())) break;
 *	if (Check) MenWin->SetSelectColor(Line, Position);
 *	else LogMsg("MenuWindow::SetSelectColor","funny menu stack");
 */
}

void StackMenuList::Dump() const
{
	StackMenuListIterator next(*this);
	StackMenu * elt ;
	int count = 0 ;
	while (elt = next()) {
		TheLog << count++ << ":" ;
		elt->Dump();
	}
}

void StackMenuBaseList::Dump() const
{
	StackMenuBaseListIterator next(*this);
	StackMenuBase * elt ;
	int count = 0 ;
	while (elt = next()) {
		TheLog << count++ << ":" ;
		elt->Dump();
	}
}


void StackMenuBaseList::record_menu_state(ostream& out, Menu& men)
{
	// LogOut << "StackMenuBaseList::record_menu_state, menu dump:\n" ;
	// Dump();
	// LogOut << "end_dump\n" ;
	// men.Display();
	// LogOut << "end of menu_dump\n" ;
	StackMenuBaseListReverseIterator next(*this);
    StackMenuBase * elt ;
    int count = 0 ;
	int recording = 0 ;
	StackMenuBase * prev = 0 ;
    while ((elt = next()) || prev) {
		if (elt) {
			// LogOut << "Elt:\n" ;
			// elt->Dump();
			if (!recording) if (elt->GetMenu() == &men) recording = 1;
		}
		// LogOut << "recording = " << recording <<", count = " << count << "\nPrev:\n" ;
		// if (prev) prev->Dump() ; else LogOut << "Previous is null\n" ;
		if (recording && prev) {
			const char * where = prev->GetWhereFrom() ;
			if (!where) return ;
			// LogOut << "where `" << where << "'\n" ;
			if (!*where) return ;
			if (strcmp("other",where)) {
				out << " \"" << where << "\"" ;
				if (++count >= ManagedKeyboards::manager()->max_menu_history) 
					return ;
			}
		}
		prev=elt ;
	}
}



StackMenuList::StackMenuList(StackMenuBaseList * par,int size_copy)
{
	// LogOut << "::StackMenuList(,," << size_copy << "\n" ;
	size = 0 ;
	if (!par || size_copy < 1) return ;
	// par->Dump();
	StackMenuBaseListIterator next(*par);
	StackMenuBase * elt ;
	int count = 0 ;
	StackMenu * clone = 0 ;
	while (elt = next()) {
		if (++count > size_copy) break ;
		StackMenu * clone = new StackMenu(elt);
		// LogOut << "Copying at "<< count << "`"<<clone->GetWhereFrom() << "'\n" ;
		Append(clone);
		size++;
	}
	if (clone) clone->set_in_menu();
	// Dump();
}


int StackMenuList::top_param()
{
	// LogOut << "StackMenuList::top_param()\n" ;
	StackMenuListReverseIterator Previous(*this) ;
	StackMenu * AMenu ;
	while (AMenu = Previous()) {
		// LogOut << "index = " << AMenu->get_parameter_index() << "\n" ;
		int top = AMenu->get_parameter_index();
		if (top > -1) {
			// LogOut << "Returning " << top << "\n" ;
			return top ;
		}
	}
	// LogOut << "Returning -1\n" ;
	return -1 ;
}

StackMenuBase * StackMenuBaseList::Pop()
{
	StackMenuBase * Temp = (StackMenuBase *) DoubleList::Pop();
	return Temp ;
}

void StackMenuBaseList::Push(Menu * menu, const char * WhereFrom, int par)
{
	StackMenuBase * This = new StackMenuBase(menu,WhereFrom,par,Size());
	this->Append(This);
}

StackMenuBase * StackMenuBaseList::GetStackMenuNFromTop(int N)
{
	// LogOut << "StackMenuBaseList::GetStackMenuNFromTop(" << N << ")\n" ;
	// Dump();
	StackMenuBaseListReverseIterator Next(*this) ;
	int Sz = Size() ;
	if (Sz < N) return 0 ;
	StackMenuBase * previous = 0 ;
	StackMenuBase * AMenu ;
	int i = 0 ;
	while (AMenu = Next())  {
	 if (previous)
	  if (!AMenu->expanded() || !(previous->get_parameter_index()==0))
	   if (i++ == N) {
			// LogOut << "Returning:\n" ;
			// previous->Dump();
			return previous ;
		}
		previous = AMenu ;
	}
	return 0;
}



StackMenu * StackMenuList::Pop()
{
	if (size) size-- ;
	StackMenu * Temp = (StackMenu *) DoubleList::Pop();
	// Menu * Return = Temp->GetMenu();
	// delete Temp ;
	return Temp ;
}

void StackMenuList::Push(MenuViewLine * menu, const char * WhereFrom, int par)
{
	StackMenu * This = new StackMenu(menu,WhereFrom,par,size++);
	menu->set_stack_menu(This);
	this->Append(This);
}

int StackMenuList::all_visible(int skip)
{
	// LogOut << "StackMenuList::all_visible(" << skip << ")\n" ;
	StackMenuListIterator Next(*this) ;
	StackMenu * AMenu ;
	int count = 0 ;
	while (AMenu = Next()) {
		if (count++ < skip) continue ;
/*
 *		LogOut << "count = " << count << ", `" << AMenu->GetWhereFrom() <<
 *			"'\n" ;
 */
		if (!AMenu->is_visible()) return 0 ;
	}
	// LogOut << "Returning 1\n" ;
	return 1 ;
}

StackMenu * StackMenuList::first_visible(int skip)
{
	// LogOut << "first_visible(" << skip << ")\n" ;
	StackMenuListIterator Next(*this) ;
	StackMenu * AMenu ;
	int count = 0 ;
	while (AMenu = Next()) {
		if (!AMenu->is_in_menu()) continue ;
		if (count++ < skip) continue ;
/*
 *		LogOut << "count = " << count << ", `" << AMenu->GetWhereFrom() <<
 *			"'\n" ;
 */
		if (AMenu->is_visible()) return AMenu ;
	}
	// LogOut << "Returning null\n" ;
	return 0 ;
}

StackMenu * StackMenuList::GetStackNthEntry(int N)
{
	StackMenuListIterator Next(*this) ;
	StackMenu * AMenu ;
	for (int i = 1 ; AMenu = Next() ; i++)
		if (i==N) return AMenu ;
	return 0;
}

Menu * StackMenuList::GetNthEntry(int N)
{
	return GetStackNthEntry(N)->GetMenu() ;
}

StackMenu * StackMenuList::GetNFromTop(int N)
{
	// StackMenu * Return = GetStackMenuNFromTop(N);
	StackMenuListReverseIterator Next(*this);
	int i = 0 ;
	StackMenu * Return = 0 ;
	while (Return = Next()) if (i++ == N) break ;
	return Return ;
}

StackMenu * StackMenuList::GetStackMenuNFromTop(int N)
{
	// LogOut << "StackMenuList::GetStackMenuNFromTop(" << N << ")\n" ;
	// Dump();
	StackMenuListReverseIterator Next(*this) ;
	int Sz = Size() ;
	if (Sz < N) return 0 ;
	// N = Sz - N ;
	StackMenu * AMenu ;
	int i = 0 ;
	// int prev_ix_0 = 0 ;
	while (AMenu = Next())  {
		// if (AMenu->expanded() && prev_ix_0) continue ;
		// prev_ix_0 = !AMenu->get_parameter_index();
		if (i++ == N) {
			// LogOut << "returning `" << AMenu->GetWhereFrom() << "'.\n" ;
			// AMenu->Dump();
			return AMenu ;
		}
	}
	// LogOut << "returning 0\n" ;
	return 0;
}

const char * StackMenuList::name_from_level(int level, int from) const
{
/*
 *	LogOut << "StackMenuList::name_from_level( " << level << ", " << from
 *		<< ")\n" ;
 *	Dump();
 */
	StackMenuListReverseIterator Next(*this) ;
	StackMenu * AMenu ;
	int i = 1 ;
	StackMenu * previous = 0 ;
	while (AMenu = Next())  {
/*
 *		LogOut << "AMenu->level() = " << AMenu->level() << ", expanded = "
 *			<< AMenu->expanded() << ", i = " << i << ", par_index = " <<
 *			AMenu->get_parameter_index() << "\n" ;
 */
		if (previous) if ( previous->level() <= level) {
			if (!AMenu->expanded() || !(previous->get_parameter_index()==0))
		  	  if(i++ == from) {
				// LogOut<<"returning `" << previous->GetWhereFrom() << "'\n" ;
				return previous->GetWhereFrom() ;
			}
		}
		previous = AMenu ;
	}
	// LogOut << "returning STACK_ERROR\n" ;
	return "STACK_ERROR";
}

int StackMenuList::visible_levels()
{
	StackMenuListIterator Next(*this);
	StackMenu * elt ;
	int count = 0 ;
	while (elt=Next()) if (elt->is_visible()) count++;
	// LogOut << "StackMenuList::visible_levels() returning " << count << "\n";
	return count ;
}

const StackMenuBase * StackMenuList::find_command(MenuTreeTraverseObject& obj)
	const
{
	StackMenuListReverseIterator next(*this);
	StackMenu * stack_menu ;
	while (stack_menu = next()) {
		stack_menu->find_command(obj);
		if (obj.is_duplicate()) break ;
	}
	if (obj.is_error()) {
		if (HelpDo.All()) {
			obj.error_message("visible menu tree");
			*Output + OutputHelp << "Checking the full menu tree.\n" ;
		}
		return 0;
	}
	return obj.found();
}

MenuTreeTraverseObject::~MenuTreeTraverseObject()
{
	StackMenuBase * st ;
	while (st = stack.Get()) delete st ;

}

const char * MenuTreeTraverseObject::next()
{
	return next_check;
}

int MenuTreeTraverseObject::check_on_enter(Menu *ck)
{
	if (!history_constraint) return 1 ;
	if (found_constraint) return 0 ;
	return found_constraint = ck == history_constraint ;
}

MenuEnums::ExitStatus MenuTreeTraverseObject::check_on_exit(Menu *ck)
{
	if (history_constraint)
		if (ck == history_constraint) return MenuEnums::abort ;
	return MenuEnums::not_found ;
}

void MenuTreeTraverseObject::error_message(const char * where) const
{
	if (!is_error()) return ;
	if (is_duplicate()) *Output + OutputHelp <<
		"There is more than one command `" << command_name << "' in the " <<
		where << ".\n";
	else *Output + OutputHelp << "Command `" << command_name
			<< "' was not found in the " << where << ".\n" ;
}

HotButton * StackMenuList::find_selection(const char * sel)
{
	StackMenuListIterator next(*this);
	StackMenu * menu ;
	while (menu = next()) if (menu->is_visible()) {
		HotButton * select = menu->view_line().find_selection(sel);
		if (select) return select ;
	}
	return 0 ;
}

HotButton * StackMenuList::select_home()
{
	StackMenuListIterator next(*this);
	StackMenu * menu ;
	while (menu = next()) if (menu->is_visible())
		return menu->view_line().button(0);
	return 0 ;
}

HotButton * StackMenuList::select_end()
{
	StackMenuListReverseIterator next(*this);
	StackMenu * menu ;
	while (menu = next()) if (menu->is_visible()) {
		MenuViewLine& line = menu->view_line();
		return line.button(line.number_buttons()-1);
	}
	return 0;
}

HotButton * StackMenuList::select_left(HotButton * selected)
{
	StackMenuListReverseIterator next(*this);
	StackMenu * menu ;
	while (menu = next()) if (menu->is_visible()) {
		MenuViewLine& line = menu->view_line();
		ivGlyphIndex index = line.button_index(selected);
		if (index < 0) continue ;
		if (index == 0) return 0 ;
		return line.button(index-1);
	}
	return 0 ;
}

HotButton * StackMenuList::select_right(HotButton * selected)
{
	StackMenuListReverseIterator next(*this);
	StackMenu * menu ;
	while (menu = next()) if (menu->is_visible()) {
		MenuViewLine& line = menu->view_line();
		ivGlyphIndex index = line.button_index(selected);
		if (index < 0) continue ;
		if (index > (line.number_buttons() - 2)) return 0 ;
		return line.button(index+1);
	}
	return 0 ;
}

HotButton * StackMenuList::select_up(HotButton * selected)
{
	StackMenuListReverseIterator next(*this);
	StackMenu * menu ;
	while (menu = next()) if (menu->is_visible()) {
		MenuViewLine& line = menu->view_line();
		ivGlyphIndex index = line.button_index(selected);
		if (index < 0) continue ;
		while (menu = next()) if (menu->is_visible()) break ;
		if (!menu) return 0 ;
		MenuViewLine& up = menu->view_line();
		ivGlyphIndex count = line.number_buttons();
		ivGlyphIndex up_count = up.number_buttons();
		ivGlyphIndex use = (ivGlyphIndex)
			(.5 + (((float) index) * up_count / (float) count)) ;
		if (use > up_count-1) use = up_count-1 ;
		return up.button(use);
	}
	return 0 ;
}

HotButton * StackMenuList::select_down(HotButton * selected)
{
	StackMenuListReverseIterator next(*this);
	StackMenu * menu ;
	MenuViewLine * prev_line = 0 ;
	while (menu = next()) if (menu->is_visible()) {
		MenuViewLine& line = menu->view_line();
		ivGlyphIndex index = line.button_index(selected);
		if (index < 0) {
			prev_line = &line ;
			continue ;
		}
		if (!prev_line) return 0 ;
		ivGlyphIndex count = line.number_buttons();
		ivGlyphIndex down_count = prev_line->number_buttons();
		ivGlyphIndex use = (ivGlyphIndex)
			(.5 + (((float) index) * down_count / (float) count)) ;
		if (use > down_count-1) use = down_count-1 ;
		return prev_line->button(use);
	}
	return 0 ;
}

