/* 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.
 *
 */

#include "voyeur/voyeur.h"
#include "voyeur/staticres.h"
#include "voyeur/animation.h"

namespace Voyeur {

void VoyeurEngine::playStamp() {
	_stampLibPtr = NULL;
	_filesManager->openBoltLib("stampblt.blt", _stampLibPtr);

	_stampLibPtr->getBoltGroup(0);
	_controlPtr->_state = _stampLibPtr->boltEntry(_controlPtr->_stateId >> 16)._stateResource;
	assert(_controlPtr->_state);

	_resolvePtr = &RESOLVE_TABLE[0];
	initStamp();

	PtrResource *threadsList = _stampLibPtr->boltEntry(3)._ptrResource;
	_mainThread = threadsList->_entries[0]->_threadResource;
	_mainThread->initThreadStruct(0, 0);

	_voy->_isAM = false;
	_gameHour = 9;
	_gameMinute = 0;
	_voy->_abortInterface = true;

	int buttonId;
	bool breakFlag = false;
	while (!breakFlag && !shouldQuit()) {
		_voyeurArea = AREA_NONE;
		_eventsManager->getMouseInfo();
		_playStampGroupId = _currentVocId = -1;
		_audioVideoId = -1;

		_mainThread->parsePlayCommands();

		bool flag = breakFlag = (_voy->_eventFlags & EVTFLAG_2) != 0;

		switch (_voy->_playStampMode) {
		case 5:
			buttonId = _mainThread->doInterface();
			if (shouldQuit())
				return;

			if (buttonId == -2) {
				switch (_mainThread->doApt()) {
				case 0:
					_voy->_aptLoadMode = 140;
					break;
				case 1:
					_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
					_voy->_abortInterface = true;
					_mainThread->chooseSTAMPButton(22);
					_voy->_aptLoadMode = 143;
					break;
				case 2:
					_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
					reviewTape();
					_voy->_abortInterface = true;
					_voy->_aptLoadMode = 142;
					break;
				case 3:
					_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
					_mainThread->chooseSTAMPButton(21);
					break;
				case 4:
					breakFlag = true;
					break;
				case 5:
					doGossip();
					_voy->_abortInterface = true;
					_voy->_aptLoadMode = 141;
					_voy->_eventFlags &= ~EVTFLAG_100;
					break;
				default:
					break;
				}
			} else {
				_mainThread->chooseSTAMPButton(buttonId);
			}

			flag = true;
			break;

		case 6:
			_mainThread->doRoom();
			flag = true;
			break;

		case 16:
			_voy->_transitionId = 17;
			buttonId = _mainThread->doApt();

			switch (buttonId) {
			case 1:
				_mainThread->chooseSTAMPButton(22);
				flag = true;
				break;
			case 2:
				reviewTape();
				_voy->_abortInterface = true;
				break;
			case 4:
				flag = true;
				breakFlag = true;
				break;
			default:
				break;
			}
			break;

		case 17:
			// Called the police, showing the tape
			doTapePlaying();
			if (!checkForMurder() && _voy->_transitionId <= 15)
				checkForIncriminate();

			if (_voy->_videoEventId != -1) {
				// Show the found video that is of interest to the police
				playAVideoEvent(_voy->_videoEventId);
				_voy->_eventFlags &= ~EVTFLAG_RECORDING;
			}

			// Handle response
			_mainThread->chooseSTAMPButton(0);
			flag = true;
			break;

		case 130: {
			// user selected to send the tape
			if (_bVoy->getBoltGroup(_playStampGroupId)) {
				_screen->_backgroundPage = _bVoy->boltEntry(_playStampGroupId)._picResource;
				_screen->_backColors = _bVoy->boltEntry(_playStampGroupId + 1)._cMapResource;

				buttonId = getChooseButton();
				if (_eventsManager->_rightClick)
					// Aborted out of selecting a recipient
					buttonId = 4;

				_bVoy->freeBoltGroup(_playStampGroupId);
				_screen->screenReset();
				_playStampGroupId = -1;
				flag = true;

				if (buttonId != 4) {
					_voy->_playStampMode = 131;
					_voy->checkForKey();
					_mainThread->chooseSTAMPButton(buttonId);
				} else {
					_mainThread->chooseSTAMPButton(buttonId);
					_voy->_abortInterface = true;
				}
			}
			break;
		}

		default:
			break;
		}

		do {
			if (flag) {
				if (_currentVocId != -1) {
					_soundManager->stopVOCPlay();
					_currentVocId = -1;
				}

				_audioVideoId = -1;

				if (_voy->_boltGroupId2 != -1) {
					_bVoy->freeBoltGroup(_voy->_boltGroupId2);
					_voy->_boltGroupId2 = -1;
				}

				if (_playStampGroupId != -1) {
					_bVoy->freeBoltGroup(_playStampGroupId);
					_playStampGroupId = -1;
				}

				// Break out of loop
				flag = false;

			} else if (_mainThread->_stateFlags & 2) {
				_eventsManager->getMouseInfo();
				_mainThread->chooseSTAMPButton(0);
				flag = true;
			} else {
				_mainThread->chooseSTAMPButton(0);
				flag = true;
			}
		} while (flag);
	}

	_voy->_viewBounds = nullptr;
	closeStamp();
	_stampLibPtr->freeBoltGroup(0);
	delete _stampLibPtr;
}

void VoyeurEngine::initStamp() {
	_stampFlags &= ~1;
	_stackGroupPtr = _controlGroupPtr;

	if (!_controlPtr->_entries[0])
		error("No control entries");

	ThreadResource::initUseCount();
}

void VoyeurEngine::closeStamp() {
	ThreadResource::unloadAllStacks(this);
}

void VoyeurEngine::doTailTitle() {
	_screen->_vPort->setupViewPort(NULL);
	_screen->screenReset();

	if (_bVoy->getBoltGroup(0x600)) {
		RL2Decoder decoder;
		decoder.loadRL2File("a1100200.rl2", false);
		decoder.start();
		decoder.play(this);

		if (!shouldQuit() && !_eventsManager->_mouseClicked) {
			doClosingCredits();

			if (!shouldQuit() && !_eventsManager->_mouseClicked) {
				_screen->screenReset();

				PictureResource *pic = _bVoy->boltEntry(0x602)._picResource;
				CMapResource *pal = _bVoy->boltEntry(0x603)._cMapResource;

				_screen->_vPort->setupViewPort(pic);
				pal->startFade();
				flipPageAndWaitForFade();
				_eventsManager->delayClick(300);

				pic = _bVoy->boltEntry(0x604)._picResource;
				pal = _bVoy->boltEntry(0x605)._cMapResource;

				_screen->_vPort->setupViewPort(pic);
				pal->startFade();
				flipPageAndWaitForFade();
				_eventsManager->delayClick(120);

				_soundManager->stopVOCPlay();
			}
		}

		_bVoy->freeBoltGroup(0x600);
	}

	if (!shouldQuit()) {
		_bVoy->getBoltGroup(0x100);
		doPiracy();
	}
}

void VoyeurEngine::doClosingCredits() {
	if (!_bVoy->getBoltGroup(0x400))
		return;

	const char *msg = (const char *)_bVoy->memberAddr(0x404);
	const byte *creditList = (const byte *)_bVoy->memberAddr(0x405);

	_screen->_vPort->setupViewPort(NULL);
	_screen->setColor(1, 180, 180, 180);
	_screen->setColor(2, 200, 200, 200);
	_eventsManager->_intPtr._hasPalette = true;

	_screen->_fontPtr->_curFont = _bVoy->boltEntry(0x402)._fontResource;
	_screen->_fontPtr->_foreColor = 2;
	_screen->_fontPtr->_backColor = 2;
	_screen->_fontPtr->_fontSaveBack = false;
	_screen->_fontPtr->_fontFlags = DISPFLAG_NONE;

	_soundManager->startVOCPlay(152);
	FontInfoResource &fi = *_screen->_fontPtr;

	for (int idx = 0; idx < 78; ++idx) {
		const byte *entry = creditList + idx * 6;
		int flags = READ_LE_UINT16(entry + 4);

		if (flags & 0x10)
			_screen->_vPort->fillPic(0);

		if (flags & 1) {
			fi._foreColor = 1;
			fi._curFont = _bVoy->boltEntry(0x402)._fontResource;
			fi._justify = ALIGN_CENTER;
			fi._justifyWidth = 384;
			fi._justifyHeight = 240;
			fi._pos = Common::Point(0, READ_LE_UINT16(entry));

			_screen->_vPort->drawText(msg);
			msg += strlen(msg) + 1;
		}

		if (flags & 0x40) {
			fi._foreColor = 2;
			fi._curFont = _bVoy->boltEntry(0x400)._fontResource;
			fi._justify = ALIGN_CENTER;
			fi._justifyWidth = 384;
			fi._justifyHeight = 240;
			fi._pos = Common::Point(0, READ_LE_UINT16(entry));

			_screen->_vPort->drawText(msg);
			msg += strlen(msg) + 1;
		}

		if (flags & 2) {
			fi._foreColor = 1;
			fi._curFont = _bVoy->boltEntry(0x400)._fontResource;
			fi._justify = ALIGN_LEFT;
			fi._justifyWidth = 384;
			fi._justifyHeight = 240;
			fi._pos = Common::Point(38, READ_LE_UINT16(entry));

			_screen->_vPort->drawText(msg);
			msg += strlen(msg) + 1;

			fi._foreColor = 2;
			fi._justify = ALIGN_LEFT;
			fi._justifyWidth = 384;
			fi._justifyHeight = 240;
			fi._pos = Common::Point(198, READ_LE_UINT16(entry));

			_screen->_vPort->drawText(msg);
			msg += strlen(msg) + 1;
		}

		if (flags & 4) {
			fi._foreColor = 1;
			fi._curFont = _bVoy->boltEntry(0x402)._fontResource;
			fi._justify = ALIGN_CENTER;
			fi._justifyWidth = 384;
			fi._justifyHeight = 240;
			fi._pos = Common::Point(0, READ_LE_UINT16(entry));

			_screen->_vPort->drawText(msg);
			msg += strlen(msg) + 1;

			fi._foreColor = 2;
			fi._curFont = _bVoy->boltEntry(0x400)._fontResource;
			fi._justify = ALIGN_CENTER;
			fi._justifyWidth = 384;
			fi._justifyHeight = 240;
			fi._pos = Common::Point(0, READ_LE_UINT16(entry) + 13);

			_screen->_vPort->drawText(msg);
			msg += strlen(msg) + 1;
		}

		if (flags & 0x20) {
			flipPageAndWait();
			_eventsManager->delayClick(READ_LE_UINT16(entry + 2) * 60);
		}

		if (shouldQuit() || _eventsManager->_mouseClicked)
			break;
	}

	_soundManager->stopVOCPlay();
	_screen->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource;
	_bVoy->freeBoltGroup(0x400);
}

void VoyeurEngine::doPiracy() {
	_screen->screenReset();
	_screen->setColor(1, 0, 0, 0);
	_screen->setColor(2, 255, 255, 255);
	_eventsManager->_intPtr._hasPalette = true;
	_screen->_vPort->setupViewPort(NULL);
	_screen->_vPort->fillPic(1);

	FontInfoResource &fi = *_screen->_fontPtr;
	fi._curFont = _bVoy->boltEntry(0x101)._fontResource;
	fi._foreColor = 2;
	fi._backColor = 2;
	fi._fontSaveBack = false;
	fi._fontFlags = DISPFLAG_NONE;
	fi._justify = ALIGN_CENTER;
	fi._justifyWidth = 384;
	fi._justifyHeight = 230;

	// Loop through the piracy message array to draw each line
	for (int idx = 0, yp = 33; idx < 10; ++idx) {
		fi._pos = Common::Point(0, yp);
		_screen->_vPort->drawText(PIRACY_MESSAGE[idx]);

		yp += fi._curFont->_fontHeight + 4;
	}

	flipPageAndWait();
	_eventsManager->getMouseInfo();
	_eventsManager->delayClick(720);
}

void VoyeurEngine::reviewTape() {
	int eventStart = 0;
	int newX = -1;
	int newY = -1;
	int eventLine = 7;
	Common::Rect tempRect(58, 30, 58 + 223, 30 + 124);
	Common::Point pt;
	int foundIndex;

	_bVoy->getBoltGroup(0x900);
	PictureResource *cursor = _bVoy->boltEntry(0x903)._picResource;

	if ((_voy->_eventCount - 8) != 0)
		eventStart = MAX(_voy->_eventCount - 8, 0);

	if ((eventStart + _voy->_eventCount) <= 7)
		eventLine = eventStart + _voy->_eventCount - 1;

	bool breakFlag = false;
	while (!shouldQuit() && !breakFlag) {
		_voy->_viewBounds = _bVoy->boltEntry(0x907)._rectResource;
		Common::Array<RectEntry> &hotspots = _bVoy->boltEntry(0x906)._rectResource->_entries;

		_screen->_backColors = _bVoy->boltEntry(0x902)._cMapResource;
		_screen->_backgroundPage = _bVoy->boltEntry(0x901)._picResource;
		_screen->_vPort->setupViewPort(_screen->_backgroundPage);
		_screen->_backColors->startFade();

		flipPageAndWaitForFade();

		_screen->setColor(1, 32, 32, 32);
		_screen->setColor(2, 96, 96, 96);
		_screen->setColor(3, 160, 160, 160);
		_screen->setColor(4, 224, 224, 224);
		_screen->setColor(9, 24, 64, 24);
		_screen->setColor(10, 64, 132, 64);
		_screen->setColor(11, 100, 192, 100);
		_screen->setColor(12, 120, 248, 120);
		_eventsManager->setCursorColor(128, 1);

		_eventsManager->_intPtr._hasPalette = true;
		_screen->_fontPtr->_curFont = _bVoy->boltEntry(0x909)._fontResource;
		_screen->_fontPtr->_fontSaveBack = false;
		_screen->_fontPtr->_fontFlags = DISPFLAG_NONE;

		_eventsManager->getMouseInfo();
		if (newX == -1) {
			_eventsManager->setMousePos(Common::Point(hotspots[1].left + 12, hotspots[1].top + 6));
		} else {
			_eventsManager->setMousePos(Common::Point(newX, newY));
		}

		_currentVocId = 151;
		_voy->_vocSecondsOffset = 0;
		bool needRedraw = true;
		do {
			if (_currentVocId != -1 && !_soundManager->getVOCStatus()) {
				_voy->_musicStartTime = _voy->_RTVNum;
				_soundManager->startVOCPlay(_currentVocId);
			}

			if (needRedraw) {
				needRedraw = false;
				flipPageAndWait();

				_screen->_drawPtr->_penColor = 0;
				_screen->_drawPtr->_pos = Common::Point(tempRect.left, tempRect.top);
				_screen->_backgroundPage->sFillBox(tempRect.width(), tempRect.height());

				int yp = 45;
				int eventNum = eventStart;
				for (int lineNum = 0; lineNum < 8 && eventNum < _voy->_eventCount; ++lineNum, ++eventNum) {
					_screen->_fontPtr->_picFlags = DISPFLAG_NONE;
					_screen->_fontPtr->_picSelect = 0xff;
					_screen->_fontPtr->_picPick = 7;
					_screen->_fontPtr->_picOnOff = (lineNum == eventLine) ? 8 : 0;
					_screen->_fontPtr->_pos = Common::Point(68, yp);
					_screen->_fontPtr->_justify = ALIGN_LEFT;
					_screen->_fontPtr->_justifyWidth = 0;
					_screen->_fontPtr->_justifyHeight = 0;

					Common::String msg = _eventsManager->getEvidString(eventNum);
					_screen->_backgroundPage->drawText(msg);

					yp += 15;
				}

				_screen->_vPort->addSaveRect(
					_screen->_vPort->_lastPage, tempRect);
				flipPageAndWait();

				_screen->_vPort->addSaveRect(
					_screen->_vPort->_lastPage, tempRect);
			}

			_screen->sDrawPic(cursor, _screen->_vPort,
				_eventsManager->getMousePos());
			flipPageAndWait();

			_eventsManager->getMouseInfo();
			foundIndex = -1;

			Common::Point tempPos = _eventsManager->getMousePos() + Common::Point(14, 7);
			for (uint idx = 0; idx < hotspots.size(); ++idx) {
				if (hotspots[idx].contains(tempPos)) {
					// Found hotspot area
					foundIndex = idx;
					break;
				}
			}

			pt = _eventsManager->getMousePos();
			if (tempPos.x >= 68 && tempPos.x <= 277 && tempPos.y >= 31 && tempPos.y <= 154) {
				tempPos.y -= 2;
				foundIndex = (tempPos.y - 31) / 15;
				if ((tempPos.y - 31) % 15 >= 12 || (eventStart + foundIndex) >= _voy->_eventCount) {
					_eventsManager->setCursorColor(128, 0);
					foundIndex = 999;
				} else if (!_eventsManager->_leftClick) {
					_eventsManager->setCursorColor(128, 2);
					foundIndex = -1;
				} else {
					_eventsManager->setCursorColor(128, 2);
					eventLine =  foundIndex;

					flipPageAndWait();

					_screen->_drawPtr->_penColor = 0;
					_screen->_drawPtr->_pos = Common::Point(tempRect.left, tempRect.top);
					_screen->_backgroundPage->sFillBox(tempRect.width(), tempRect.height());

					int yp = 45;
					int eventNum = eventStart;
					for (int idx = 0; idx < 8 && eventNum < _voy->_eventCount; ++idx, ++eventNum) {
						_screen->_fontPtr->_picFlags = DISPFLAG_NONE;
						_screen->_fontPtr->_picSelect = 0xff;
						_screen->_fontPtr->_picPick = 7;
						_screen->_fontPtr->_picOnOff = (idx == eventLine) ? 8 : 0;
						_screen->_fontPtr->_pos = Common::Point(68, yp);
						_screen->_fontPtr->_justify = ALIGN_LEFT;
						_screen->_fontPtr->_justifyWidth = 0;
						_screen->_fontPtr->_justifyHeight = 0;

						Common::String msg = _eventsManager->getEvidString(eventNum);
						_screen->_backgroundPage->drawText(msg);

						yp += 15;
					}

					_screen->_vPort->addSaveRect(
						_screen->_vPort->_lastPage, tempRect);
					flipPageAndWait();

					_screen->_vPort->addSaveRect(
						_screen->_vPort->_lastPage, tempRect);
					flipPageAndWait();

					_eventsManager->getMouseInfo();
					foundIndex = -1;
				}
			} else if ((_voy->_eventFlags & EVTFLAG_40) && _voy->_viewBounds->left == pt.x &&
					_voy->_viewBounds->bottom == pt.y) {
				foundIndex = 999;
			} else if ((_voy->_eventFlags & EVTFLAG_40) && _voy->_viewBounds->left == pt.x &&
					_voy->_viewBounds->top == pt.y) {
				foundIndex = 998;
			} else {
				_eventsManager->setCursorColor(128, (foundIndex == -1) ? 0 : 1);
			}

			_eventsManager->_intPtr._hasPalette = true;

			if (_eventsManager->_mouseClicked) {
				switch (foundIndex) {
				case 2:
					if (eventStart > 0) {
						--eventStart;
						needRedraw = true;
					}
					foundIndex = -1;
					break;

				case 3:
					if (eventStart > 0) {
						eventStart -= 8;
						if (eventStart < 0)
							eventStart = 0;
						needRedraw = true;
					}
					foundIndex = -1;
					break;

				case 4:
					if ((_voy->_eventCount - 8) > eventStart) {
						++eventStart;
						needRedraw = true;
					}
					foundIndex = -1;
					break;

				case 5:
					if (_voy->_eventCount >= 8 && (_voy->_eventCount - 8) != eventStart) {
						eventStart += 8;
						if ((_voy->_eventCount - 8) < eventStart)
							eventStart = _voy->_eventCount - 8;
						needRedraw = true;
					}
					foundIndex = -1;
					break;

				default:
					break;
				}

				while (eventLine > 0 && (eventLine + eventStart) >= _voy->_eventCount)
					--eventLine;
			}

			pt = _eventsManager->getMousePos();
			if (_eventsManager->_mouseClicked && _voy->_viewBounds->left == pt.x &&
					(_voy->_eventFlags & EVTFLAG_40) && _eventsManager->_rightClick) {
				_controlPtr->_state->_victimIndex = (pt.y / 60) + 1;
				foundIndex = -1;
				_eventsManager->_rightClick = 0;
			}

			if (_eventsManager->_rightClick)
				foundIndex = 0;

		} while (!shouldQuit() && (!_eventsManager->_mouseClicked || foundIndex == -1));

		if (shouldQuit())
			return;

		newY = _eventsManager->getMousePos().y;
		_voy->_fadingType = 0;
		_voy->_viewBounds = nullptr;
		_screen->_vPort->setupViewPort(NULL);

		if (_currentVocId != -1) {
			_voy->_vocSecondsOffset = _voy->_RTVNum - _voy->_musicStartTime;
			_soundManager->stopVOCPlay();
		}

		// Break out if the exit button was pressed
		if (!foundIndex)
			break;

		int eventIndex = eventStart + eventLine;
		VoyeurEvent &e = _voy->_events[eventIndex];
		switch (e._type) {
		case EVTYPE_VIDEO:
			playAVideoEvent(eventIndex);
			break;

		case EVTYPE_AUDIO: {
			_audioVideoId = e._audioVideoId;
			_voy->_vocSecondsOffset = e._computerOn;

			_bVoy->getBoltGroup(0x7F00);
			_screen->_backgroundPage = _bVoy->boltEntry(0x7F00 +
				BLIND_TABLE[_audioVideoId] * 2)._picResource;
			_screen->_backColors = _bVoy->boltEntry(0x7F01 +
				BLIND_TABLE[_audioVideoId] * 2)._cMapResource;

			_screen->_vPort->setupViewPort(_screen->_backgroundPage);
			_screen->_backColors->startFade();
			flipPageAndWaitForFade();

			_eventsManager->_intPtr._flashStep = 1;
			_eventsManager->_intPtr._flashTimer = 0;
			_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;

			// Play sound for the given duration
			_soundManager->setVOCOffset(_voy->_vocSecondsOffset);
			Common::String filename = _soundManager->getVOCFileName(
				_audioVideoId + 159);
			_soundManager->startVOCPlay(filename);

			uint32 secondsDuration = e._computerOff;

			_eventsManager->getMouseInfo();
			while (!_eventsManager->_mouseClicked && _soundManager->getVOCStatus() &&
					_soundManager->getVOCFrame() < secondsDuration) {
				_eventsManager->getMouseInfo();
				_eventsManager->delay(10);
			}

			_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
			_soundManager->stopVOCPlay();
			_bVoy->freeBoltGroup(0x7F00);
			break;
		}

		case EVTYPE_EVID:
			_voy->reviewAnEvidEvent(eventIndex);

			_voy->_vocSecondsOffset = _voy->_RTVNum - _voy->_musicStartTime;
			_soundManager->stopVOCPlay();
			_bVoy->getBoltGroup(0x900);
			break;

		case EVTYPE_COMPUTER:
			_voy->reviewComputerEvent(eventIndex);

			_voy->_vocSecondsOffset = _voy->_RTVNum - _voy->_musicStartTime;
			_soundManager->stopVOCPlay();
			_bVoy->getBoltGroup(0x900);
			break;

		default:
			break;
		}
	}

	_screen->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource;

	_screen->_vPort->fillPic(0);
	flipPageAndWait();
	_bVoy->freeBoltGroup(0x900);
}

void VoyeurEngine::doGossip() {
	_screen->resetPalette();
	_screen->screenReset();

	if (!_bVoy->getBoltGroup(0x300))
		return;

	// Load the gossip animation
	RL2Decoder decoder;
	decoder.loadRL2File("a2050100.rl2", false);
	decoder.start();

	// Get the resource data for the first gossip video
	PictureResource *bgPic = _bVoy->boltEntry(0x300)._picResource;
	CMapResource *pal = _bVoy->boltEntry(0x301)._cMapResource;
	pal->startFade();

	// Transfer initial background to video decoder
	PictureResource videoFrame(decoder.getRL2VideoTrack()->getBackSurface());
	bgPic->_bounds.moveTo(0, 0);
	_screen->sDrawPic(bgPic, &videoFrame, Common::Point(0, 0));

	byte *frameNumsP = _bVoy->memberAddr(0x309);
	byte *posP = _bVoy->boltEntry(0x30A)._data;

	// Play the initial gossip video
	decoder.play(this, 0x302, frameNumsP, posP);
	decoder.close();

	// Reset the palette and clear the screen
	_screen->resetPalette();
	_screen->screenReset();

	// Play interview video
	RL2Decoder decoder2;
	decoder2.loadRL2File("a2110100.rl2", true);
	decoder2.start();

	_eventsManager->getMouseInfo();
	decoder2.play(this);
	decoder2.close();

	_bVoy->freeBoltGroup(0x300);
	_screen->screenReset();
}

void VoyeurEngine::doTapePlaying() {
	if (!_bVoy->getBoltGroup(0xA00))
		return;

	_eventsManager->getMouseInfo();
	_screen->_backColors = _bVoy->boltEntry(0xA01)._cMapResource;
	_screen->_backgroundPage = _bVoy->boltEntry(0xA00)._picResource;
	PictureResource *pic = _bVoy->boltEntry(0xA02)._picResource;
	VInitCycleResource *cycle = _bVoy->boltEntry(0xA05)._vInitCycleResource;

	_screen->_vPort->setupViewPort(_screen->_backgroundPage);
	_screen->sDrawPic(pic, _screen->_vPort, Common::Point(57, 30));
	_screen->_backColors->startFade();
	flipPageAndWaitForFade();

	cycle->vStartCycle();

	_soundManager->startVOCPlay("vcr.voc");
	while (!shouldQuit() && !_eventsManager->_mouseClicked && _soundManager->getVOCStatus()) {
		_eventsManager->delayClick(2);
	}

	_soundManager->stopVOCPlay();
	cycle->vStopCycle();
	_bVoy->freeBoltGroup(0xA00);
}

bool VoyeurEngine::checkForMurder() {
	int oldMurderIndex = _controlPtr->_state->_victimMurderIndex;

	for (int idx = 0; idx < _voy->_eventCount; ++idx) {
		VoyeurEvent &evt = _voy->_events[idx];

		if (evt._type == EVTYPE_VIDEO) {
			switch (_controlPtr->_state->_victimIndex) {
			case 1:
				if (evt._audioVideoId == 41 && evt._computerOn <= 15 &&
						(evt._computerOff + evt._computerOn) >= 16) {
					_controlPtr->_state->_victimMurderIndex = 1;
				}
				break;

			case 2:
				if (evt._audioVideoId == 53 && evt._computerOn <= 19 &&
						(evt._computerOff + evt._computerOn) >= 21) {
					_controlPtr->_state->_victimMurderIndex = 2;
				}
				break;

			case 3:
				if (evt._audioVideoId == 50 && evt._computerOn <= 28 &&
						(evt._computerOff + evt._computerOn) >= 29) {
					_controlPtr->_state->_victimMurderIndex = 3;
				}
				break;

			case 4:
				if (evt._audioVideoId == 43 && evt._computerOn <= 10 &&
						(evt._computerOff + evt._computerOn) >= 14) {
					_controlPtr->_state->_victimMurderIndex = 4;
				}
				break;

			default:
				break;
			}
		}

		if (_controlPtr->_state->_victimMurderIndex == _controlPtr->_state->_victimIndex) {
			_voy->_videoEventId = idx;
			return true;
		}
	}

	_controlPtr->_state->_victimMurderIndex = oldMurderIndex;
	_voy->_videoEventId = -1;
	return false;
}

bool VoyeurEngine::checkForIncriminate() {
	_voy->_incriminatedVictimNumber = 0;

	for (int idx = 0; idx < _voy->_eventCount; ++idx) {
		VoyeurEvent &evt = _voy->_events[idx];

		if (evt._type == EVTYPE_VIDEO) {
			if (evt._audioVideoId == 44 && evt._computerOn <= 40 &&
					(evt._computerOff + evt._computerOn) >= 70) {
				_voy->_incriminatedVictimNumber = 1;
			}

			if (evt._audioVideoId == 44 && evt._computerOn <= 79 &&
					(evt._computerOff + evt._computerOn) >= 129) {
				_voy->_incriminatedVictimNumber = 1;
			}

			if (evt._audioVideoId == 20 && evt._computerOn <= 28 &&
					(evt._computerOff + evt._computerOn) >= 45) {
				_voy->_incriminatedVictimNumber = 2;
			}

			if (evt._audioVideoId == 35 && evt._computerOn <= 17 &&
					(evt._computerOff + evt._computerOn) >= 36) {
				_voy->_incriminatedVictimNumber = 3;
			}

			if (evt._audioVideoId == 30 && evt._computerOn <= 80 &&
					(evt._computerOff + evt._computerOn) >= 139) {
				_voy->_incriminatedVictimNumber = 4;
			}
		}

		if (_voy->_incriminatedVictimNumber) {
			_controlPtr->_state->_victimMurderIndex = 88;
			_voy->_videoEventId = idx;
			return true;
		}
	}

	_voy->_videoEventId = -1;
	return false;
}

void VoyeurEngine::playAVideoEvent(int eventIndex) {
	VoyeurEvent &evt = _voy->_events[eventIndex];
	_audioVideoId = evt._audioVideoId;
	_voy->_vocSecondsOffset = evt._computerOn;
	_eventsManager->_videoDead = evt._dead;
	_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;

	playAVideoDuration(_audioVideoId, evt._computerOff);

	_voy->_eventFlags |= EVTFLAG_TIME_DISABLED;
	if (_eventsManager->_videoDead != -1) {
		_bVoy->freeBoltGroup(0xE00);
		_eventsManager->_videoDead = -1;
		flipPageAndWait();
		_eventsManager->_videoDead = -1;
	}

	_audioVideoId = -1;
	if (_eventsManager->_videoDead != -1) {
		_bVoy->freeBoltGroup(0xE00);
		_eventsManager->_videoDead = -1;
		flipPageAndWait();
	}
}

int VoyeurEngine::getChooseButton()  {
	int prevIndex = -2;
	Common::Array<RectEntry> &hotspots = _bVoy->boltEntry(_playStampGroupId
		+ 6)._rectResource->_entries;
	int selectedIndex = -1;

	_screen->_vPort->setupViewPort(_screen->_backgroundPage);
	_screen->_backColors->_steps = 0;
	_screen->_backColors->startFade();
	flipPageAndWait();

	_voy->_viewBounds = _bVoy->boltEntry(_playStampGroupId + 7)._rectResource;
	PictureResource *cursorPic = _bVoy->boltEntry(_playStampGroupId + 2)._picResource;

	do {
		do {
			if (_currentVocId != -1 && !_soundManager->getVOCStatus())
				_soundManager->startVOCPlay(_currentVocId);

			_eventsManager->getMouseInfo();
			selectedIndex = -1;
			Common::Point pt = _eventsManager->getMousePos();

			for (uint idx = 0; idx < hotspots.size(); ++idx) {
				if (hotspots[idx].contains(pt)) {
					if (!_voy->_victimMurdered || ((int)idx + 1) != _controlPtr->_state->_victimIndex) {
						selectedIndex = idx;
						if (selectedIndex != prevIndex) {
							PictureResource *btnPic = _bVoy->boltEntry(_playStampGroupId + 8 + idx)._picResource;
							_screen->sDrawPic(btnPic, _screen->_vPort,
								Common::Point(106, 200));

							cursorPic = _bVoy->boltEntry(_playStampGroupId + 4)._picResource;
						}
					}
				}
			}

			if (selectedIndex == -1) {
				cursorPic = _bVoy->boltEntry(_playStampGroupId + 2)._picResource;
				PictureResource *btnPic = _bVoy->boltEntry(_playStampGroupId + 12)._picResource;
				_screen->sDrawPic(btnPic, _screen->_vPort,
					Common::Point(106, 200));
			}

			_screen->sDrawPic(cursorPic, _screen->_vPort,
				Common::Point(pt.x + 13, pt.y - 12));

			flipPageAndWait();
		} while (!shouldQuit() && !_eventsManager->_mouseClicked);
	} while (!shouldQuit() && selectedIndex == -1 && !_eventsManager->_rightClick);

	return selectedIndex;
}

void VoyeurEngine::makeViewFinder() {
	_screen->_backgroundPage = _bVoy->boltEntry(0x103)._picResource;
	_screen->sDrawPic(_screen->_backgroundPage,
		_screen->_vPort, Common::Point(0, 0));
	CMapResource *pal = _bVoy->boltEntry(0x104)._cMapResource;

	int palOffset = 0;
	switch (_voy->_transitionId) {
	case 1:
	case 2:
	case 5:
	case 6:
	case 7:
	case 8:
	case 9:
	case 17:
		palOffset = 0;
		break;
	case 3:
		palOffset = 1;
		break;
	case 4:
	case 10:
	case 11:
	case 12:
	case 13:
	case 14:
	case 15:
	case 16:
		palOffset = 2;
		break;
	default:
		break;
	}

	_screen->_vPort->drawIfaceTime();
	doTimeBar();
	pal->startFade();

	flipPageAndWaitForFade();

	_screen->setColor(241, 105, 105, 105);
	_screen->setColor(242, 105, 105, 105);
	_screen->setColor(243, 105, 105, 105);
	_screen->setColor(palOffset + 241, 219, 235, 235);

	_eventsManager->_intPtr._hasPalette = true;
}

void VoyeurEngine::makeViewFinderP() {
	_screen->screenReset();
}

void VoyeurEngine::initIFace() {
	int playStamp1 = _playStampGroupId;
	switch (_voy->_transitionId) {
	case 0:
		break;
	case 1:
	case 2:
	case 5:
	case 6:
	case 7:
	case 8:
	case 9:
		_playStampGroupId = 0xB00;
		break;
	case 3:
		_playStampGroupId = 0xC00;
		break;
	default:
		_playStampGroupId = 0xD00;
		break;
	}
	if (playStamp1 != -1)
		_bVoy->freeBoltGroup(playStamp1);

	_bVoy->getBoltGroup(_playStampGroupId);
	CMapResource *pal = _bVoy->boltEntry(_playStampGroupId + 2)._cMapResource;
	pal->startFade();

	// Reset the mansion view off to it's prior position (if any)
	doScroll(_mansionViewPos);

	_voy->_viewBounds = _bVoy->boltEntry(_playStampGroupId)._rectResource;

	// Show the cursor using ScummVM functionality
	_eventsManager->showCursor();

	// Note: the original did two loops to preload members here, which is
	// redundant for ScummVM, since computers are faster these days, and
	// getting resources as needed will be fast enough.
}

void VoyeurEngine::doScroll(const Common::Point &pt) {
	Common::Rect clipRect(72, 47, 72 + 240, 47 + 148);
	_screen->_vPort->setupViewPort(NULL, &clipRect);

	int base = 0;
	switch (_voy->_transitionId) {
	case 0:
		break;
	case 1:
	case 2:
	case 5:
	case 6:
	case 7:
	case 8:
	case 9:
		base = 0xB00;
		break;
	case 3:
		base = 0xC00;
		break;
	default:
		base = 0xD00;
	}

	if (base) {
		PictureResource *pic = _bVoy->boltEntry(base + 3)._picResource;
 		_screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y - 104));
		pic = _bVoy->boltEntry(base + 4)._picResource;
		_screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y - 44));
		pic = _bVoy->boltEntry(base + 5)._picResource;
		_screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 16));
		pic = _bVoy->boltEntry(base + 6)._picResource;
		_screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 76));
		pic = _bVoy->boltEntry(base + 7)._picResource;
		_screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 136));
	}

	_screen->_vPort->setupViewPort(NULL);
}

