/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 *
 */

#include "common/timer.h"
#include "common/util.h"

#include "sci/sci.h"
#include "sci/debug.h"	// for g_debug_sleeptime_factor
#include "sci/event.h"
#include "sci/engine/state.h"
#include "sci/gui/gui.h"
#include "sci/gui/gui_screen.h"
#include "sci/gui/gui_palette.h"
#include "sci/gui/gui_cursor.h"
#include "sci/gui/gui_gfx.h"
#include "sci/gui/gui_windowmgr.h"
#include "sci/gui/gui_animate.h"
#include "sci/gui/gui_controls.h"
#include "sci/gui/gui_menu.h"
#include "sci/gui/gui_text.h"
#include "sci/gui/gui_transitions.h"
#include "sci/gui/gui_view.h"

#include "sci/gfx/operations.h"

namespace Sci {

// for debug purposes
// class SciGui32 : public SciGui {
// public:
// 	SciGui32(EngineState *s, SciGuiScreen *screen, SciGuiPalette *palette, SciGuiCursor *cursor);
// 	~SciGui32();
// };

SciGui::SciGui(EngineState *state, SciGuiScreen *screen, SciGuiPalette *palette, SciGuiCursor *cursor)
	: _s(state), _screen(screen), _palette(palette), _cursor(cursor) {

	_gfx = new SciGuiGfx(_s->resMan, _s->_segMan, _s->_kernel, _screen, _palette);
	_transitions = new SciGuiTransitions(this, _screen, _palette, _s->resMan->isVGA());
	_animate = new SciGuiAnimate(_s, _gfx, _screen, _palette);
	_text = new SciGuiText(_s->resMan, _gfx, _screen);
	_windowMgr = new SciGuiWindowMgr(this, _screen, _gfx, _text);
	_controls = new SciGuiControls(_s->_segMan, _gfx, _text);
	_menu = new SciGuiMenu(_s->_event, _s->_segMan, _gfx, _text, _screen, _cursor);
//  	_gui32 = new SciGui32(_s, _screen, _palette, _cursor); // for debug purposes
}

SciGui::SciGui() {
}

SciGui::~SciGui() {
	delete _menu;
	delete _controls;
	delete _windowMgr;
	delete _text;
	delete _animate;
	delete _transitions;
	delete _gfx;
}

void SciGui::resetEngineState(EngineState *s) {
	_s = s;
	_animate->resetEngineState(s);
}

void SciGui::init(bool usesOldGfxFunctions) {
	_usesOldGfxFunctions = usesOldGfxFunctions;

	_gfx->init(_text);
	_windowMgr->init(_s->_gameId);
	_menu->init(_s->gfx_state);
	initPriorityBands();
}

void SciGui::initPriorityBands() {
	if (_usesOldGfxFunctions) {
		_gfx->PriorityBandsInit(15, 42, 200);
	} else {
		_gfx->PriorityBandsInit(14, 42, 190);
	}
}

void SciGui::wait(int16 ticks) {
	uint32 time;

	time = g_system->getMillis();
	_s->r_acc = make_reg(0, ((long)time - (long)_s->last_wait_time) * 60 / 1000);
	_s->last_wait_time = time;

	ticks *= g_debug_sleeptime_factor;
	kernel_sleep(_s->_event, ticks * 1000 / 60);
}

void SciGui::setPort(uint16 portPtr) {
	switch (portPtr) {
	case 0:
		_gfx->SetPort(_windowMgr->_wmgrPort);
		break;
	case 0xFFFF:
		_gfx->SetPort(_gfx->_menuPort);
		break;
	default:
		_gfx->SetPort(_windowMgr->getPortById(portPtr));
	};
}

Common::Rect SciGui::getPortPic(int16 &picTop, int16 &picLeft) {
	picTop = _windowMgr->_picWind->top;
	picLeft = _windowMgr->_picWind->left;
	return _windowMgr->_picWind->rect;
}

void SciGui::setPortPic(Common::Rect rect, int16 picTop, int16 picLeft, bool initPriorityBandsFlag) {
	_windowMgr->_picWind->rect = rect;
	_windowMgr->_picWind->top = picTop;
	_windowMgr->_picWind->left = picLeft;
	if (initPriorityBandsFlag)
		initPriorityBands();
}

reg_t SciGui::getPort() {
	return make_reg(0, _gfx->GetPort()->id);
}

void SciGui::globalToLocal(int16 *x, int16 *y) {
	GuiPort *curPort = _gfx->GetPort();
	*x = *x - curPort->left;
	*y = *y - curPort->top;
}

void SciGui::localToGlobal(int16 *x, int16 *y) {
	GuiPort *curPort = _gfx->GetPort();
	*x = *x + curPort->left;
	*y = *y + curPort->top;
}

int16 SciGui::coordinateToPriority(int16 y) {
	return _gfx->CoordinateToPriority(y);
}

int16 SciGui::priorityToCoordinate(int16 priority) {
	return _gfx->PriorityToCoordinate(priority);
}

reg_t SciGui::newWindow(Common::Rect dims, Common::Rect restoreRect, uint16 style, int16 priority, int16 colorPen, int16 colorBack, const char *title) {
	GuiWindow *wnd = NULL;

	if (restoreRect.top != 0 && restoreRect.left != 0 && restoreRect.height() != 0 && restoreRect.width() != 0)
		wnd = _windowMgr->NewWindow(dims, &restoreRect, title, style, priority, false);
	else
		wnd = _windowMgr->NewWindow(dims, NULL, title, style, priority, false);
	wnd->penClr = colorPen;
	wnd->backClr = colorBack;
	_windowMgr->DrawWindow(wnd);

	return make_reg(0, wnd->id);
}

void SciGui::disposeWindow(uint16 windowPtr, bool reanimate) {
	GuiWindow *wnd = (GuiWindow *)_windowMgr->getPortById(windowPtr);
	_windowMgr->DisposeWindow(wnd, reanimate);
}

#define SCI_DISPLAY_MOVEPEN				100
#define SCI_DISPLAY_SETALIGNMENT		101
#define SCI_DISPLAY_SETPENCOLOR			102
#define SCI_DISPLAY_SETBACKGROUNDCOLOR	103
#define SCI_DISPLAY_SETGREYEDOUTPUT		104
#define SCI_DISPLAY_SETFONT				105
#define SCI_DISPLAY_WIDTH				106
#define SCI_DISPLAY_SAVEUNDER			107
#define SCI_DISPLAY_RESTOREUNDER		108
#define SCI_DISPLAY_DONTSHOWBITS		121

void SciGui::display(const char *text, int argc, reg_t *argv) {
	int displayArg;
	GuiTextAlignment alignment = SCI_TEXT_ALIGNMENT_LEFT;
	int16 bgcolor = -1, width = -1, bRedraw = 1;
	bool doSaveUnder = false;
	Common::Rect rect;

	// Make a "backup" of the port settings
	GuiPort oldPort = *_gfx->GetPort();

	// setting defaults
	_gfx->PenMode(0);
	_gfx->PenColor(0);
	_gfx->TextGreyedOutput(false);
	// processing codes in argv
	while (argc > 0) {
		displayArg = argv[0].toUint16();
		argc--; argv++;
		switch (displayArg) {
		case SCI_DISPLAY_MOVEPEN:
			_gfx->MoveTo(argv[0].toUint16(), argv[1].toUint16());
			argc -= 2; argv += 2;
			break;
		case SCI_DISPLAY_SETALIGNMENT:
			alignment = argv[0].toSint16();
			argc--; argv++;
			break;
		case SCI_DISPLAY_SETPENCOLOR:
			_gfx->PenColor(argv[0].toUint16());
			argc--; argv++;
			break;
		case SCI_DISPLAY_SETBACKGROUNDCOLOR:
			bgcolor = argv[0].toUint16();
			argc--; argv++;
			break;
		case SCI_DISPLAY_SETGREYEDOUTPUT:
			_gfx->TextGreyedOutput(argv[0].isNull() ? false : true);
			argc--; argv++;
			break;
		case SCI_DISPLAY_SETFONT:
			_text->SetFont(argv[0].toUint16());
			argc--; argv++;
			break;
		case SCI_DISPLAY_WIDTH:
			width = argv[0].toUint16();
			argc--; argv++;
			break;
		case SCI_DISPLAY_SAVEUNDER:
			doSaveUnder = true;
			break;
		case SCI_DISPLAY_RESTOREUNDER:
			_gfx->BitsGetRect(argv[0], &rect);
			rect.translate(-_gfx->GetPort()->left, -_gfx->GetPort()->top);
			_gfx->BitsRestore(argv[0]);
			graphRedrawBox(rect);
			// finishing loop
			argc = 0;
			break;
		case SCI_DISPLAY_DONTSHOWBITS:
			bRedraw = 0;
			break;
		default:
			warning("Unknown kDisplay argument %X", displayArg);
			break;
		}
	}

	// now drawing the text
	_text->Size(rect, text, -1, width);
	rect.moveTo(_gfx->GetPort()->curLeft, _gfx->GetPort()->curTop);
	_gfx->Move(rect.right <= _screen->_width ? 0 : _screen->_width - rect.right, rect.bottom <= _screen->_height ? 0 : _screen->_width - rect.bottom);
	rect.moveTo(_gfx->GetPort()->curLeft, _gfx->GetPort()->curTop);

	if (doSaveUnder)
		_s->r_acc = _gfx->BitsSave(rect, SCI_SCREEN_MASK_VISUAL);
	if (bgcolor != -1)
		_gfx->FillRect(rect, SCI_SCREEN_MASK_VISUAL, bgcolor, 0, 0);
	_text->Box(text, 0, rect, alignment, -1);
	if (_screen->_picNotValid == 0 && bRedraw)
		_gfx->BitsShow(rect);
	// restoring port and cursor pos
	GuiPort *currport = _gfx->GetPort();
	uint16 tTop = currport->curTop;
	uint16 tLeft = currport->curLeft;
	*currport = oldPort;
	currport->curTop = tTop;
	currport->curLeft = tLeft;
}

void SciGui::textSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) {
	Common::Rect rect(0, 0, *textWidth, *textHeight);
	_text->Size(rect, text, font, maxWidth);
	*textWidth = rect.width();
	*textHeight = rect.height();
}

