/* 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.
 *
 * Additional copyright for this file:
 * Copyright (C) 1995-1997 Presto Studios, Inc.
 *
 * 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 "pegasus/energymonitor.h"
#include "pegasus/gamestate.h"
#include "pegasus/pegasus.h"
#include "pegasus/ai/ai_area.h"
#include "pegasus/items/inventory/airmask.h"
#include "pegasus/neighborhood/norad/constants.h"
#include "pegasus/neighborhood/norad/norad.h"
#include "pegasus/neighborhood/norad/noradelevator.h"
#include "pegasus/neighborhood/norad/pressuredoor.h"
#include "pegasus/neighborhood/norad/subcontrolroom.h"
#include "pegasus/neighborhood/norad/subplatform.h"

namespace Pegasus {

const NotificationFlags kDoneWithPressureDoorNotification = 1;

const NotificationFlags kNoradNotificationFlags = kDoneWithPressureDoorNotification;

// This class handles everything that Norad Alpha and Delta have in common, such as
// oxygen mask usage, the elevator and the pressure doors.

Norad::Norad(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id) :
		Neighborhood(nextHandler, vm, resName, id), _noradNotification(kNoradNotificationID, vm) {
	_elevatorUpSpotID = kNoHotSpotID;
	_elevatorDownSpotID = kNoHotSpotID;
	_elevatorUpRoomID = kNoHotSpotID;
	_elevatorDownRoomID = kNoHotSpotID;

	_subRoomEntryRoom1 = kNoRoomID;
	_subRoomEntryDir1 = kNoDirection;
	_subRoomEntryRoom2 = kNoRoomID;
	_subRoomEntryDir2 = kNoDirection;
	_upperPressureDoorRoom = kNoRoomID;
	_lowerPressureDoorRoom = kNoRoomID;

	_upperPressureDoorUpSpotID = kNoHotSpotID;
	_upperPressureDoorDownSpotID = kNoHotSpotID;
	_upperPressureDoorAbortSpotID = kNoHotSpotID;

	_lowerPressureDoorUpSpotID = kNoHotSpotID;
	_lowerPressureDoorDownSpotID = kNoHotSpotID;
	_lowerPressureDoorAbortSpotID = kNoHotSpotID;

	_pressureSoundIn = 0xffffffff;
	_pressureSoundOut = 0xffffffff;
	_equalizeSoundIn = 0xffffffff;
	_equalizeSoundOut = 0xffffffff;
	_accessDeniedIn = 0xffffffff;
	_accessDeniedOut = 0xffffffff;

	_platformRoom = kNoRoomID;
	_subControlRoom = kNoRoomID;

	_doneWithPressureDoor = false;

	_noradNotification.notifyMe(this, kNoradNotificationFlags, kNoradNotificationFlags);
}

GameInteraction *Norad::makeInteraction(const InteractionID interactionID) {
	PressureDoor *pressureDoor;
	SubControlRoom *subControl;

	switch (interactionID) {
	case kNoradElevatorInteractionID:
		return new NoradElevator(this, _elevatorUpRoomID, _elevatorDownRoomID, _elevatorUpSpotID, _elevatorDownSpotID);
	case kNoradPressureDoorInteractionID:
		if (GameState.getCurrentRoom() == _upperPressureDoorRoom)
			pressureDoor = new PressureDoor(this, true, _upperPressureDoorUpSpotID, _upperPressureDoorDownSpotID,
					_upperPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut);
		else
			pressureDoor = new PressureDoor(this, false, _lowerPressureDoorUpSpotID, _lowerPressureDoorDownSpotID,
					_lowerPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut);

		if (GameState.getCurrentRoom() == kNorad59West && playingAgainstRobot())
			pressureDoor->playAgainstRobot();

		return pressureDoor;
	case kNoradSubControlRoomInteractionID:
		subControl = new SubControlRoom(this);

		if (GameState.getCurrentRoom() == kNorad60West && playingAgainstRobot())
			subControl->playAgainstRobot();

		return subControl;
	case kNoradSubPlatformInteractionID:
		return new SubPlatform(this);
	default:
		return 0;
	}
}

void Norad::flushGameState() {
	g_energyMonitor->saveCurrentEnergyValue();
}

void Norad::start() {
	setUpAirMask();
	Neighborhood::start();
}

void Norad::activateHotspots() {
	Neighborhood::activateHotspots();

	RoomID room = GameState.getCurrentRoom();
	if (room == _elevatorUpRoomID)
		_neighborhoodHotspots.activateOneHotspot(_elevatorDownSpotID);
	else if (room == _elevatorDownRoomID)
		_neighborhoodHotspots.activateOneHotspot(_elevatorUpSpotID);
}

void Norad::arriveAt(const RoomID room, const DirectionConstant direction) {
	Neighborhood::arriveAt(room, direction);

	if (GameState.getCurrentRoom() == _elevatorUpRoomID || GameState.getCurrentRoom() == _elevatorDownRoomID)
		arriveAtNoradElevator();
	else if (GameState.getCurrentRoom() == _upperPressureDoorRoom)
		arriveAtUpperPressureDoorRoom();
	else if (GameState.getCurrentRoom() == _lowerPressureDoorRoom)
		arriveAtLowerPressureDoorRoom();
	else if (GameState.getCurrentRoom() == _platformRoom)
		arriveAtSubPlatformRoom();
	else if (GameState.getCurrentRoom() == _subControlRoom)
		arriveAtSubControlRoom();

	if (_doneWithPressureDoor) {
		_doneWithPressureDoor = false;
		openDoor();
	}
}

void Norad::arriveAtNoradElevator() {
	if (_currentInteraction)
		_currentInteraction->startOverInteraction();
	else
		newInteraction(kNoradElevatorInteractionID);
}

void Norad::arriveAtUpperPressureDoorRoom() {
	newInteraction(kNoradPressureDoorInteractionID);
}

void Norad::arriveAtLowerPressureDoorRoom() {
	newInteraction(kNoradPressureDoorInteractionID);
}

void Norad::arriveAtSubPlatformRoom() {
	newInteraction(kNoradSubPlatformInteractionID);
}

void Norad::arriveAtSubControlRoom() {
	newInteraction(kNoradSubControlRoomInteractionID);
}

int16 Norad::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) {
	int16 result = Neighborhood::getStaticCompassAngle(room, dir);

	if (room == _elevatorUpRoomID || room == _elevatorDownRoomID)
		result += kElevatorCompassAngle;
	else if (room == _platformRoom)
		result += kSubPlatformCompassAngle;
	else if (room == _subControlRoom)
		result += kSubControlCompassAngle;

	return result;
}

CanOpenDoorReason Norad::canOpenDoor(DoorTable::Entry &entry) {
	if (((GameState.getCurrentRoom() == _subRoomEntryRoom1 && GameState.getCurrentDirection() == _subRoomEntryDir1) ||
			(GameState.getCurrentRoom() == _subRoomEntryRoom2 && GameState.getCurrentDirection() == _subRoomEntryDir2)) &&
			GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure)
		return kCantOpenBadPressure;

	return Neighborhood::canOpenDoor(entry);
}

void Norad::cantOpenDoor(CanOpenDoorReason reason) {
	if (reason == kCantOpenBadPressure)
		playSpotSoundSync(_pressureSoundIn, _pressureSoundOut);
	else
		playSpotSoundSync(_accessDeniedIn, _accessDeniedOut);
}

void Norad::startExitMovie(const ExitTable::Entry &exitEntry) {
	if (GameState.getCurrentRoom() == _elevatorUpRoomID) {
		if (exitEntry.exitRoom != _elevatorDownRoomID)
			newInteraction(kNoInteractionID);
	} else if (GameState.getCurrentRoom() == _elevatorDownRoomID) {
		if (exitEntry.exitRoom != _elevatorUpRoomID)
			newInteraction(kNoInteractionID);
	} else {
		newInteraction(kNoInteractionID);
	}

	Neighborhood::startExitMovie(exitEntry);
}

void Norad::startZoomMovie(const ZoomTable::Entry &zoomEntry) {
	newInteraction(kNoInteractionID);
	Neighborhood::startZoomMovie(zoomEntry);
}

void Norad::upButton(const Input &input) {
	if (GameState.getCurrentRoom() != _elevatorUpRoomID && GameState.getCurrentRoom() != _elevatorDownRoomID)
		Neighborhood::upButton(input);
}

void Norad::setUpAirMask() {
	_airMaskCallBack.setNotification(&_neighborhoodNotification);
	_airMaskCallBack.initCallBack(&_airMaskTimer, kCallBackAtExtremes);
	_airMaskCallBack.setCallBackFlag(kAirTimerExpiredFlag);
	_neighborhoodNotification.notifyMe(this, kAirTimerExpiredFlag, kAirTimerExpiredFlag);
	_airMaskCallBack.scheduleCallBack(kTriggerAtStop, 0, 0);
	_airMaskTimer.setScale(1);
	_airMaskTimer.setSegment(0, kNoradAirMaskTimeLimit);
	checkAirMask();
}

void Norad::checkAirMask() {
	// WORKAROUND: The original game forgot to handle the case where the canister would
	// be removed, leading to the timer remaining active.
	if (!GameState.getNoradGassed() || (g_airMask && g_airMask->isAirFilterOn())) {
		_airMaskTimer.stop();
	} else if (GameState.getNoradGassed() && !_airMaskTimer.isRunning()) {
		_airMaskTimer.setTime(0);
		_airMaskTimer.start();
	}

	loadAmbientLoops();
}

void Norad::receiveNotification(Notification *notification, const NotificationFlags flags) {
	if (notification == &_neighborhoodNotification && (flags & kAirTimerExpiredFlag) != 0)
		((PegasusEngine *)g_engine)->die(kDeathGassedInNorad);

	Neighborhood::receiveNotification(notification, flags);

	if (notification == &_noradNotification) {
		// Must be kDoneWithPressureDoorNotification...
		Input scratch;
		_doneWithPressureDoor = true;
		downButton(scratch);
	}
}

uint16 Norad::getDateResID() const {
	return kDate2112ID;
}

Common::String Norad::getBriefingMovie() {
	return "Images/AI/Norad/XNO";
}

void Norad::pickedUpItem(Item *item) {
	Neighborhood::pickedUpItem(item);
	g_AIArea->checkMiddleArea();
}

void Norad::doneWithPressureDoor() {
	_noradNotification.setNotificationFlags(kDoneWithPressureDoorNotification, kDoneWithPressureDoorNotification);
}

} // End of namespace Pegasus