void VoyeurEngine::checkTransition() {
	Common::String time, day;

	if (_voy->_transitionId != _checkTransitionId) {
		// Get the day
		day = getDayName();

		// Only proceed if a valid day string was returned
		if (!day.empty()) {
			_screen->fadeDownICF(6);

			// Get the time of day string
			time = getTimeOfDay();

			// Show a transition card with the day and time, and wait
			doTransitionCard(day, time);
			_eventsManager->delayClick(180);
		}

		_checkTransitionId = _voy->_transitionId;
		centerMansionView();
	}
}

Common::String VoyeurEngine::getDayName() {
	switch (_voy->_transitionId) {
	case 0:
		return "";
	case 1:
	case 2:
	case 3:
	case 4:
		return SATURDAY;
	case 17:
		return MONDAY;
	default:
		return SUNDAY;
	}
}

Common::String VoyeurEngine::getTimeOfDay() {
	if (_voy->_transitionId == 17)
		return "";

	return Common::String::format("%d:%02d%s", _gameHour, _gameMinute, _voy->_isAM ? AM : PM);
}

int VoyeurEngine::doComputerText(int maxLen) {
	FontInfoResource &font = *_screen->_fontPtr;
	int totalChars = 0;

	font._curFont = _bVoy->boltEntry(0x4910)._fontResource;
	font._foreColor = 129;
	font._fontSaveBack = false;
	font._fontFlags = DISPFLAG_NONE;
	if (_voy->_vocSecondsOffset > 60)
		_voy->_vocSecondsOffset = 0;

	if (_voy->_RTVNum > _voy->_computerTimeMax && maxLen == 9999) {
		if (_currentVocId != -1)
			_soundManager->startVOCPlay(_currentVocId);
		font._justify = ALIGN_LEFT;
		font._justifyWidth = 384;
		font._justifyHeight = 100;
		font._pos = Common::Point(128, 100);
		_screen->_vPort->drawText(END_OF_MESSAGE);
	} else if (_voy->_RTVNum < _voy->_computerTimeMin && maxLen == 9999) {
		if (_currentVocId != -1)
			_soundManager->startVOCPlay(_currentVocId);
		font._justify = ALIGN_LEFT;
		font._justifyWidth = 384;
		font._justifyHeight = 100;
		font._pos = Common::Point(120, 100);
		_screen->_vPort->drawText(START_OF_MESSAGE);
	} else {
		char *msg = (char *)_bVoy->memberAddr(0x4900 + _voy->_computerTextId);
		font._pos = Common::Point(96, 60);

		bool showEnd = true;
		int yp = 60;
		do {
			if (_currentVocId != -1 && !_soundManager->getVOCStatus()) {
				if (_voy->_vocSecondsOffset > 60)
					_voy->_vocSecondsOffset = 0;
				_soundManager->startVOCPlay(_currentVocId);
			}

			char c = *msg++;
			if (c == '\0') {
				if (showEnd) {
					_eventsManager->delay(90);
					_screen->_drawPtr->_pos = Common::Point(96, 54);
					_screen->_drawPtr->_penColor = 254;
					_screen->_vPort->sFillBox(196, 124);
					_screen->_fontPtr->_justify = ALIGN_LEFT;
					_screen->_fontPtr->_justifyWidth = 384;
					_screen->_fontPtr->_justifyHeight = 100;
					_screen->_fontPtr->_pos = Common::Point(128, 100);
					_screen->_vPort->drawText(END_OF_MESSAGE);
				}
				break;
			}

			if (c == '~' || c == '^') {
				if (c == '^') {
					yp += 10;
				} else {
					_eventsManager->delay(90);
					_screen->_drawPtr->_pos = Common::Point(96, 54);
					_screen->_drawPtr->_penColor = 255;
					_screen->_vPort->sFillBox(196, 124);
					yp = 60;
				}

				_screen->_fontPtr->_pos = Common::Point(96, yp);
			} else if (c == '_') {
				showEnd = false;
			} else {
				_screen->_fontPtr->_justify = ALIGN_LEFT;
				_screen->_fontPtr->_justifyWidth = 0;
				_screen->_fontPtr->_justifyHeight = 0;
				_screen->_vPort->drawText(Common::String(c));
				_eventsManager->delay(4);
			}

			flipPageAndWait();
			_eventsManager->getMouseInfo();
			++totalChars;

		} while (!shouldQuit() && !_eventsManager->_mouseClicked && totalChars < maxLen);

		_voy->_computerTimeMax = 0;
	}

	flipPageAndWait();

	_screen->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource;
	return totalChars;
}