// Used SCI1+ for text codes
void SciGui::textFonts(int argc, reg_t *argv) {
	_text->CodeSetFonts(argc, argv);
}

// Used SCI1+ for text codes
void SciGui::textColors(int argc, reg_t *argv) {
	_text->CodeSetColors(argc, argv);
}

void SciGui::drawStatus(const char *text, int16 colorPen, int16 colorBack) {
	GuiPort *oldPort = _gfx->SetPort(_gfx->_menuPort);

	_gfx->FillRect(_gfx->_menuBarRect, 1, colorBack);
	_gfx->PenColor(colorPen);
	_gfx->MoveTo(0, 1);
	_text->Draw_String(text);
	if (_screen->_picNotValid == 0)
		_gfx->BitsShow(_gfx->_menuBarRect);
	_gfx->SetPort(oldPort);
}

void SciGui::drawMenuBar(bool clear) {
	if (!clear) {
		GuiPort *oldPort = _gfx->SetPort(_gfx->_menuPort);
		_menu->drawBar();
		if (_screen->_picNotValid == 0)
			_gfx->BitsShow(_gfx->_menuBarRect);
		_gfx->SetPort(oldPort);
	} else {
		drawStatus("", 0, 0);
	}
}

void SciGui::menuReset() {
	delete _menu;
	_menu = new SciGuiMenu(_s->_event, _s->_segMan, _gfx, _text, _screen, _cursor);
	_menu->init(_s->gfx_state);
}

