/*
manager.cpp

Переключение между несколькими file panels, viewers, editors, dialogs
*/
/*
Copyright (c) 1996 Eugene Roshal
Copyright (c) 2000 Far Group
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. The name of the authors may not be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "headers.hpp"

#include "manager.hpp"
#include "lang.hpp"
#include "keys.hpp"
#include "frame.hpp"
#include "vmenu.hpp"
#include "filepanels.hpp"
#include "panel.hpp"
#include "savescr.hpp"
#include "cmdline.hpp"
#include "ctrlobj.hpp"
#include "syslog.hpp"
#include "interf.hpp"
#include "keyboard.hpp"
#include "grabber.hpp"
#include "message.hpp"
#include "config.hpp"
#include "plist.hpp"
#include "pathmix.hpp"
#include "strmix.hpp"
#include "exitcode.hpp"
#include "scrbuf.hpp"
#include "console.hpp"
#include "InterThreadCall.hpp"
#include "vt/vtshell.h"
#include "execute.hpp"
#include "fileview.hpp"

Manager *FrameManager;

Manager::Manager()
	:
	ModalStack(nullptr),
	ModalStackCount(0),
	ModalStackSize(0),
	FrameCount(0),
	FrameList(reinterpret_cast<Frame **>(malloc(sizeof(Frame *) * (FrameCount + 1)))),
	FrameListSize(0),
	FramePos(-1),
	InsertedFrame(nullptr),
	DeletedFrame(nullptr),
	ActivatedFrame(nullptr),
	RefreshedFrame(nullptr),
	ModalizedFrame(nullptr),
	UnmodalizedFrame(nullptr),
	DeactivatedFrame(nullptr),
	ExecutedFrame(nullptr),
	CurrentFrame(nullptr),
	ModalEVCount(0),
	EndLoop(FALSE),
	StartManager(FALSE)
{}

Manager::~Manager()
{
	if (FrameList)
		free(FrameList);

	if (ModalStack)
		free(ModalStack);

	/*
	if (SemiModalBackFrames)
		free(SemiModalBackFrames);
	*/
}

/*
	$ 29.12.2000 IS
	Аналог CloseAll, но разрешает продолжение полноценной работы в фаре,
	если пользователь продолжил редактировать файл.
	Возвращает TRUE, если все закрыли и можно выходить из фара.
*/
BOOL Manager::ExitAll()
{
	_MANAGER(CleverSysLog clv(L"Manager::ExitAll()"));

	for (int i = this->ModalStackCount - 1; i >= 0; i--) {
		Frame *iFrame = this->ModalStack[i];

		if (!iFrame->GetCanLoseFocus(TRUE)) {
			int PrevFrameCount = ModalStackCount;
			iFrame->ProcessKey(KEY_ESC);
			Commit();

			if (PrevFrameCount == ModalStackCount) {
				return FALSE;
			}
		}
	}

	for (int i = FrameCount - 1; i >= 0; i--) {
		Frame *iFrame = FrameList[i];

		if (!iFrame->GetCanLoseFocus(TRUE)) {
			ActivateFrame(iFrame);
			Commit();
			int PrevFrameCount = FrameCount;
			iFrame->ProcessKey(KEY_ESC);
			Commit();

			if (PrevFrameCount == FrameCount) {
				return FALSE;
			}
		}
	}

	return TRUE;
}

void Manager::CloseAll()
{
	_MANAGER(CleverSysLog clv(L"Manager::CloseAll()"));
	Frame *iFrame;

	for (int i = ModalStackCount - 1; i >= 0; i--) {
		iFrame = ModalStack[i];
		DeleteFrame(iFrame);
		DeleteCommit();
		DeletedFrame = nullptr;
	}

	for (int i = FrameCount - 1; i >= 0; i--) {
		iFrame = (*this)[i];
		DeleteFrame(iFrame);
		DeleteCommit();
		DeletedFrame = nullptr;
	}

	free(FrameList);
	FrameList = nullptr;
	FrameCount = FramePos = 0;
}

BOOL Manager::IsAnyFrameModified(int Activate)
{
	for (int I = 0; I < FrameCount; I++)
		if (FrameList[I]->IsFileModified()) {
			if (Activate) {
				ActivateFrame(I);
				Commit();
			}

			return TRUE;
		}

	return FALSE;
}

void Manager::InsertFrame(Frame *Inserted, int Index)
{
	_MANAGER(CleverSysLog clv(L"Manager::InsertFrame(Frame *Inserted, int Index)"));
	_MANAGER(SysLog(L"Inserted=%p, Index=%i", Inserted, Index));

	if (Index == -1)
		Index = FramePos;

	InsertedFrame = Inserted;
}

void Manager::DeleteFrame(Frame *Deleted)
{
	_MANAGER(CleverSysLog clv(L"Manager::DeleteFrame(Frame *Deleted)"));
	_MANAGER(SysLog(L"Deleted=%p", Deleted));

	for (int i = 0; i < FrameCount; i++) {
		Frame *iFrame = FrameList[i];

		if (iFrame->RemoveModal(Deleted)) {
			return;
		}
	}

	if (!Deleted) {
		DeletedFrame = CurrentFrame;
	} else {
		DeletedFrame = Deleted;
	}
}

void Manager::DeleteFrame(int Index)
{
	_MANAGER(CleverSysLog clv(L"Manager::DeleteFrame(int Index)"));
	_MANAGER(SysLog(L"Index=%i", Index));
	DeleteFrame(this->operator[](Index));
}

void Manager::ModalizeFrame(Frame *Modalized, int Mode)
{
	_MANAGER(CleverSysLog clv(L"Manager::ModalizeFrame (Frame *Modalized, int Mode)"));
	_MANAGER(SysLog(L"Modalized=%p", Modalized));
	ModalizedFrame = Modalized;
	ModalizeCommit();
}

void Manager::UnmodalizeFrame(Frame *Unmodalized)
{
	_MANAGER(CleverSysLog clv(L"Manager::UnmodalizeFrame (Frame *Unmodalized)"));
	_MANAGER(SysLog(L"Unmodalized=%p", Unmodalized));
	UnmodalizedFrame = Unmodalized;
	UnmodalizeCommit();
}

