/* 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "mm/xeen/dialogs/dialogs_map.h"
#include "mm/xeen/resources.h"
#include "mm/xeen/xeen.h"

namespace MM {
namespace Xeen {

#define MAP_SIZE 16
#define MAP_DIFF (MAP_SIZE / 2)
#define MAP_XSTART 80
#define MAP_YSTART 38
#define TILE_WIDTH 10
#define TILE_HEIGHT 8

void MapDialog::show(XeenEngine *vm) {
	MapDialog *dlg = new MapDialog(vm);
	dlg->execute();
	delete dlg;
}

void MapDialog::execute() {
	EventsManager &events = *_vm->_events;
	Interface &intf = *_vm->_interface;
	Map &map = *_vm->_map;
	Party &party = *_vm->_party;
	Windows &windows = *_vm->_windows;

	_pt = party._mazePosition;
	_globalSprites.load("global.icn");

	if (_pt.x < 8 && map.mazeData()._surroundingMazes._west == 0) {
		_arrowPt.x = _pt.x * 10 + 4;
		_pt.x = 7;
	} else if (_pt.x > 23) {
		_arrowPt.x = (byte)(_pt.x * 10 + 100);
		_pt.x = 23;
	} else if (_pt.x > 8 && map.mazeData()._surroundingMazes._east == 0) {
		_arrowPt.x = (byte)(_pt.x * 10 + 4);
		_pt.x = 7;
	} else {
		_arrowPt.x = 74;
	}

	if (_pt.y < 8 && map.mazeData()._surroundingMazes._south == 0) {
		_arrowPt.y = ((15 - _pt.y) << 3) + 13;
		_pt.y = 8;
	} else if (_pt.y > 24) {
		_arrowPt.y = ((15 - (_pt.y - 16)) << 3) + 13;
		_pt.y = 24;
	} else if (_pt.y >= 8 && map.mazeData()._surroundingMazes._north == 0) {
		_arrowPt.y = ((15 - _pt.y) << 3) + 13;
		_pt.y = 8;
	} else {
		_arrowPt.y = 69;
	}

	windows[5].open();
	bool drawFlag = true;

#ifdef USE_TTS
	_vm->stopTextToSpeech();
#endif
	bool ttsVoiceText = true;

	events.updateGameCounter();
	do {
		if (drawFlag)
			intf.draw3d(false, false);
		windows[5].writeString("\r");

		if (map._isOutdoors)
			drawOutdoors();
		else
			drawIndoors();

		windows[5].frame();
		if (!map._isOutdoors) {
			map._tileSprites.draw(0, 52, Common::Point(76, 30));
		} else if (_frameEndFlag) {
			_globalSprites.draw(0, party._mazeDirection + 1,
				Common::Point(_arrowPt.x + 76, _arrowPt.y + 25));
		}

		if (events.timeElapsed() > 5) {
			// Set the flag to make the basic arrow blinking effect
			_frameEndFlag = !_frameEndFlag;
			events.updateGameCounter();
		}

		windows[5].writeString(Common::String::format(Res.MAP_TEXT,
			map._mazeName.c_str(), party._mazePosition.x,
			party._mazePosition.y, Res.DIRECTION_TEXT[party._mazeDirection]), ttsVoiceText);
		ttsVoiceText = false;
		windows[5].update();
		windows[3].update();

		events.ipause5(2);
		drawFlag = false;
	} while (!_vm->shouldExit() && !events.isKeyMousePressed());

#ifdef USE_TTS
	_vm->stopTextToSpeech();
#endif
	events.clearEvents();
	windows[5].close();
}

void MapDialog::drawOutdoors() {
	Map &map = *g_vm->_map;
	int v, frame;

	// Draw outdoors map
	for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
			--mazeY, yp += TILE_HEIGHT) {
		for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
				xp += TILE_WIDTH, ++mazeX) {
			v = map.mazeLookup(Common::Point(mazeX, mazeY), 0);
			assert(v != INVALID_CELL);
			frame = map.mazeDataCurrent()._surfaceTypes[v];

			if (map._currentSteppedOn) {
				map._tileSprites.draw(0, frame, Common::Point(xp, yp));
			}
		}
	}

	for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
			--mazeY, yp += TILE_HEIGHT) {
		for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
				xp += TILE_WIDTH, ++mazeX) {
			v = map.mazeLookup(Common::Point(mazeX, mazeY), 4);
			assert(v != INVALID_CELL);
			frame = map.mazeDataCurrent()._wallTypes[v];

			if (frame && map._currentSteppedOn)
				map._tileSprites.draw(0, frame + 16, Common::Point(xp, yp));
		}
	}

	for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
			--mazeY, yp += TILE_HEIGHT) {
		for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
				xp += TILE_WIDTH, ++mazeX) {
			frame = map.mazeLookup(Common::Point(mazeX, mazeY), 8, 0xff);

			if (frame && map._currentSteppedOn)
				map._tileSprites.draw(0, frame + 32, Common::Point(xp, yp));
		}
	}
}

void MapDialog::drawIndoors() {
	Map &map = *g_vm->_map;
	Party &party = *g_vm->_party;
	int v, frame;
	int frame2 = _animFrame;
	_animFrame = (_animFrame + 2) % 8;

	// Draw indoors map
	frame2 = (frame2 + 2) % 8;

	// Draw default ground for all the valid explored areas
	for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
			yp += TILE_HEIGHT, --mazeY) {
		for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
				xp += TILE_WIDTH, ++mazeX) {
			v = map.mazeLookup(Common::Point(mazeX, mazeY), 0, 0xffff);

			if (v != INVALID_CELL && map._currentSteppedOn)
				map._tileSprites.draw(0, 0, Common::Point(xp, yp));
		}
	}

	// Draw thinner ground tiles on the left edge of the map
	for (int yp = MAP_YSTART + 5, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
			yp += TILE_HEIGHT, --mazeY) {
		v = map.mazeLookup(Common::Point(_pt.x - 8, mazeY), 0, 0xffff);

		if (v != INVALID_CELL && map._currentSurfaceId != 0 && map._currentSteppedOn)
			map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
				map._currentSurfaceId], Common::Point(75, yp));
	}

	// Draw thin tile portion on top-left corner of map
	v = map.mazeLookup(Common::Point(_pt.x - 8, _pt.y + 8), 0, 0xffff);
	if (v != INVALID_CELL && map._currentSurfaceId != 0 && map._currentSteppedOn)
		map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
			map._currentSurfaceId], Common::Point(75, 35));

	// Draw any thin tiles at the very top of the map
	for (int xp = MAP_XSTART + 5, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
			xp += TILE_WIDTH, ++mazeX) {
		v = map.mazeLookup(Common::Point(mazeX, _pt.y + 8), 0, 0xffff);

		if (v != INVALID_CELL && map._currentSurfaceId != 0 && map._currentSteppedOn)
			map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
				map._currentSurfaceId], Common::Point(xp, 35));
	}

	// Draw the default ground tiles
	for (int yp = MAP_YSTART + 5, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
			yp += TILE_HEIGHT, --mazeY) {
		for (int xp = MAP_XSTART + 5, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
				xp += TILE_WIDTH, ++mazeX) {
			v = map.mazeLookup(Common::Point(mazeX, mazeY), 0, 0xffff);

			if (v != INVALID_CELL && map._currentSurfaceId && map._currentSteppedOn)
				map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
					map._currentSurfaceId], Common::Point(xp, yp));
		}
	}

	// Draw walls on left and top edges of map
	for (int xp = MAP_XSTART, yp = MAP_YSTART + (MAP_SIZE - 1) * TILE_HEIGHT,
			mazeX = _pt.x - (MAP_DIFF - 1), mazeY = _pt.y - MAP_DIFF;
			mazeX < (_pt.x + MAP_DIFF); xp += TILE_WIDTH, yp -= TILE_HEIGHT, ++mazeX, ++mazeY) {
		// Draw walls on left edge of map
		v = map.mazeLookup(Common::Point(_pt.x - 8, mazeY), 12);

		switch (v) {
		case SURFTYPE_DIRT:
			frame = 18;
			break;
		case SURFTYPE_SNOW:
			frame = 22;
			break;
		case SURFTYPE_SWAMP:
		case SURFTYPE_CLOUD:
			frame = 16;
			break;
		case SURFTYPE_LAVA:
		case SURFTYPE_DWATER:
			frame = 2;
			break;
		case SURFTYPE_DESERT:
			frame = 30;
			break;
		case SURFTYPE_ROAD:
			frame = 32;
			break;
		case SURFTYPE_TFLR:
			frame = 20;
			break;
		case SURFTYPE_SKY:
			frame = 28;
			break;
		case SURFTYPE_CROAD:
			frame = 14;
			break;
		case SURFTYPE_SEWER:
			frame = frame2 + 4;
			break;
		case SURFTYPE_SCORCH:
			frame = 24;
			break;
		case SURFTYPE_SPACE:
			frame = 26;
			break;
		default:
			frame = -1;
			break;
		}

		if (frame != -1 && map._currentSteppedOn)
			map._tileSprites.draw(0, frame, Common::Point(70, yp));

		// Draw walls on top edge of map
		v = map.mazeLookup(Common::Point(mazeX, _pt.y + 8), 0);

		switch (v) {
		case SURFTYPE_DIRT:
			frame = 19;
			break;
		case SURFTYPE_GRASS:
			frame = 35;
			break;
		case SURFTYPE_SNOW:
			frame = 23;
			break;
		case SURFTYPE_SWAMP:
		case SURFTYPE_CLOUD:
			frame = 17;
			break;
		case SURFTYPE_LAVA:
		case SURFTYPE_DWATER:
			frame = 3;
			break;
		case SURFTYPE_DESERT:
			frame = 31;
			break;
		case SURFTYPE_ROAD:
			frame = 33;
			break;
		case SURFTYPE_TFLR:
			frame = 21;
			break;
		case SURFTYPE_SKY:
			frame = 29;
			break;
		case SURFTYPE_CROAD:
			frame = 15;
			break;
		case SURFTYPE_SEWER:
			frame = frame2 + 5;
			break;
		case SURFTYPE_SCORCH:
			frame = 25;
			break;
		case SURFTYPE_SPACE:
			frame = 27;
			break;
		default:
			frame = -1;
			break;
		}

		if (frame != -1 && map._currentSteppedOn)
			map._tileSprites.draw(0, frame, Common::Point(xp, 30));
	}

	// Draw the walls for the remaining cells of the minimap
	for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1, yCtr = 0; yCtr < MAP_SIZE;
			yp += TILE_HEIGHT, --mazeY, ++yCtr) {
		for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1), xCtr = 0; xCtr < MAP_SIZE;
				xp += TILE_WIDTH, ++mazeX, ++xCtr) {
			// Draw the arrow if at the correct position
			if ((_arrowPt.x / 10) == xCtr && (14 - (_arrowPt.y / 10)) == yCtr && _frameEndFlag) {
				_globalSprites.draw(0, party._mazeDirection + 1,
					Common::Point(_arrowPt.x + 81, _arrowPt.y + 29));
			}

			v = map.mazeLookup(Common::Point(mazeX, mazeY), 12);
			switch (v) {
			case 1:
				frame = 18;
				break;
			case 2:
				frame = 34;
				break;
			case 3:
				frame = 22;
				break;
			case 4:
			case 13:
				frame = 16;
				break;
			case 5:
			case 8:
				frame = 2;
				break;
			case 6:
				frame = 30;
				break;
			case 7:
				frame = 32;
				break;
			case 9:
				frame = 20;
				break;
			case 10:
				frame = 28;
				break;
			case 11:
				frame = 14;
				break;
			case 12:
				frame = frame2 + 4;
				break;
			case 14:
				frame = 24;
				break;
			case 15:
				frame = 26;
				break;
			default:
				frame = -1;
				break;
			}

			if (frame != -1 && map._currentSteppedOn)
				map._tileSprites.draw(0, frame, Common::Point(xp, yp));

			v = map.mazeLookup(Common::Point(mazeX, mazeY), 0);
			switch (v) {
			case 1:
				frame = 19;
				break;
			case 2:
				frame = 35;
				break;
			case 3:
				frame = 23;
				break;
			case 4:
			case 13:
				frame = 17;
				break;
			case 5:
			case 8:
				frame = 3;
				break;
			case 6:
				frame = 31;
				break;
			case 7:
				frame = 33;
				break;
			case 9:
				frame = 21;
				break;
			case 10:
				frame = 29;
				break;
			case 11:
				frame = 15;
				break;
			case 12:
				frame = frame2 + 5;
				break;
			case 14:
				frame = 25;
				break;
			case 15:
				frame = 27;
				break;
			default:
				frame = -1;
				break;
			}

			if (frame != -1 && map._currentSteppedOn)
				map._tileSprites.draw(0, frame, Common::Point(xp, yp));
		}
	}

	// Draw overlay on cells that haven't been stepped on yet
	for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
			yp += TILE_HEIGHT, --mazeY) {
		for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
				xp += TILE_WIDTH, ++mazeX) {
			v = map.mazeLookup(Common::Point(mazeX, mazeY), 0, 0xffff);

			if (v == INVALID_CELL || !map._currentSteppedOn)
				map._tileSprites.draw(0, 1, Common::Point(xp, yp));
		}
	}
}

} // End of namespace Xeen
} // End of namespace MM