void SciGui::menuAdd(Common::String title, Common::String content, reg_t contentVmPtr) {
	_menu->add(title, content, contentVmPtr);
}

void SciGui::menuSet(uint16 menuId, uint16 itemId, uint16 attributeId, reg_t value) {
	_menu->setAttribute(menuId, itemId, attributeId, value);
}

reg_t SciGui::menuGet(uint16 menuId, uint16 itemId, uint16 attributeId) {
	return _menu->getAttribute(menuId, itemId, attributeId);
}

reg_t SciGui::menuSelect(reg_t eventObject) {
	return _menu->select(eventObject);
}

void SciGui::drawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) {
	GuiPort *oldPort = _gfx->SetPort((GuiPort *)_windowMgr->_picWind);

	if (_windowMgr->isFrontWindow(_windowMgr->_picWind)) {
		_screen->_picNotValid = 1;
		_gfx->drawPicture(pictureId, animationNr, mirroredFlag, addToFlag, EGApaletteNo);
		_transitions->setup(animationNr, animationBlackoutFlag);
	} else {
		_windowMgr->BeginUpdate(_windowMgr->_picWind);
		_gfx->drawPicture(pictureId, animationNr, mirroredFlag, addToFlag, EGApaletteNo);
		_windowMgr->EndUpdate(_windowMgr->_picWind);
	}
	_gfx->SetPort(oldPort);
}