void Manager::ExecuteNonModal()
{
	_MANAGER(CleverSysLog clv(L"Manager::ExecuteNonModal ()"));
	_MANAGER(SysLog(L"ExecutedFrame=%p, InsertedFrame=%p, DeletedFrame=%p", ExecutedFrame, InsertedFrame,
			DeletedFrame));
	Frame *NonModal = InsertedFrame ? InsertedFrame : (ExecutedFrame ? ExecutedFrame : ActivatedFrame);

	if (!NonModal) {
		return;
	}

	/*
		$ 14.05.2002 SKV
		Положим текущий фрэйм в список "родителей" полумодальных фрэймов
	*/
	// Frame *SaveFrame=CurrentFrame;
	// AddSemiModalBackFrame(SaveFrame);
	int NonModalIndex = IndexOf(NonModal);

	if (-1 == NonModalIndex) {
		InsertedFrame = NonModal;
		ExecutedFrame = nullptr;
		InsertCommit();
		InsertedFrame = nullptr;
	} else {
		ActivateFrame(NonModalIndex);
	}

	// Frame* ModalStartLevel=NonModal;
	for (;;) {
		Commit();

		if (CurrentFrame != NonModal || EndLoop) {
			break;
		}

		ProcessMainLoop();
	}

	// ExecuteModal(NonModal);
	/*
		$ 14.05.2002 SKV
		... и уберём его же.
	*/
	// RemoveSemiModalBackFrame(SaveFrame);
}

void Manager::ExecuteModal(Frame *Executed)
{
	_MANAGER(CleverSysLog clv(L"Manager::ExecuteModal (Frame *Executed)"));
	_MANAGER(SysLog(L"Executed=%p, ExecutedFrame=%p", Executed, ExecutedFrame));

	if (!Executed && !ExecutedFrame) {
		fprintf(stderr, "ExecuteModal1\n");
		return;
	}

	if (Executed) {
		if (ExecutedFrame) {
			fprintf(stderr, "ExecuteModal2\n");
			_MANAGER(SysLog(L"WARNING! Попытка в одном цикле запустить в модальном режиме два фрейма. "
							L"Executed=%p, ExecitedFrame=%p",
					Executed, ExecutedFrame));
			return;		// nullptr; //?? Определить, какое значение правильно возвращать в этом случае
		} else {
			ExecutedFrame = Executed;
		}
	}

	int ModalStartLevel = ModalStackCount;
	int OriginalStartManager = StartManager;
	StartManager = TRUE;

	for (;;) {
		Commit();

		if (ModalStackCount <= ModalStartLevel) {
			break;
		}

		ProcessMainLoop();
	}

	StartManager = OriginalStartManager;
	return;		// GetModalExitCode();
}

int Manager::GetModalExitCode()
{
	return ModalExitCode;
}

/*
	$ 11.10.2001 IS
	Подсчитать количество фреймов с указанным именем.
*/
int Manager::CountFramesWithName(const wchar_t *Name, BOOL IgnoreCase)
{
	int Counter = 0;
	typedef int(__cdecl * cmpfunc_t)(const wchar_t *s1, const wchar_t *s2);
	cmpfunc_t cmpfunc = IgnoreCase ? StrCmpI : StrCmp;
	FARString strType, strCurName;

	for (int I = 0; I < FrameCount; I++) {
		FrameList[I]->GetTypeAndName(strType, strCurName);

		if (!cmpfunc(Name, strCurName))
			++Counter;
	}

	return Counter;
}

static FARString FrameMenuNumTextPrefix(int i)
{
	FARString out;
	if (i < 10)
		out.Format(L"&%d ", i);
	else if (i - 10 < 'Z' - 'A')
		out.Format(L"&%c ", char(i - 10 + 'A'));
	else
		out.Format(L"&  ");
	return out;
}

/*!
	\return Возвращает nullptr если нажат "отказ" или если нажат текущий фрейм.
	Другими словами, если немодальный фрейм не поменялся.
	Если же фрейм поменялся, то тогда функция должна возвратить
	указатель на предыдущий фрейм.
*/

class FramesMenu : public VMenu 
{
	VTInfos _vts;
	int _vts_base_index{-1};

public:
	FramesMenu() : VMenu (Msg::ScreensTitle, nullptr, 0, ScrY - 4)
	{
		VTShell_Enum(_vts);
	}

	virtual int ProcessKey(FarKey Key)
	{
		if (Key == KEY_F3 && _vts_base_index >= 0
				&& _vts_base_index <= GetSelectPos()
				&& _vts_base_index + int(_vts.size()) > GetSelectPos() ) {
			ViewConsoleHistory(_vts[GetSelectPos() - _vts_base_index].con_hnd, true, false);
			return TRUE;
		}
		return VMenu::ProcessKey(Key);
	}

	void AddVTSItems(int FramePos)
	{
		if (_vts.empty()) {
			_vts_base_index = -1;
			return;
		}
		_vts_base_index = GetItemCount() + 1;
		MenuItemEx mi;
		mi.Clear();
		mi.strName = Msg::BackgroundCommands;
		mi.Flags = LIF_SEPARATOR;
		AddItem(&mi);
		for (const auto &vt : _vts) {
			mi.Clear();
			mi.strName = vt.title;
			ReplaceStrings(mi.strName, L"&", L"&&", -1);
			mi.strName.Insert(0, FrameMenuNumTextPrefix(GetItemCount() - 1) );
			mi.SetSelect(GetItemCount() == FramePos);
			if (vt.exited)
				mi.SetCheck(vt.exit_code ? L'!' : L'#');

			AddItem(&mi);
		}
	}

	int Do()
	{
		VMenu::Process();
		int r = Modal::GetExitCode();
		if (_vts_base_index >= 0 && r >= _vts_base_index && r < GetItemCount()) {
			CtrlObject->CmdLine->SwitchToBackgroundTerminal(r - _vts_base_index);
			return -1;
		}
		return r;
	}
};