void VoyeurEngine::getComputerBrush() {
	if (!_bVoy->getBoltGroup(0x4900))
		return;

	PictureResource *pic = _bVoy->boltEntry(0x490E)._picResource;
	int xp = (384 - pic->_bounds.width()) / 2;
	int yp = (240 - pic->_bounds.height()) / 2 - 4;

	_screen->_vPort->drawPicPerm(pic, Common::Point(xp, yp));

	CMapResource *pal = _bVoy->boltEntry(0x490F)._cMapResource;
	pal->startFade();
}

void VoyeurEngine::doTimeBar() {
	flashTimeBar();

	if (_voy->_RTVLimit > 0) {
		if (_voy->_RTVNum > _voy->_RTVLimit || _voy->_RTVNum < 0)
			_voy->_RTVNum = _voy->_RTVLimit - 1;

		_timeBarVal = _voy->_RTVNum;
		int height = ((_voy->_RTVLimit - _voy->_RTVNum) * 59) / _voy->_RTVLimit;
		int fullHeight = MAX(151 - height, 93);

		_screen->_drawPtr->_penColor = 134;
		_screen->_drawPtr->_pos = Common::Point(39, 92);

		_screen->_vPort->sFillBox(6, fullHeight - 92);
		if (height > 0) {
			_screen->setColor(215, 238, 238, 238);
			_eventsManager->_intPtr._hasPalette = true;

			_screen->_drawPtr->_penColor = 215;
			_screen->_drawPtr->_pos = Common::Point(39, fullHeight);
			_screen->_vPort->sFillBox(6, height);
		}
	}
}