void SciGui::drawCel(GuiResourceId viewId, GuiViewLoopNo loopNo, GuiViewCelNo celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, int16 origHeight) {
	_gfx->drawCel(viewId, loopNo, celNo, leftPos, topPos, priority, paletteNo, origHeight);
	_palette->setOnScreen();
}

int SciGui::getControlPicNotValid() {
	if (getSciVersion() >= SCI_VERSION_1_1)
		return _screen->_picNotValidSci11;
	return _screen->_picNotValid;
}

void SciGui::drawControlButton(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 style, bool hilite) {
	if (!hilite) {
		rect.grow(1);
		_gfx->EraseRect(rect);
		_gfx->FrameRect(rect);
		rect.grow(-2);
		_gfx->TextGreyedOutput(style & 1 ? false : true);
		_text->Box(text, 0, rect, SCI_TEXT_ALIGNMENT_CENTER, fontId);
		_gfx->TextGreyedOutput(false);
		rect.grow(1);
		if (style & 8) // selected
			_gfx->FrameRect(rect);
		if (!getControlPicNotValid()) {
			rect.grow(1);
			_gfx->BitsShow(rect);
		}
	} else {
		_gfx->InvertRect(rect);
		_gfx->BitsShow(rect);
	}
}

void SciGui::drawControlText(Common::Rect rect, reg_t obj, const char *text, int16 fontId, GuiTextAlignment alignment, int16 style, bool hilite) {
	if (!hilite) {
		rect.grow(1);
		_gfx->EraseRect(rect);
		rect.grow(-1);
		_text->Box(text, 0, rect, alignment, fontId);
		if (style & 8) { // selected
			_gfx->FrameRect(rect);
		}
		rect.grow(1);
		if (!getControlPicNotValid())
			_gfx->BitsShow(rect);
	} else {
		_gfx->InvertRect(rect);
		_gfx->BitsShow(rect);
	}
}

void SciGui::drawControlTextEdit(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 mode, int16 style, int16 cursorPos, int16 maxChars, bool hilite) {
	Common::Rect textRect = rect;
	uint16 oldFontId = _text->GetFontId();

	rect.grow(1);
	_controls->TexteditCursorErase();
	_gfx->EraseRect(rect);
	_text->Box(text, 0, textRect, SCI_TEXT_ALIGNMENT_LEFT, fontId);
	_gfx->FrameRect(rect);
	if (style & 8) {
		_text->SetFont(fontId);
		rect.grow(-1);
		_controls->TexteditCursorDraw(rect, text, cursorPos);
		_text->SetFont(oldFontId);
		rect.grow(1);
	}
	if (!getControlPicNotValid())
		_gfx->BitsShow(rect);
}