Frame *Manager::FrameMenu()
{
	/*
		$ 28.04.2002 KM
		Флаг для определения того, что меню переключения
		экранов уже активировано.
	*/
	static int AlreadyShown = FALSE;

	if (AlreadyShown)
		return nullptr;

	int ExitCode, CheckCanLoseFocus = CurrentFrame->GetCanLoseFocus();
	{
		MenuItemEx ModalMenuItem;
		FramesMenu ModalMenu;

		ModalMenu.SetHelp(L"ScrSwitch");
		ModalMenu.SetFlags(VMENU_WRAPMODE);
		ModalMenu.SetPosition(-1, -1, 0, 0);

		if (!CheckCanLoseFocus)
			ModalMenuItem.SetDisable(TRUE);

		int I = 0;
		for (; I < FrameCount; I++) {
			/* "*" если файл изменен */
			FARString strNumText = FrameMenuNumTextPrefix(I);
			FARString strType, strName;
			FrameList[I]->GetTypeAndName(strType, strName);
			ModalMenuItem.Clear();

			// TruncPathStr(strName,ScrX-24);
			ReplaceStrings(strName, L"&", L"&&", -1);
			ModalMenuItem.strName.Format(L"%ls%-10.10ls %ls", strNumText.CPtr(), strType.CPtr(), strName.CPtr());
			ModalMenuItem.SetSelect(I == FramePos);
			if (FrameList[I]->IsFileModified())
				ModalMenuItem.SetCheck(L'*');

			ModalMenu.AddItem(&ModalMenuItem);
		}

		ModalMenu.AddVTSItems(FramePos);

		AlreadyShown = TRUE;
		ExitCode = ModalMenu.Do();
		AlreadyShown = FALSE;
//		ExitCode = ModalMenu.Modal::GetExitCode();
	}

	if (CheckCanLoseFocus) {
		if (ExitCode >= 0 && ExitCode < FrameCount) {
			ActivateFrame(ExitCode);
			return (ActivatedFrame == CurrentFrame || !CurrentFrame->GetCanLoseFocus()
							? nullptr
							: CurrentFrame);
		}

		return (ActivatedFrame == CurrentFrame ? nullptr : CurrentFrame);
	}

	return nullptr;
}

int Manager::GetFrameCountByType(int Type)
{
	int ret = 0;

	for (int I = 0; I < FrameCount; I++) {
		/*
			$ 10.05.2001 DJ
			не учитываем фрейм, который собираемся удалять
		*/
		if (FrameList[I] == DeletedFrame || (unsigned int)FrameList[I]->GetExitCode() == XC_QUIT)
			continue;

		if (FrameList[I]->GetType() == Type)
			ret++;
	}

	return ret;
}

void Manager::SetFramePos(int NewPos)
{
	_MANAGER(CleverSysLog clv(L"Manager::SetFramePos(int NewPos)"));
	_MANAGER(SysLog(L"NewPos=%i", NewPos));
	FramePos = NewPos;
}

/*$ 11.05.2001 OT Теперь можно искать файл не только по полному имени, но и отдельно - путь, отдельно имя */
int Manager::FindFrameByFile(int ModalType, const wchar_t *FileName, const wchar_t *Dir)
{
	FARString strBufFileName;
	FARString strFullFileName = FileName;

	if (Dir) {
		strBufFileName = Dir;
		AddEndSlash(strBufFileName);
		strBufFileName+= FileName;
		strFullFileName = strBufFileName;
	}

	for (int I = 0; I < FrameCount; I++) {
		FARString strType, strName;

		// Mantis#0000469 - получать Name будем только при совпадении ModalType
		if (FrameList[I]->GetType() == ModalType) {
			FrameList[I]->GetTypeAndName(strType, strName);

			if (!StrCmpI(strName, strFullFileName))
				return (I);
		}
	}

	return -1;
}

BOOL Manager::ShowBackground()
{
	if (CtrlObject->CmdLine) {
		CtrlObject->CmdLine->ShowBackground();
		return TRUE;
	}
	return FALSE;
}

void Manager::ActivateFrame(Frame *Activated)
{
	_MANAGER(CleverSysLog clv(L"Manager::ActivateFrame(Frame *Activated)"));
	_MANAGER(SysLog(L"Activated=%i", Activated));

	if (IndexOf(Activated) == -1 && IndexOfStack(Activated) == -1)
		return;

	if (!ActivatedFrame) {
		ActivatedFrame = Activated;
	}
}

void Manager::ActivateFrame(int Index)
{
	_MANAGER(CleverSysLog clv(L"Manager::ActivateFrame(int Index)"));
	_MANAGER(SysLog(L"Index=%i", Index));
	ActivateFrame((*this)[Index]);
}

void Manager::DeactivateFrame(Frame *Deactivated, int Direction)
{
	_MANAGER(CleverSysLog clv(L"Manager::DeactivateFrame (Frame *Deactivated,int Direction)"));
	_MANAGER(SysLog(L"Deactivated=%p, Direction=%d", Deactivated, Direction));

	if (Direction) {
		FramePos+= Direction;

		if (Direction > 0) {
			if (FramePos >= FrameCount) {
				FramePos = 0;
			}
		} else {
			if (FramePos < 0) {
				FramePos = FrameCount - 1;
			}
		}

		ActivateFrame(FramePos);
	} else {
		// Direction==0
		// Direct access from menu or (in future) from plugin
	}

	DeactivatedFrame = Deactivated;
}

void Manager::SwapTwoFrame(int Direction)
{
	if (Direction) {
		int OldFramePos = FramePos;
		FramePos+= Direction;

		if (Direction > 0) {
			if (FramePos >= FrameCount) {
				FramePos = 0;
			}
		} else {
			if (FramePos < 0) {
				FramePos = FrameCount - 1;
			}
		}

		Frame *TmpFrame = FrameList[OldFramePos];
		FrameList[OldFramePos] = FrameList[FramePos];
		FrameList[FramePos] = TmpFrame;
		ActivateFrame(OldFramePos);
	}

	DeactivatedFrame = CurrentFrame;
}

void Manager::RefreshFrame(Frame *Refreshed)
{
	_MANAGER(CleverSysLog clv(L"Manager::RefreshFrame(Frame *Refreshed)"));
	_MANAGER(SysLog(L"Refreshed=%p", Refreshed));

	if (ActivatedFrame)
		return;

	if (Refreshed) {
		RefreshedFrame = Refreshed;
	} else {
		RefreshedFrame = CurrentFrame;
	}

	if (IndexOf(Refreshed) == -1 && IndexOfStack(Refreshed) == -1)
		return;

	/*
		$ 13.04.2002 KM
		- Вызываем принудительный Commit() для фрейма имеющего члена
		NextModal, это означает что активным сейчас является
		VMenu, а значит Commit() сам не будет вызван после возврата
		из функции.
		Устраняет ещё один момент неперерисовки, когда один над
		другим находится несколько объектов VMenu. Пример:
		настройка цветов. Теперь AltF9 в диалоге настройки
		цветов корректно перерисовывает меню.
	*/
	if (RefreshedFrame && RefreshedFrame->NextModal)
		Commit();
}

void Manager::RefreshFrame(int Index)
{
	_MANAGER(CleverSysLog clv(L"Manager::RefreshFrame(int Index)"));
	_MANAGER(SysLog(L"Index=%d", Index));
	RefreshFrame((*this)[Index]);
}

void Manager::ExecuteFrame(Frame *Executed)
{
	_MANAGER(CleverSysLog clv(L"Manager::ExecuteFrame(Frame *Executed)"));
	_MANAGER(SysLog(L"Executed=%p", Executed));
	ExecutedFrame = Executed;
}