void VoyeurEngine::flashTimeBar() {
	if (_voy->_RTVNum >= 0 && (_voy->_RTVLimit - _voy->_RTVNum) < 11 &&
		(_eventsManager->_intPtr._flashTimer >= (_flashTimeVal + 15) ||
		_eventsManager->_intPtr._flashTimer < _flashTimeVal)) {
		// Within camera low power range
		_flashTimeVal = _eventsManager->_intPtr._flashTimer;

		if (_flashTimeFlag)
			_screen->setColor(240, 220, 20, 20);
		else
			_screen->setColor(240, 220, 220, 220);

		_eventsManager->_intPtr._hasPalette = true;
		_flashTimeFlag = !_flashTimeFlag;
	}
}

void VoyeurEngine::checkPhoneCall() {
	if ((_voy->_RTVLimit - _voy->_RTVNum) >= 36 && _voy->_totalPhoneCalls < 5 &&
			_currentVocId <= 151 && _currentVocId > 146) {
		if ((_voy->_switchBGNum < _checkPhoneVal || _checkPhoneVal > 180) &&
				!_soundManager->getVOCStatus()) {
			int soundIndex;
			do {
				soundIndex = getRandomNumber(4);
			} while (_voy->_phoneCallsReceived[soundIndex]);
			_currentVocId = 154 + soundIndex;

			_soundManager->stopVOCPlay();
			_soundManager->startVOCPlay(_currentVocId);
			_checkPhoneVal = _voy->_switchBGNum;
			_voy->_phoneCallsReceived[soundIndex] = true;
			++_voy->_totalPhoneCalls;
		}
	}
}