void SciGui::drawControlIcon(Common::Rect rect, reg_t obj, GuiResourceId viewId, GuiViewLoopNo loopNo, GuiViewCelNo celNo, int16 style, bool hilite) {
	if (!hilite) {
		_gfx->drawCel(viewId, loopNo, celNo, rect.left, rect.top, 255, 0);
		if (style & 0x20) {
			_gfx->FrameRect(rect);
		}
		if (!getControlPicNotValid())
			_gfx->BitsShow(rect);
	} else {
		_gfx->InvertRect(rect);
		_gfx->BitsShow(rect);
	}
}

void SciGui::drawControlList(Common::Rect rect, reg_t obj, int16 maxChars, int16 count, const char **entries, GuiResourceId fontId, int16 style, int16 upperPos, int16 cursorPos, bool isAlias, bool hilite) {
	if (!hilite) {
		_controls->drawListControl(rect, obj, maxChars, count, entries, fontId, upperPos, cursorPos, isAlias);
		rect.grow(1);
		if (isAlias && (style & 8)) {
			_gfx->FrameRect(rect);
		}
		if (!getControlPicNotValid())
			_gfx->BitsShow(rect);
	}
}

void SciGui::editControl(reg_t controlObject, reg_t eventObject) {
	int16 controlType = GET_SEL32V(_s->_segMan, controlObject, type);

	switch (controlType) {
	case SCI_CONTROLS_TYPE_TEXTEDIT:
		// Only process textedit controls in here
		_controls->TexteditChange(controlObject, eventObject);
		return;
	}
}

void SciGui::graphFillBoxForeground(Common::Rect rect) {
	_gfx->PaintRect(rect);
}

void SciGui::graphFillBoxBackground(Common::Rect rect) {
	_gfx->EraseRect(rect);
}

void SciGui::graphFillBox(Common::Rect rect, uint16 colorMask, int16 color, int16 priority, int16 control) {
	_gfx->FillRect(rect, colorMask, color, priority, control);
}

void SciGui::graphFrameBox(Common::Rect rect, int16 color) {
	int16 oldColor = _gfx->GetPort()->penClr;
	_gfx->PenColor(color);
	_gfx->FrameRect(rect);
	_gfx->PenColor(oldColor);
}

void SciGui::graphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control) {
	_gfx->OffsetLine(startPoint, endPoint);
	_screen->drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, color, priority, control);
}

reg_t SciGui::graphSaveBox(Common::Rect rect, uint16 screenMask) {
	return _gfx->BitsSave(rect, screenMask);
}

reg_t SciGui::graphSaveUpscaledHiresBox(Common::Rect rect) {
	return _gfx->BitsSave(rect, SCI_SCREEN_MASK_DISPLAY);
}

void SciGui::graphRestoreBox(reg_t handle) {
	_gfx->BitsRestore(handle);
}

void SciGui::graphUpdateBox(Common::Rect rect) {
	_gfx->BitsShow(rect);
}

void SciGui::graphRedrawBox(Common::Rect rect) {
	localToGlobal(&rect.left, &rect.top);
	localToGlobal(&rect.right, &rect.bottom);
	GuiPort *oldPort = _gfx->SetPort((GuiPort *)_windowMgr->_picWind);
	globalToLocal(&rect.left, &rect.top);
	globalToLocal(&rect.right, &rect.bottom);

	_animate->reAnimate(rect);

	_gfx->SetPort(oldPort);
}

void SciGui::graphAdjustPriority(int top, int bottom) {
	if (_usesOldGfxFunctions) {
		_gfx->PriorityBandsInit(15, top, bottom);
	} else {
		_gfx->PriorityBandsInit(14, top, bottom);
	}
}