/*
	$ 10.05.2001 DJ
	переключается на панели (фрейм с номером 0)
*/

void Manager::SwitchToPanels()
{
	_MANAGER(CleverSysLog clv(L"Manager::SwitchToPanels()"));
	ActivateFrame(0);
}

int Manager::HaveAnyFrame()
{
	if (FrameCount || InsertedFrame || DeletedFrame || ActivatedFrame || RefreshedFrame || ModalizedFrame
			|| DeactivatedFrame || ExecutedFrame || CurrentFrame)
		return 1;

	return 0;
}

void Manager::EnterMainLoop()
{
	WaitInFastFind = 0;
	StartManager = TRUE;

	for (;;) {
		Commit();

		if (EndLoop || !HaveAnyFrame()) {
			break;
		}

		ProcessMainLoop();
	}
}

void Manager::SetLastInputRecord(INPUT_RECORD *Rec)
{
	if (&LastInputRecord != Rec)
		LastInputRecord = *Rec;
}

void Manager::ProcessMainLoop()
{
	if (CurrentFrame)
		CtrlObject->Macro.SetMode(CurrentFrame->GetMacroMode());

	DispatchInterThreadCalls();

	if (CurrentFrame && !CurrentFrame->ProcessEvents()) {
		ProcessKey(KEY_IDLE);
	} else {
		// Mantis#0000073: Не работает автоскролинг в QView
		WaitInMainLoop =
				IsPanelsActive() && ((FilePanels *)CurrentFrame)->ActivePanel->GetType() != QVIEW_PANEL;
		// WaitInFastFind++;
		FarKey Key = GetInputRecord(&LastInputRecord);
		// WaitInFastFind--;
		WaitInMainLoop = FALSE;

		if (EndLoop)
			return;

		if (LastInputRecord.EventType == MOUSE_EVENT) {
			// используем копию структуры, т.к. LastInputRecord может внезапно измениться во время выполнения ProcessMouse
			MOUSE_EVENT_RECORD mer = LastInputRecord.Event.MouseEvent;
			ProcessMouse(&mer);
		} else
			ProcessKey(Key);
	}
}

static bool ConfirmExit(size_t vts_cnt)
{
	int r;
	ExMessager m(Msg::Quit);
	m.Add(Msg::AskQuit);
	if (vts_cnt) {
		m.AddFormat(Msg::AskQuitVTS, (unsigned int)vts_cnt);
	}
	m.Add(Msg::Yes);
	m.Add(Msg::No);
	if (WINPORT(ConsoleBackgroundMode)(FALSE)) {
		m.Add(Msg::Background);
		r = m.Show(vts_cnt ? MSG_WARNING : 0, 3);
		if (r == 2) {
			WINPORT(ConsoleBackgroundMode)(TRUE);
		}
	} else {
		r = m.Show(vts_cnt ? MSG_WARNING : 0, 2);
	}

	return r == 0;
}

void Manager::ExitMainLoop(int Ask)
{
	if (CloseFAR) {
		CloseFAR = FALSE;
		CloseFARMenu = TRUE;
	};


	size_t vts_cnt = VTShell_Count();
	if (!Ask || (((!Opt.Confirm.ExitEffective() && !vts_cnt) || ConfirmExit(vts_cnt)) && CtrlObject->Plugins.MayExitFar())) {
		/*
			$ 29.12.2000 IS
			+ Проверяем, сохранены ли все измененные файлы. Если нет, то не выходим
			из фара.
		*/
		if (ExitAll()) {
			// TODO: при закрытии по x нужно делать форсированный выход. Иначе могут быть
			// глюки, например, при перезагрузке
			FilePanels *cp;

			if (!(cp = CtrlObject->Cp())
					|| (!cp->LeftPanel->ProcessPluginEvent(FE_CLOSE, nullptr)
							&& !cp->RightPanel->ProcessPluginEvent(FE_CLOSE, nullptr))) {
				EndLoop = TRUE;
			}
		} else {
			CloseFARMenu = FALSE;
		}
	}
}