void VoyeurEngine::doEvidDisplay(int evidId, int eventId) {
	_eventsManager->getMouseInfo();
	flipPageAndWait();

	if (_currentVocId != -1) {
		_voy->_vocSecondsOffset = _voy->_RTVNum - _voy->_musicStartTime;
		_soundManager->stopVOCPlay();
	}

	_bVoy->getBoltGroup(_voy->_boltGroupId2);
	PictureResource *pic = _bVoy->boltEntry(_voy->_boltGroupId2 + evidId * 2)._picResource;
	_screen->sDrawPic(pic, _screen->_vPort, Common::Point(
		(384 - pic->_bounds.width()) / 2, (240 - pic->_bounds.height()) / 2));
	_bVoy->freeBoltMember(_voy->_boltGroupId2 + evidId * 2);

	CMapResource *pal = _bVoy->boltEntry(_voy->_boltGroupId2 + evidId * 2 + 1)._cMapResource;
	pal->startFade();

	while (!shouldQuit() && (_eventsManager->_fadeStatus & 1))
		_eventsManager->delay(1);
	_bVoy->freeBoltMember(_voy->_boltGroupId2 + evidId * 2 + 1);

	Common::Array<RectEntry> &hotspots = _bVoy->boltEntry(_playStampGroupId + 4)._rectResource->_entries;
	int count = hotspots[evidId]._count;

	if (count > 0) {
		for (int idx = 1; idx <= count; ++idx) {
			_voy->_evPicPtrs[idx - 1] = _bVoy->boltEntry(_voy->_boltGroupId2 +
				(evidId + idx) * 2)._picResource;
			_voy->_evCmPtrs[idx - 1] = _bVoy->boltEntry(_voy->_boltGroupId2 +
				(evidId + idx) * 2 + 1)._cMapResource;
		}
	}

	flipPageAndWait();
	_eventsManager->stopEvidDim();

	if (eventId == 999)
		_voy->addEvidEventStart(evidId);

	_eventsManager->getMouseInfo();

	int arrIndex = 0;
	int evidIdx = evidId;

	while (!shouldQuit() && !_eventsManager->_rightClick) {
		_voyeurArea = AREA_EVIDENCE;

		if (_currentVocId != -1 && !_soundManager->getVOCStatus()) {
			if (_voy->_vocSecondsOffset > 60)
				_voy->_vocSecondsOffset = 0;

			_soundManager->startVOCPlay(_currentVocId);
		}

		_eventsManager->delayClick(600);
		if (_eventsManager->_rightClick)
			break;
		if (count == 0 || evidIdx >= eventId)
			continue;

		pic = _voy->_evPicPtrs[arrIndex];
		_screen->sDrawPic(pic, _screen->_vPort,
			Common::Point((384 - pic->_bounds.width()) / 2,
			(240 - pic->_bounds.height()) / 2));
		_voy->_evCmPtrs[arrIndex]->startFade();
		while (!shouldQuit() && (_eventsManager->_fadeStatus & 1))
			_eventsManager->delay(1);

		flipPageAndWait();
		_eventsManager->delay(6);

		++evidIdx;
		++arrIndex;
		--count;
	}

	if (eventId == 999)
		_voy->addEvidEventEnd(evidIdx);

	for (int idx = 1; idx <= hotspots[evidId]._count; ++idx) {
		_bVoy->freeBoltMember(_voy->_boltGroupId2 + (evidId + idx) * 2);
		_bVoy->freeBoltMember(_voy->_boltGroupId2 + (evidId + idx) * 2 + 1);
	}
}

void VoyeurEngine::centerMansionView() {
	_mansionViewPos = Common::Point((MANSION_MAX_X - MANSION_VIEW_WIDTH) / 2,
		(MANSION_MAX_Y - MANSION_VIEW_HEIGHT) / 2);
}

} // End of namespace Voyeur