int16 SciGui::picNotValid(int16 newPicNotValid) {
	int16 oldPicNotValid;

	if (getSciVersion() >= SCI_VERSION_1_1) {
		oldPicNotValid = _screen->_picNotValidSci11;

		if (newPicNotValid != -1)
			_screen->_picNotValidSci11 = newPicNotValid;
	} else {
		oldPicNotValid = _screen->_picNotValid;

		if (newPicNotValid != -1)
			_screen->_picNotValid = newPicNotValid;
	}

	return oldPicNotValid;
}


void SciGui::paletteSet(GuiResourceId resourceId, uint16 flags) {
	// we are also called on EGA games as well, this doesnt make sense. doing this would actually break the system EGA palette
	if (!_s->resMan->isVGA())
		return;
   _palette->setFromResource(resourceId, flags);
}

void SciGui::paletteSetFlag(uint16 fromColor, uint16 toColor, uint16 flag) {
	_palette->setFlag(fromColor, toColor, flag);
}

void SciGui::paletteUnsetFlag(uint16 fromColor, uint16 toColor, uint16 flag) {
	_palette->unsetFlag(fromColor, toColor, flag);
}

int16 SciGui::paletteFind(uint16 r, uint16 g, uint16 b) {
	return _palette->matchColor(&_palette->_sysPalette, r, g, b) & 0xFF;
}

void SciGui::paletteSetIntensity(uint16 fromColor, uint16 toColor, uint16 intensity, bool setPalette) {
	_palette->setIntensity(fromColor, toColor, intensity, setPalette);
}

void SciGui::paletteAnimate(uint16 fromColor, uint16 toColor, int16 speed) {
	// we are also called on Amiga as well, but for colors above 32, so it doesnt make sense
	if (!_s->resMan->isVGA())
		return;

	_palette->animate(fromColor, toColor, speed);
}

void SciGui::shakeScreen(uint16 shakeCount, uint16 directions) {
	while (shakeCount--) {
		if (directions & SCI_SHAKE_DIRECTION_VERTICAL)
			_screen->setVerticalShakePos(10);
		// TODO: horizontal shakes
		g_system->updateScreen();
		wait(3);
		if (directions & SCI_SHAKE_DIRECTION_VERTICAL)
			_screen->setVerticalShakePos(0);
		g_system->updateScreen();
		wait(3);
	}
}

uint16 SciGui::onControl(byte screenMask, Common::Rect rect) {
	GuiPort *oldPort = _gfx->SetPort((GuiPort *)_windowMgr->_picWind);
	uint16 result;

	result = _gfx->onControl(screenMask, rect);
	_gfx->SetPort(oldPort);
	return result;
}

void SciGui::animateShowPic() {
	GuiPort *picPort = _windowMgr->_picWind;
	Common::Rect picRect = picPort->rect;
	bool previousCursorState = _cursor->isVisible();

	if (previousCursorState)
		_cursor->hide();
	// Adjust picRect to become relative to screen
	picRect.translate(picPort->left, picPort->top);
	_transitions->doit(picRect);
	if (previousCursorState)
		_cursor->show();
}

void SciGui::animate(reg_t listReference, bool cycle, int argc, reg_t *argv) {
	byte old_picNotValid = _screen->_picNotValid;

	if (listReference.isNull()) {
		_animate->disposeLastCast();
		if (_screen->_picNotValid)
			animateShowPic();
		return;
	}

	List *list = _s->_segMan->lookupList(listReference);
	if (!list)
		error("kAnimate called with non-list as parameter");

	if (cycle) {
		if (!_animate->invoke(list, argc, argv))
			return;
	}

	GuiPort *oldPort = _gfx->SetPort((GuiPort *)_windowMgr->_picWind);
	_animate->disposeLastCast();

	_animate->makeSortedList(list);
	_animate->fill(old_picNotValid);

	if (old_picNotValid) {
		_windowMgr->BeginUpdate(_windowMgr->_picWind);
		_animate->update();
		_windowMgr->EndUpdate(_windowMgr->_picWind);
	}

	_animate->drawCels();

	if (_screen->_picNotValid)
		animateShowPic();

	_animate->updateScreen(old_picNotValid);
	_animate->restoreAndDelete(argc, argv);

	_gfx->SetPort(oldPort);
}