#if defined(FAR_ALPHA_VERSION)
#include <float.h>
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4717)
// #ifdef __cplusplus
// #if defined(_MSC_VER < 1500) // TODO: See REMINDER file, section intrin.h
#ifndef _M_IA64
extern "C" void __ud2();
#else
extern "C" void __setReg(int, uint64_t);
#endif
// #endif                       // TODO: See REMINDER file, section intrin.h
// #endif
#endif
static void Test_EXCEPTION_STACK_OVERFLOW(char *target)
{
	char Buffer[1024];	/* чтобы быстрее рвануло */
	strcpy(Buffer, "zzzz");
	Test_EXCEPTION_STACK_OVERFLOW(Buffer);
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#endif

int Manager::ProcessKey(DWORD Key)
{
	int ret = FALSE;

	if (CurrentFrame) {
		DWORD KeyM = (Key & (~KEY_CTRLMASK));

		if (!((KeyM >= KEY_MACRO_BASE && KeyM <= KEY_MACRO_ENDBASE)
					|| (KeyM >= KEY_OP_BASE && KeyM <= KEY_OP_ENDBASE)))	// пропустим макро-коды
		{
			switch (CurrentFrame->GetType()) {
				case MODALTYPE_PANELS: {
					_ALGO(CleverSysLog clv(L"Manager::ProcessKey()"));
					_ALGO(SysLog(L"Key=%ls", _FARKEY_ToName(Key)));

					if (CtrlObject->Cp()->ActivePanel->SendKeyToPlugin(Key, TRUE))
						return TRUE;

					break;
				}
				case MODALTYPE_VIEWER:
					// if(((FileViewer*)CurrentFrame)->ProcessViewerInput(FrameManager->GetLastInputRecord()))
					// return TRUE;
					break;
				case MODALTYPE_EDITOR:
					// if(((FileEditor*)CurrentFrame)->ProcessEditorInput(FrameManager->GetLastInputRecord()))
					// return TRUE;
					break;
				case MODALTYPE_DIALOG:
					//((Dialog*)CurrentFrame)->CallDlgProc(DN_KEY,((Dialog*)CurrentFrame)->GetDlgFocusPos(),Key);
					break;
				case MODALTYPE_VMENU:
				case MODALTYPE_HELP:
				case MODALTYPE_COMBOBOX:
				case MODALTYPE_USER:
				case MODALTYPE_FINDFOLDER:
				default:
					break;
			}
		}

		/*** БЛОК ПРИВЕЛЕГИРОВАННЫХ КЛАВИШ ! ***/

		/***   КОТОРЫЕ НЕЛЬЗЯ НАМАКРОСИТЬ    ***/
		switch (Key) {
			case KEY_ALT | KEY_NUMPAD0:
			case KEY_ALTINS: {
				Grabber::Run();
				return TRUE;
			}
			case KEY_CONSOLE_BUFFER_RESIZE:
				WINPORT(Sleep)(10);
				ResizeAllFrame();
				return TRUE;
		}

		/*** А вот здесь - все остальное! ***/
		if (!IsProcessAssignMacroKey)
		// в любом случае если кому-то ненужны все клавиши или
		{
			switch (Key) {
				case KEY_CTRLW:
					ShowProcessList();
					return TRUE;
				case KEY_F11:
					PluginsMenu();
					FrameManager->RefreshFrame();
					//_MANAGER(SysLog(-1));
					return TRUE;
				case KEY_ALTF9: {
					//_MANAGER(SysLog(1,"Manager::ProcessKey, KEY_ALTF9 pressed..."));
					WINPORT(Sleep)(10);
					ToggleVideoMode();
					WINPORT(Sleep)(10);

					/*
						В процессе исполнения Alt-F9 (в нормальном режиме) в очередь
						консоли попадает WINDOW_BUFFER_SIZE_EVENT, формируется в
						ToggleVideoMode().
						В режиме исполнения макросов ЭТО не происходит по вполне понятным
						причинам.
					*/
					if (CtrlObject->Macro.IsExecuting()) {
						int PScrX = ScrX;
						int PScrY = ScrY;
						WINPORT(Sleep)(10);
						GetVideoMode(CurSize);

						if (PScrX + 1 == CurSize.X && PScrY + 1 == CurSize.Y) {
							//_MANAGER(SysLog(-1,"GetInputRecord(WINDOW_BUFFER_SIZE_EVENT); return KEY_NONE"));
							return TRUE;
						} else {
							PrevScrX = PScrX;
							PrevScrY = PScrY;
							//_MANAGER(SysLog(-1,"GetInputRecord(WINDOW_BUFFER_SIZE_EVENT); return KEY_CONSOLE_BUFFER_RESIZE"));
							WINPORT(Sleep)(10);
							return ProcessKey(KEY_CONSOLE_BUFFER_RESIZE);
						}
					}

					//_MANAGER(SysLog(-1));
					return TRUE;
				}
				case KEY_F12: {
					auto CurFrame=FrameManager->GetCurrentFrame();
					int TypeFrame=CurFrame->GetType();

					if ((TypeFrame != MODALTYPE_HELP && TypeFrame != MODALTYPE_DIALOG) || CurFrame->GetCanLoseFocus())
					{
						DeactivateFrame(FrameMenu(),0);
						return TRUE;
					}

					break;	// отдадим F12 дальше по цепочке
				}

				case KEY_CTRLALTSHIFTPRESS:
				case KEY_RCTRLALTSHIFTPRESS: {
					if (!(Opt.CASRule & 1) && Key == KEY_CTRLALTSHIFTPRESS)
						break;

					if (!(Opt.CASRule & 2) && Key == KEY_RCTRLALTSHIFTPRESS)
						break;

					if (!Opt.OnlyEditorViewerUsed) {
						if (CurrentFrame->FastHide()) {
							int isPanelFocus = CurrentFrame->GetType() == MODALTYPE_PANELS;

							if (isPanelFocus) {
								int LeftVisible = CtrlObject->Cp()->LeftPanel->IsVisible();
								int RightVisible = CtrlObject->Cp()->RightPanel->IsVisible();
								int CmdLineVisible = CtrlObject->CmdLine->IsVisible();
								int KeyBarVisible = CtrlObject->Cp()->MainKeyBar.IsVisible();
								CtrlObject->CmdLine->ShowBackground();
								CtrlObject->Cp()->LeftPanel->Hide0();
								CtrlObject->Cp()->RightPanel->Hide0();

								switch (Opt.PanelCtrlAltShiftRule) {
									case 0:
										CtrlObject->CmdLine->Show();
										CtrlObject->Cp()->MainKeyBar.Refresh(true);
										break;
									case 1:
										CtrlObject->Cp()->MainKeyBar.Refresh(true);
										break;
								}
								WaitKey(Key == KEY_CTRLALTSHIFTPRESS
												? KEY_CTRLALTSHIFTRELEASE
												: KEY_RCTRLALTSHIFTRELEASE);

								if (LeftVisible)
									CtrlObject->Cp()->LeftPanel->Show();

								if (RightVisible)
									CtrlObject->Cp()->RightPanel->Show();

								if (CmdLineVisible)
									CtrlObject->CmdLine->Show();

								CtrlObject->Cp()->MainKeyBar.Refresh(KeyBarVisible);
							} else {
								ImmediateHide();
								WaitKey(Key == KEY_CTRLALTSHIFTPRESS
												? KEY_CTRLALTSHIFTRELEASE
												: KEY_RCTRLALTSHIFTRELEASE);
							}

							FrameManager->RefreshFrame();
						}

						return TRUE;
					}

					break;
				}
				case KEY_CTRLTAB:
				case KEY_CTRLSHIFTTAB:

					if (CurrentFrame->GetCanLoseFocus()) {
						DeactivateFrame(CurrentFrame, Key == KEY_CTRLTAB ? 1 : -1);
					}

					_MANAGER(SysLog(-1));
					return TRUE;
			}
		}

		CurrentFrame->UpdateKeyBar();
		CurrentFrame->ProcessKey(Key);
	}

	_MANAGER(SysLog(-1));
	return ret;
}

int Manager::ProcessMouse(MOUSE_EVENT_RECORD *MouseEvent)
{
	// При каптюренной мыши отдаем управление заданному объекту
	// if (ScreenObject::CaptureMouseObject)
	// return ScreenObject::CaptureMouseObject->ProcessMouse(MouseEvent);
	int ret = FALSE;

	//_D(SysLog(1,"Manager::ProcessMouse()"));
	if (CurrentFrame)
		ret = CurrentFrame->ProcessMouse(MouseEvent);

	//_D(SysLog(L"Manager::ProcessMouse() ret=%i",ret));
	_MANAGER(SysLog(-1));
	return ret;
}

void Manager::PluginsMenu()
{
	_MANAGER(SysLog(1));
	int curType = CurrentFrame->GetType();

	if (curType == MODALTYPE_PANELS || curType == MODALTYPE_EDITOR || curType == MODALTYPE_VIEWER
			|| curType == MODALTYPE_DIALOG) {
		/*
			02.01.2002 IS
			! Вывод правильной помощи по Shift-F1 в меню плагинов в редакторе/вьюере/диалоге
			! Если на панели QVIEW или INFO открыт файл, то считаем, что это
			полноценный вьюер и запускаем с соответствующим параметром плагины
		*/
		if (curType == MODALTYPE_PANELS) {
			int pType = CtrlObject->Cp()->ActivePanel->GetType();

			if (pType == QVIEW_PANEL || pType == INFO_PANEL) {
				FARString strType, strCurFileName;
				CtrlObject->Cp()->GetTypeAndName(strType, strCurFileName);

				if (!strCurFileName.IsEmpty()) {
					DWORD Attr = apiGetFileAttributes(strCurFileName);

					// интересуют только обычные файлы
					if (Attr != INVALID_FILE_ATTRIBUTES && !(Attr & FILE_ATTRIBUTE_DIRECTORY))
						curType = MODALTYPE_VIEWER;
				}
			}
		}

		// в редакторе, вьюере или диалоге покажем свою помощь по Shift-F1
		const wchar_t *Topic = curType == MODALTYPE_EDITOR ? L"Editor"
				: curType == MODALTYPE_VIEWER ? L"Viewer"
				: curType == MODALTYPE_DIALOG
				? L"Dialog"
				: nullptr;
		CtrlObject->Plugins.CommandsMenu(curType, 0, Topic);
	}

	_MANAGER(SysLog(-1));
}

BOOL Manager::IsPanelsActive()
{
	if (FramePos >= 0) {
		return CurrentFrame ? CurrentFrame->GetType() == MODALTYPE_PANELS : FALSE;
	} else {
		return FALSE;
	}
}

Frame *Manager::operator[](int Index)
{
	if (Index < 0 || Index >= FrameCount || !FrameList) {
		return nullptr;
	}

	return FrameList[Index];
}

int Manager::IndexOfStack(Frame *Frame)
{
	int Result = -1;

	for (int i = 0; i < ModalStackCount; i++) {
		if (Frame == ModalStack[i]) {
			Result = i;
			break;
		}
	}

	return Result;
}

int Manager::IndexOf(Frame *Frame)
{
	int Result = -1;

	for (int i = 0; i < FrameCount; i++) {
		if (Frame == FrameList[i]) {
			Result = i;
			break;
		}
	}

	return Result;
}

BOOL Manager::Commit()
{
	_MANAGER(CleverSysLog clv(L"Manager::Commit()"));
	_MANAGER(ManagerClass_Dump(L"ManagerClass"));
	int Result = false;

	if (DeletedFrame && (InsertedFrame || ExecutedFrame)) {
		UpdateCommit();
		DeletedFrame = nullptr;
		InsertedFrame = nullptr;
		ExecutedFrame = nullptr;
		Result = true;
	} else if (ExecutedFrame) {
		ExecuteCommit();
		ExecutedFrame = nullptr;
		Result = true;
	} else if (DeletedFrame) {
		DeleteCommit();
		DeletedFrame = nullptr;
		Result = true;
	} else if (InsertedFrame) {
		InsertCommit();
		InsertedFrame = nullptr;
		Result = true;
	} else if (DeactivatedFrame) {
		DeactivateCommit();
		DeactivatedFrame = nullptr;
		Result = true;
	} else if (ActivatedFrame) {
		ActivateCommit();
		ActivatedFrame = nullptr;
		Result = true;
	} else if (RefreshedFrame) {
		RefreshCommit();
		RefreshedFrame = nullptr;
		Result = true;
	} else if (ModalizedFrame) {
		ModalizeCommit();
		// ModalizedFrame=nullptr;
		Result = true;
	} else if (UnmodalizedFrame) {
		UnmodalizeCommit();
		// UnmodalizedFrame=nullptr;
		Result = true;
	}

	if (Result) {
		Result = Commit();
	}

	return Result;
}

void Manager::DeactivateCommit()
{
	_MANAGER(CleverSysLog clv(L"Manager::DeactivateCommit()"));
	_MANAGER(SysLog(L"DeactivatedFrame=%p", DeactivatedFrame));

	/*
		$ 18.04.2002 skv
		Если нечего активировать, то в общем-то не надо и деактивировать.
	*/
	if (!DeactivatedFrame || !ActivatedFrame) {
		return;
	}

	if (!ActivatedFrame) {
		_MANAGER("WARNING! !ActivatedFrame");
	}

	if (DeactivatedFrame) {
		DeactivatedFrame->OnChangeFocus(0);
	}

	int modalIndex = IndexOfStack(DeactivatedFrame);

	if (-1 != modalIndex && modalIndex == ModalStackCount - 1) {
		/*if (IsSemiModalBackFrame(ActivatedFrame))
		{ // Является ли "родителем" полумодального фрэйма?
			ModalStackCount--;
		}
		else
		{*/
		if (IndexOfStack(ActivatedFrame) == -1) {
			ModalStack[ModalStackCount - 1] = ActivatedFrame;
		} else {
			ModalStackCount--;
		}

		//}
	}
}

void Manager::ActivateCommit()
{
	_MANAGER(CleverSysLog clv(L"Manager::ActivateCommit()"));
	_MANAGER(SysLog(L"ActivatedFrame=%p", ActivatedFrame));

	if (CurrentFrame == ActivatedFrame) {
		RefreshedFrame = ActivatedFrame;
		return;
	}

	int FrameIndex = IndexOf(ActivatedFrame);

	if (-1 != FrameIndex) {
		FramePos = FrameIndex;
	}

	/*
		14.05.2002 SKV
		Если мы пытаемся активировать полумодальный фрэйм,
		то надо его вытащит на верх стэка модалов.
	*/

	for (int I = 0; I < ModalStackCount; I++) {
		if (ModalStack[I] == ActivatedFrame) {
			Frame *tmp = ModalStack[I];
			ModalStack[I] = ModalStack[ModalStackCount - 1];
			ModalStack[ModalStackCount - 1] = tmp;
			break;
		}
	}

	RefreshedFrame = CurrentFrame = ActivatedFrame;
}

void Manager::UpdateCommit()
{
	_MANAGER(CleverSysLog clv(L"Manager::UpdateCommit()"));
	_MANAGER(SysLog(L"DeletedFrame=%p, InsertedFrame=%p, ExecutedFrame=%p", DeletedFrame, InsertedFrame,
			ExecutedFrame));

	if (ExecutedFrame) {
		DeleteCommit();
		ExecuteCommit();
		return;
	}

	int FrameIndex = IndexOf(DeletedFrame);

	if (-1 != FrameIndex) {
		ActivateFrame(FrameList[FrameIndex] = InsertedFrame);
		ActivatedFrame->FrameToBack = CurrentFrame;
		DeleteCommit();
	} else {
		_MANAGER(SysLog(L"ERROR! DeletedFrame not found"));
	}
}

//! Удаляет DeletedFrame изо всех очередей!
//! Назначает следующий активный, (исходя из своих представлений)
//! Но только в том случае, если активный фрейм еще не назначен заранее.
void Manager::DeleteCommit()
{
	_MANAGER(CleverSysLog clv(L"Manager::DeleteCommit()"));
	_MANAGER(SysLog(L"DeletedFrame=%p", DeletedFrame));

	if (!DeletedFrame) {
		return;
	}

	// <ifDoubleInstance>
	// BOOL ifDoubI=ifDoubleInstance(DeletedFrame);
	// </ifDoubleInstance>
	int ModalIndex = IndexOfStack(DeletedFrame);

	if (ModalIndex != -1) {
		/*
			$ 14.05.2002 SKV
			Надёжнее найти и удалить именно то, что
			нужно, а не просто верхний.
		*/
		for (int i = 0; i < ModalStackCount; i++) {
			if (ModalStack[i] == DeletedFrame) {
				for (int j = i + 1; j < ModalStackCount; j++) {
					ModalStack[j - 1] = ModalStack[j];
				}

				ModalStackCount--;
				break;
			}
		}

		if (ModalStackCount) {
			ActivateFrame(ModalStack[ModalStackCount - 1]);
		}
	}

	for (int i = 0; i < FrameCount; i++) {
		if (FrameList[i]->FrameToBack == DeletedFrame) {
			FrameList[i]->FrameToBack = CtrlObject->Cp();
		}
	}

	int FrameIndex = IndexOf(DeletedFrame);

	if (-1 != FrameIndex) {
		DeletedFrame->DestroyAllModal();

		for (int j = FrameIndex; j < FrameCount - 1; j++) {
			FrameList[j] = FrameList[j + 1];
		}

		FrameCount--;

		if (FramePos >= FrameCount) {
			FramePos = 0;
		}

		if (DeletedFrame->FrameToBack == CtrlObject->Cp()) {
			ActivateFrame(FrameList[FramePos]);
		} else {
			ActivateFrame(DeletedFrame->FrameToBack);
		}
	}

	/*
		$ 14.05.2002 SKV
		Долго не мог понять, нужен всё же этот код или нет.
		Но вроде как нужен.
		SVS> Когда понадобится - в некоторых местах расскомментить куски кода
			помеченные скобками <ifDoubleInstance>

	if (ifDoubI && IsSemiModalBackFrame(ActivatedFrame)){
		for(int i=0;i<ModalStackCount;i++)
		{
			if(ModalStack[i]==ActivatedFrame)
			{
				break;
			}
		}

		if(i==ModalStackCount)
		{
			if (ModalStackCount == ModalStackSize){
				ModalStack = (Frame **) realloc (ModalStack, ++ModalStackSize * sizeof (Frame *));
			}
			ModalStack[ModalStackCount++]=ActivatedFrame;
		}
	}
	*/
	DeletedFrame->OnDestroy();

	if (DeletedFrame->GetDynamicallyBorn()) {
		_MANAGER(SysLog(L"delete DeletedFrame %p, CurrentFrame=%p", DeletedFrame, CurrentFrame));

		if (CurrentFrame == DeletedFrame)
			CurrentFrame = 0;

		/*
			$ 14.05.2002 SKV
			Так как в деструкторе фрэйма неявно может быть
			вызван commit, то надо подстраховаться.
		*/
		Frame *tmp = DeletedFrame;
		DeletedFrame = nullptr;
		delete tmp;
	}

	// Полагаемся на то, что в ActevateFrame не будет переписан уже
	// присвоенный ActivatedFrame
	if (ModalStackCount) {
		ActivateFrame(ModalStack[ModalStackCount - 1]);
	} else {
		ActivateFrame(FramePos);
	}
}

void Manager::InsertCommit()
{
	_MANAGER(CleverSysLog clv(L"Manager::InsertCommit()"));
	_MANAGER(SysLog(L"InsertedFrame=%p", InsertedFrame));

	if (InsertedFrame) {
		if (FrameListSize <= FrameCount) {
			FrameList = (Frame **)realloc(FrameList, sizeof(*FrameList) * (FrameCount + 1));
			FrameListSize++;
		}

		InsertedFrame->FrameToBack = CurrentFrame;
		FrameList[FrameCount] = InsertedFrame;

		if (!ActivatedFrame) {
			ActivatedFrame = InsertedFrame;
		}

		FrameCount++;
	}
}

void Manager::RefreshCommit()
{
	_MANAGER(CleverSysLog clv(L"Manager::RefreshCommit()"));
	_MANAGER(SysLog(L"RefreshedFrame=%p", RefreshedFrame));

	if (!RefreshedFrame)
		return;

	if (IndexOf(RefreshedFrame) == -1 && IndexOfStack(RefreshedFrame) == -1)
		return;

	if (!RefreshedFrame->Locked()) {
		if (!IsRedrawFramesInProcess)
			RefreshedFrame->ShowConsoleTitle();

		if (RefreshedFrame)
			RefreshedFrame->Refresh();

		if (!RefreshedFrame)
			return;

		CtrlObject->Macro.SetMode(RefreshedFrame->GetMacroMode());
	}

	if ((RefreshedFrame->GetType() == MODALTYPE_EDITOR || RefreshedFrame->GetType() == MODALTYPE_VIEWER)
			? Opt.ViewerEditorClock
			: (WaitInMainLoop && Opt.Clock))
		ShowTime(1);
}

void Manager::ExecuteCommit()
{
	_MANAGER(CleverSysLog clv(L"Manager::ExecuteCommit()"));
	_MANAGER(SysLog(L"ExecutedFrame=%p", ExecutedFrame));

	if (!ExecutedFrame) {
		return;
	}

	if (ModalStackCount == ModalStackSize) {
		ModalStack = (Frame **)realloc(ModalStack, ++ModalStackSize * sizeof(Frame *));
	}

	ModalStack[ModalStackCount++] = ExecutedFrame;
	ActivatedFrame = ExecutedFrame;
}

/*
	$ 26.06.2001 SKV
	Для вызова из плагинов посредством ACTL_COMMIT
*/
BOOL Manager::PluginCommit()
{
	return Commit();
}

/* $ Введена для нужд CtrlAltShift OT */
void Manager::ImmediateHide()
{
	if (FramePos < 0)
		return;

	// Сначала проверяем, есть ли у прятываемого фрейма SaveScreen
	if (CurrentFrame->HasSaveScreen()) {
		CurrentFrame->Hide();
		return;
	}

	// Фреймы перерисовываются, значит для нижних
	// не выставляем заголовок консоли, чтобы не мелькал.
	if (ModalStackCount > 0) {
		/*
			$ 28.04.2002 KM
			Проверим, а не модальный ли редактор или вьювер на вершине
			модального стека? И если да, покажем User screen.
		*/
		if (ModalStack[ModalStackCount - 1]->GetType() == MODALTYPE_EDITOR
				|| ModalStack[ModalStackCount - 1]->GetType() == MODALTYPE_VIEWER) {
			if (CtrlObject->CmdLine)
				CtrlObject->CmdLine->ShowBackground();
		} else {
			int UnlockCount = 0;
			IsRedrawFramesInProcess++;

			while ((*this)[FramePos]->Locked()) {
				(*this)[FramePos]->Unlock();
				UnlockCount++;
			}

			RefreshFrame((*this)[FramePos]);
			Commit();

			for (int i = 0; i < UnlockCount; i++) {
				(*this)[FramePos]->Lock();
			}

			if (ModalStackCount > 1) {
				for (int i = 0; i < ModalStackCount - 1; i++) {
					if (!(ModalStack[i]->FastHide() & CASR_HELP)) {
						RefreshFrame(ModalStack[i]);
						Commit();
					} else {
						break;
					}
				}
			}

			/*
				$ 04.04.2002 KM
				Перерисуем заголовок только у активного фрейма.
				Этим мы предотвращаем мелькание заголовка консоли
				при перерисовке всех фреймов.
			*/
			IsRedrawFramesInProcess--;
			CurrentFrame->ShowConsoleTitle();
		}
	} else {
		if (CtrlObject->CmdLine)
			CtrlObject->CmdLine->ShowBackground();
	}
}

void Manager::ModalizeCommit()
{
	CurrentFrame->Push(ModalizedFrame);
	ModalizedFrame = nullptr;
}

void Manager::UnmodalizeCommit()
{
	Frame *iFrame;

	for (int i = 0; i < FrameCount; i++) {
		iFrame = FrameList[i];

		if (iFrame->RemoveModal(UnmodalizedFrame)) {
			break;
		}
	}

	for (int i = 0; i < ModalStackCount; i++) {
		iFrame = ModalStack[i];

		if (iFrame->RemoveModal(UnmodalizedFrame)) {
			break;
		}
	}

	UnmodalizedFrame = nullptr;
}

BOOL Manager::ifDoubleInstance(Frame *frame)
{
	// <ifDoubleInstance>
	/*
		if (ModalStackCount<=0)
			return FALSE;
		if(IndexOfStack(frame)==-1)
			return FALSE;
		if(IndexOf(frame)!=-1)
			return TRUE;
	*/
	// </ifDoubleInstance>
	return FALSE;
}

/*
	Вызов ResizeConsole для всех NextModal у
	модального фрейма. KM
*/
void Manager::ResizeAllModal(Frame *ModalFrame)
{
	if (!ModalFrame->NextModal)
		return;

	Frame *iModal = ModalFrame->NextModal;

	while (iModal) {
		iModal->ResizeConsole();
		iModal = iModal->NextModal;
	}
}

void Manager::ResizeAllFrame()
{
	ScrBuf.Lock();
	for (int i = 0; i < FrameCount; i++) {
		FrameList[i]->ResizeConsole();
	}

	for (int i = 0; i < ModalStackCount; i++) {
		ModalStack[i]->ResizeConsole();
		/*
			$ 13.04.2002 KM
			- А теперь проресайзим все NextModal...
		*/
		ResizeAllModal(ModalStack[i]);
	}

	ImmediateHide();
	FrameManager->RefreshFrame();
	// RefreshFrame();
	ScrBuf.Unlock();
}

void Manager::InitKeyBar()
{
	for (int I = 0; I < FrameCount; I++)
		FrameList[I]->InitKeyBar();
}

/*void Manager::AddSemiModalBackFrame(Frame* frame)
{
	if(SemiModalBackFramesCount>=SemiModalBackFramesSize)
	{
		SemiModalBackFramesSize+=4;
		SemiModalBackFrames=
			(Frame**)realloc(SemiModalBackFrames,sizeof(Frame*)*SemiModalBackFramesSize);

	}
	SemiModalBackFrames[SemiModalBackFramesCount]=frame;
	SemiModalBackFramesCount++;
}

BOOL Manager::IsSemiModalBackFrame(Frame *frame)
{
	if(!SemiModalBackFrames)return FALSE;
	for(int i=0;i<SemiModalBackFramesCount;i++)
	{
		if(SemiModalBackFrames[i]==frame)return TRUE;
	}
	return FALSE;
}

void Manager::RemoveSemiModalBackFrame(Frame* frame)
{
	if(!SemiModalBackFrames)return;
	for(int i=0;i<SemiModalBackFramesCount;i++)
	{
		if(SemiModalBackFrames[i]==frame)
		{
			for(int j=i+1;j<SemiModalBackFramesCount;j++)
			{
				SemiModalBackFrames[j-1]=SemiModalBackFrames[j];
			}
			SemiModalBackFramesCount--;
			return;
		}
	}
}
*/

// возвращает top-модал или сам фрейм, если у фрейма нету модалов
Frame *Manager::GetTopModal()
{
	Frame *f = CurrentFrame, *fo = nullptr;

	while (f) {
		fo = f;
		f = f->GetTopModal();
	}

	if (!f)
		f = fo;

	return f;
}

/*
	$ 15.05.2002 SKV
	Так как нужно это в разных местах,
	а глобальные счётчики не концептуально,
	то лучше это делать тут.
*/
void Manager::ExecuteModalEV()
{
	++ModalEVCount;
	ExecuteModal();
	--ModalEVCount;
}

/////////

LockFrame::LockFrame(Frame *frame)
	:
	_frame(frame), _refresh(false)
{
	if (_frame)
		_frame->Lock();
}

LockFrame::~LockFrame()
{
	if (_frame)
		_frame->Unlock();

	if (_refresh && FrameManager)
		FrameManager->RefreshFrame(_frame);
}

///

LockBottomFrame::LockBottomFrame()
	:
	LockFrame(FrameManager ? FrameManager->GetBottomFrame() : nullptr)
{}

LockCurrentFrame::LockCurrentFrame()
	:
	LockFrame(FrameManager ? FrameManager->GetCurrentFrame() : nullptr)
{}