void SciGui::addToPicSetPicNotValid() {
	if (getSciVersion() <= SCI_VERSION_1_EARLY)
		_screen->_picNotValid = 1;
	else
		_screen->_picNotValid = 2;
}

void SciGui::addToPicList(reg_t listReference, int argc, reg_t *argv) {
	List *list;

	_gfx->SetPort((GuiPort *)_windowMgr->_picWind);

	list = _s->_segMan->lookupList(listReference);
	if (!list)
		error("kAddToPic called with non-list as parameter");

	_animate->makeSortedList(list);
	_animate->addToPicDrawCels();

	addToPicSetPicNotValid();
}

void SciGui::addToPicView(GuiResourceId viewId, GuiViewLoopNo loopNo, GuiViewCelNo celNo, int16 leftPos, int16 topPos, int16 priority, int16 control) {
	_gfx->SetPort((GuiPort *)_windowMgr->_picWind);
	_animate->addToPicDrawView(viewId, loopNo, celNo, leftPos, topPos, priority, control);
	addToPicSetPicNotValid();
}

void SciGui::setNowSeen(reg_t objectReference) {
	_gfx->SetNowSeen(objectReference);
}

bool SciGui::canBeHere(reg_t curObject, reg_t listReference) {
	GuiPort *oldPort = _gfx->SetPort((GuiPort *)_windowMgr->_picWind);
	Common::Rect checkRect;
	uint16 signal, controlMask;
	bool result;

	checkRect.left = GET_SEL32V(_s->_segMan, curObject, brLeft);
	checkRect.top = GET_SEL32V(_s->_segMan, curObject, brTop);
	checkRect.right = GET_SEL32V(_s->_segMan, curObject, brRight);
	checkRect.bottom = GET_SEL32V(_s->_segMan, curObject, brBottom);
	signal = GET_SEL32V(_s->_segMan, curObject, signal);
	controlMask = GET_SEL32V(_s->_segMan, curObject, illegalBits);
	result = (_gfx->onControl(SCI_SCREEN_MASK_CONTROL, checkRect) & controlMask) ? false : true;
	if ((result) && (signal & (kSignalIgnoreActor | kSignalRemoveView)) == 0) {
		List *list = _s->_segMan->lookupList(listReference);
		if (!list)
			error("kCanBeHere called with non-list as parameter");

		result = _gfx->CanBeHereCheckRectList(curObject, checkRect, list);
	}
	_gfx->SetPort(oldPort);
	return result;
}

bool SciGui::isItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Point position) {
	SciGuiView *tmpView = _gfx->getView(viewId);
	sciViewCelInfo *celInfo = tmpView->getCelInfo(loopNo, celNo);
	position.x = CLIP<int>(position.x, 0, celInfo->width - 1);
	position.y = CLIP<int>(position.y, 0, celInfo->height - 1);
	byte *celData = tmpView->getBitmap(loopNo, celNo);
	bool result = (celData[position.y * celInfo->width + position.x] == celInfo->clearKey);
	return result;
}

void SciGui::baseSetter(reg_t object) {
	if (lookup_selector(_s->_segMan, object, _s->_kernel->_selectorCache.brLeft, NULL, NULL) == kSelectorVariable) {
		int16 x = GET_SEL32V(_s->_segMan, object, x);
		int16 y = GET_SEL32V(_s->_segMan, object, y);
		int16 z = (_s->_kernel->_selectorCache.z > -1) ? GET_SEL32V(_s->_segMan, object, z) : 0;
		int16 yStep = GET_SEL32V(_s->_segMan, object, yStep);
		GuiResourceId viewId = GET_SEL32V(_s->_segMan, object, view);
		GuiViewLoopNo loopNo = GET_SEL32V(_s->_segMan, object, loop);
		GuiViewCelNo celNo = GET_SEL32V(_s->_segMan, object, cel);

		SciGuiView *tmpView = _gfx->getView(viewId);
		Common::Rect celRect;

		tmpView->getCelRect(loopNo, celNo, x, y, z, &celRect);
		celRect.bottom = y + 1;
		celRect.top = celRect.bottom - yStep;

		PUT_SEL32V(_s->_segMan, object, brLeft, celRect.left);
		PUT_SEL32V(_s->_segMan, object, brRight, celRect.right);
		PUT_SEL32V(_s->_segMan, object, brTop, celRect.top);
		PUT_SEL32V(_s->_segMan, object, brBottom, celRect.bottom);
	}
}

void SciGui::hideCursor() {
	_cursor->hide();
}

void SciGui::showCursor() {
	_cursor->show();
}

void SciGui::setCursorShape(GuiResourceId cursorId) {
	_cursor->setShape(cursorId);
}

void SciGui::setCursorView(GuiResourceId viewNum, int loopNum, int cellNum, Common::Point *hotspot) {
	_cursor->setView(viewNum, loopNum, cellNum, hotspot);
}

void SciGui::setCursorPos(Common::Point pos) {
	pos.y += _gfx->GetPort()->top;
	pos.x += _gfx->GetPort()->left;
	moveCursor(pos);
}

Common::Point SciGui::getCursorPos() {
	return _cursor->getPosition();
}

void SciGui::moveCursor(Common::Point pos) {
	pos.y += _windowMgr->_picWind->rect.top;
	pos.x += _windowMgr->_picWind->rect.left;
	
	pos.y = CLIP<int16>(pos.y, _windowMgr->_picWind->rect.top, _windowMgr->_picWind->rect.bottom - 1);
	pos.x = CLIP<int16>(pos.x, _windowMgr->_picWind->rect.left, _windowMgr->_picWind->rect.right - 1);

	if (pos.x > _screen->_width || pos.y > _screen->_height) {
		warning("attempt to place cursor at invalid coordinates (%d, %d)", pos.y, pos.x);
		return;
	}

	_cursor->setPosition(pos);

	// Trigger event reading to make sure the mouse coordinates will
	// actually have changed the next time we read them.
	_s->_event->get(SCI_EVENT_PEEK);
}

void SciGui::setCursorZone(Common::Rect zone) {
	_cursor->setMoveZone(zone);
}

int16 SciGui::getCelWidth(GuiResourceId viewId, int16 loopNo, int16 celNo) {
	return _gfx->getView(viewId)->getCelInfo(loopNo, celNo)->width;
}

int16 SciGui::getCelHeight(GuiResourceId viewId, int16 loopNo, int16 celNo) {
	return _gfx->getView(viewId)->getCelInfo(loopNo, celNo)->height;
}

int16 SciGui::getLoopCount(GuiResourceId viewId) {
	return _gfx->getView(viewId)->getLoopCount();
}

int16 SciGui::getCelCount(GuiResourceId viewId, int16 loopNo) {
	return _gfx->getView(viewId)->getLoopInfo(loopNo)->celCount;
}

reg_t SciGui::portraitLoad(Common::String resourceName) {
	return NULL_REG;
}

void SciGui::portraitShow(Common::String resourceName, Common::Point position, uint16 resourceNum, uint16 noun, uint16 verb, uint16 cond, uint16 seq) {
}

void SciGui::portraitUnload(uint16 portraitId) {
}

uint16 SciGui::getScreenWidth() {
	return _screen->_displayWidth;
}

uint16 SciGui::getScreenHeight() {
	return _screen->_displayHeight;
}

bool SciGui::debugUndither(bool flag) {
	_screen->unditherSetState(flag);
	return false;
}

bool SciGui::debugShowMap(int mapNo) {
	_screen->debugShowMap(mapNo);
	return false;
}

} // End of namespace Sci
