/* 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 "sherlock/tattoo/tattoo_darts.h"
#include "sherlock/tattoo/tattoo_fixed_text.h"
#include "sherlock/tattoo/tattoo.h"

namespace Sherlock {

namespace Tattoo {

enum {
	DART_COLOR_FORE	= 5,
	PLAYER_COLOR	= 11,
	DART_BAR_FORE	= 208
};

static const int STATUS_INFO_X = 430;
static const int STATUS_INFO_Y = 50;
static const int STATUS_INFO_WIDTH = 205;
static const int STATUS_INFO_HEIGHT = 330;
static const int STATUS2_INFO_X = 510;
static const int STATUS2_X_ADD = STATUS2_INFO_X - STATUS_INFO_X;
static const int DART_BAR_VX = 10;
static const int DART_HEIGHT_Y = 121;
static const int DART_BAR_SIZE = 150;
static const int DARTBOARD_LEFT = 73;
static const int DARTBOARD_TOP = 68;
static const int DARTBOARD_WIDTH = 257;
static const int DARTBOARD_HEIGHT = 256;
static const int DARTBOARD_TOTALX = DARTBOARD_WIDTH * 120 / 100;
static const int DARTBOARD_TOTALY = DARTBOARD_HEIGHT * 120 / 100;
static const int DARTBOARD_TOTALTOP = DARTBOARD_TOP - DARTBOARD_WIDTH / 10;
static const int DARTBOARD_TOTALLEFT = DARTBOARD_LEFT - DARTBOARD_HEIGHT / 10;
static const int CRICKET_VALUE[7] = { 20, 19, 18, 17, 16, 15, 25 };

Darts::Darts(SherlockEngine *vm) : _vm(vm) {
	_gameType = GAME_301;
	_hand1 = _hand2 = nullptr;
	_dartGraphics = nullptr;
	_dartsLeft = nullptr;
	_dartMap = nullptr;
	_dartBoard = nullptr;
	Common::fill(&_cricketScore[0][0], &_cricketScore[0][7], 0);
	Common::fill(&_cricketScore[1][0], &_cricketScore[1][7], 0);
	_score1 = _score2 = 0;
	_roundNum = 0;
	_roundScore = 0;
	_level = 0;
	_oldDartButtons = false;
	_handX = 0;
	_compPlay = 1;
	_escapePressed = false;
	_spacing = 0;
}

void Darts::playDarts(GameType gameType) {
	Events &events = *_vm->_events;
	Scene &scene = *_vm->_scene;
	Screen &screen = *_vm->_screen;
	int oldFontType = screen.fontNumber();
	int playerNum = 0;
	int lastDart;
	int numHits = 0;
	bool gameOver = false;
	bool done = false;

	// Set the game mode
	_gameType = gameType;

	screen.setFont(7);
	_spacing = screen.fontHeight() + 2;

	// Load dart graphics and initialize values
	loadDarts();
	initDarts();
	events.hideCursor();

	while (!done && !_vm->shouldQuit()) {
		int roundStart, score;
		roundStart = score = (playerNum == 0) ? _score1 : _score2;

		showNames(playerNum);
		showStatus(playerNum);
		_roundScore = 0;

		for (int idx = 0; idx < 3 && !_vm->shouldQuit(); ++idx) {
			if (_compPlay == 1)
				lastDart = throwDart(idx + 1, playerNum * 2);  /* Throw one dart */
			else
				if (_compPlay == 2)
					lastDart = throwDart(idx + 1, playerNum + 1);  /* Throw one dart */
				else
					lastDart = throwDart(idx + 1, 0);    /* Throw one dart */

			if (_gameType == GAME_301) {
				score -= lastDart;
				_roundScore += lastDart;
			} else {
				numHits = lastDart >> 16;
				if (numHits == 0)
					numHits = 1;
				if (numHits > 3)
					numHits = 3;

				lastDart = lastDart & 0xffff;
				updateCricketScore(playerNum, lastDart, numHits);
				score = (playerNum == 0) ? _score1 : _score2;
			}

			// Special case for ScummVM: I'm making pressing Escape to exit out of the Darts game as a way to skip
			// it entirely if you don't want to play all the way through it
			if (_escapePressed) {
				gameOver = true;
				done = true;
				playerNum = 0;
			}


			if (_gameType == GAME_301) {
				if (playerNum == 0)
					_score1 = score;
				else
					_score2 = score;

				if (score == 0)
					// Someone won
					gameOver = true;
			} else {
				// check for cricket game over
				bool allClosed = true;

				for (int y = 0; y < 7; y++) {
					if (_cricketScore[playerNum][y] < 3)
						allClosed = false;
				}

				if (allClosed) {
					int nOtherScore = (playerNum == 0) ? _score2 : _score1;
					if (score >= nOtherScore)
						gameOver = true;
				}
			}

			// Show scores
			showStatus(playerNum);
			screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1),
				Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));
			screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, FIXED(DartsCurrentDart), idx + 1);

			if (_gameType == GAME_301) {
				// "Scored x points"
				Common::String scoredPoints;

				// original treated 1 point and multiple points the same. Wrote "Scored 1 points"
				if (lastDart == 1) {
					scoredPoints = Common::String::format(FIXED(DartsScoredPoint), lastDart);
				} else {
					scoredPoints = Common::String::format(FIXED(DartsScoredPoints), lastDart);
				}

				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", scoredPoints.c_str());
			} else {
				Common::String hitText;

				if (lastDart != 25) {
					// Regular hit
					switch (numHits) {
					case 1: // "Hit a X"
						hitText = Common::String::format(FIXED(DartsHitSingle), lastDart);
						break;
					case 2: // "Hit double X"
						hitText = Common::String::format(FIXED(DartsHitDouble), lastDart);
						break;
					case 3: // "Hit triple X"
						hitText = Common::String::format(FIXED(DartsHitTriple), lastDart);
						break;
					default:
						break;
					}
				} else {
					// Bullseye
					switch (numHits) {
					case 1:
						hitText = Common::String(FIXED(DartsHitSingleBullseye));
						break;
					case 2:
						hitText = Common::String(FIXED(DartsHitDoubleBullseye));
						break;
					case 3:
						hitText = Common::String(FIXED(DartsHitTripleBullseye));
						break;
					default:
						break;
					}
				}
				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", hitText.c_str());
			}

			if (score != 0 && playerNum == 0 && !gameOver)
				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 3), 0,
					"%s", FIXED(DartsPressKey));

			if (gameOver) {
				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 3),
					0, "%s", FIXED(DartsGameOver));
				if (playerNum == 0) {
					screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 4), 0,
						FIXED(DartsWins), FIXED(DartsPlayerHolmes));
					_vm->setFlagsDirect(531);
				} else {
					screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 4), 0,
						FIXED(DartsWins), _opponent.c_str());
					_vm->setFlagsDirect(530);
				}

				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 5), 0,
					"%s", FIXED(DartsPressKey));

				done = true;
				idx = 10;
			} else if (_gameType == GAME_301 && score < 0) {
				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 2), 0,
					"%s!", FIXED(DartsBusted));

				// End turn
				idx = 10;
				score = roundStart;
				if (playerNum == 0)
					_score1 = score;
				else
					_score2 = score;
			}

			// Clear keyboard events
			events.clearEvents();

			if ((playerNum == 0 && _compPlay == 1) || _compPlay == 0 || done) {
				if (_escapePressed) {
					done = true;
					break;
				}
				// Wait for keypress
				do {
					events.pollEventsAndWait();
					events.setButtonState();
				} while (!_vm->shouldQuit() && !events.kbHit() && !events._pressed);
			} else {
				events.wait(40);
			}

			// Clears the status part of the board
			screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1),
				Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));
			screen.SHblitFrom(screen._backBuffer1);
		}

		playerNum ^= 1;
		if (!playerNum)
			++_roundNum;

		if (!done) {
			screen._backBuffer2.SHblitFrom((*_dartBoard)[0], Common::Point(0, 0));
			screen._backBuffer1.SHblitFrom(screen._backBuffer2);
			screen.SHblitFrom(screen._backBuffer2);
		}
	}

	// Wait for a keypress
	do {
		events.pollEventsAndWait();
		events.setButtonState();
	} while (!_vm->shouldQuit() && !events.kbHit() && !events._pressed);
	events.clearEvents();

	closeDarts();
	screen.fadeToBlack();
	screen.setFont(oldFontType);

	// Flag to return to the Billard's Academy scene
	scene._goToScene = 26;
}

void Darts::initDarts() {
	_dartInfo = Common::Rect(430, 245, 430 + 205, 245 + 150);
	_escapePressed = false;

	if (_gameType == GAME_CRICKET) {
		_dartInfo = Common::Rect(430, 245, 430 + 205, 245 + 150);
	}

	Common::fill(&_cricketScore[0][0], &_cricketScore[0][7], 0);
	Common::fill(&_cricketScore[1][0], &_cricketScore[1][7], 0);

	switch (_gameType) {
	case GAME_501:
		_score1 = _score2 = 501;
		_gameType = GAME_301;
		break;

	case GAME_301:
		_score1 = _score2 = 301;
		break;

	default:
		// Cricket
		_score1 = _score2 = 0;
		break;
	}

	_roundNum = 1;

	if (_level == 9) {
		// No computer players
		_compPlay = 0;
		_level = 0;
	} else if (_level == 8) {
		_level = _vm->getRandomNumber(3);
		_compPlay = 2;
	} else {
		// Check for opponent flags
		for (int idx = 0; idx < 4; ++idx) {
			if (_vm->readFlags(314 + idx))
				_level = idx;
		}
	}

	_opponent = FIXED(DartsPlayerJock);
}

void Darts::loadDarts() {
	Resources &res = *_vm->_res;
	Screen &screen = *_vm->_screen;
	byte palette[PALETTE_SIZE];

	// Load images
	_hand1 = new ImageFile("hand1.vgs");
	_hand2 = new ImageFile("hand2.vgs");
	_dartGraphics = new ImageFile("darts.vgs");
	_dartsLeft = new ImageFile("DartsLft.vgs");
	_dartMap = new ImageFile("DartMap.vgs");
	_dartBoard = new ImageFile("DartBd.vgs");

	// Load and set the palette
	Common::SeekableReadStream *stream = res.load("DartBd.pal");
	stream->read(palette, PALETTE_SIZE);
	screen.translatePalette(palette);
	screen.setPalette(palette);
	delete stream;

	// Load the initial background
	screen._backBuffer1.SHblitFrom((*_dartBoard)[0], Common::Point(0, 0));
	screen._backBuffer2.SHblitFrom(screen._backBuffer1);
	screen.SHblitFrom(screen._backBuffer1);
}

void Darts::closeDarts() {
	delete _dartBoard;
	delete _dartsLeft;
	delete _dartGraphics;
	delete _dartMap;
	delete _hand1;
	delete _hand2;
}

void Darts::showNames(int playerNum) {
	Screen &screen = *_vm->_screen;
	byte color;

	color = playerNum == 0 ? PLAYER_COLOR : DART_COLOR_FORE;
	screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y), 0, "%s", FIXED(DartsPlayerHolmes));
	screen._backBuffer1.fillRect(Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + _spacing + 1,
		STATUS_INFO_X + 50, STATUS_INFO_Y + _spacing + 3), color);
	screen.fillRect(Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + _spacing + 1,
		STATUS_INFO_X + 50, STATUS_INFO_Y + _spacing + 3), color);

	color = playerNum == 1 ? PLAYER_COLOR : DART_COLOR_FORE;
	screen.print(Common::Point(STATUS2_INFO_X, STATUS_INFO_Y), 0, "%s", _opponent.c_str());
	screen._backBuffer1.fillRect(Common::Rect(STATUS2_INFO_X, STATUS_INFO_Y + _spacing + 1,
		STATUS2_INFO_X + 50, STATUS_INFO_Y + _spacing + 3), color);
	screen.fillRect(Common::Rect(STATUS2_INFO_X, STATUS_INFO_Y + _spacing + 1,
		STATUS2_INFO_X + 50, STATUS_INFO_Y + _spacing + 3), color);

	screen._backBuffer2.SHblitFrom(screen._backBuffer1);
}

void Darts::showStatus(int playerNum) {
	Screen &screen = *_vm->_screen;
	const char *const CRICKET_SCORE_NAME[7] = { "20", "19", "18", "17", "16", "15", FIXED(DartsBull) };

	screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10),
		Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, STATUS_INFO_X + STATUS_INFO_WIDTH,
		STATUS_INFO_Y + STATUS_INFO_HEIGHT - 10));
	screen.print(Common::Point(STATUS_INFO_X + 30, STATUS_INFO_Y + _spacing + 4), 0, "%d", _score1);

	screen.print(Common::Point(STATUS2_INFO_X + 30, STATUS_INFO_Y + _spacing + 4), 0, "%d", _score2);

	int temp = (_gameType == GAME_CRICKET) ? STATUS_INFO_Y + 10 * _spacing + 5 : STATUS_INFO_Y + 55;

	// "Round: x"
	Common::String dartsRoundStatus = Common::String::format(FIXED(DartsCurrentRound), _roundNum);
	screen.print(Common::Point(STATUS_INFO_X, temp), 0, "%s", dartsRoundStatus.c_str());

	if (_gameType == GAME_301) {
		// "Turn Total: x"
		Common::String dartsTotalPoints = Common::String::format(FIXED(DartsCurrentTotalPoints), _roundScore);
		screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 75), 0, "%s", dartsTotalPoints.c_str());
	} else {
		// Show cricket scores
		for (int x = 0; x < 7; ++x) {
			screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 40 + x * _spacing), 0, "%s:", CRICKET_SCORE_NAME[x]);

			for (int y = 0; y < 2; ++y) {
				switch (CRICKET_SCORE_NAME[y][x]) {
				case 1:
					screen.print(Common::Point(STATUS_INFO_X + 38 + y*STATUS2_X_ADD, STATUS_INFO_Y + 40 + x * _spacing), 0, "/");
					break;
				case 2:
					screen.print(Common::Point(STATUS_INFO_X + 38 + y*STATUS2_X_ADD, STATUS_INFO_Y + 40 + x * _spacing), 0, "X");
					break;
				case 3:
					screen.print(Common::Point(STATUS_INFO_X + 38 + y * STATUS2_X_ADD - 1, STATUS_INFO_Y + 40 + x * _spacing), 0, "X");
					screen.print(Common::Point(STATUS_INFO_X + 37 + y * STATUS2_X_ADD, STATUS_INFO_Y + 40 + x * _spacing), 0, "O");
					break;
				default:
					break;
				}
			}
		}
	}

	screen.SHblitFrom(screen._backBuffer1, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10),
		Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, STATUS_INFO_X + STATUS_INFO_WIDTH,
			STATUS_INFO_Y + STATUS_INFO_HEIGHT - 10));
}

void Darts::erasePowerBars() {
	Screen &screen = *_vm->_screen;

	// Erase the old power bars and replace them with empty ones
	screen._backBuffer1.fillRect(Common::Rect(DART_BAR_VX, DART_HEIGHT_Y, DART_BAR_VX + 9, DART_HEIGHT_Y + DART_BAR_SIZE), 0);
	screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[0], Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1));
	screen.slamArea(DART_BAR_VX - 1, DART_HEIGHT_Y - 1, 10, DART_BAR_SIZE + 2);
}

bool Darts::dartHit() {
	Events &events = *_vm->_events;
	events.pollEvents();
	events.setButtonState();

	// Keyboard check
	if (events.kbHit()) {
		if (events.getKey().keycode == Common::KEYCODE_ESCAPE)
			_escapePressed = true;

		events.clearEvents();
		return true;
	}

	bool result = events._pressed && !_oldDartButtons;
	_oldDartButtons = events._pressed;
	return result;
}

int Darts::doPowerBar(const Common::Point &pt, byte color, int goToPower, int orientation) {
	Events &events = *_vm->_events;
	Screen &screen = *_vm->_screen;
	int idx = 0;

	events.clearEvents();
	events.delay(100);

	while (!_vm->shouldQuit() && idx < DART_BAR_SIZE) {
		if ((goToPower - 1) == idx)
			break;
		else if (goToPower == 0) {
			if (dartHit())
				break;
		}

		screen._backBuffer1.hLine(pt.x, pt.y + DART_BAR_SIZE- 1 - idx, pt.x + 8, color);
		screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[0], Common::Point(pt.x - 1, pt.y - 1));
		screen.slamArea(pt.x, pt.y + DART_BAR_SIZE - 1 - idx, 8, 2);

		if (!(idx % 8))
			events.wait(1);

		++idx;
	}

	return MIN(idx * 100 / DART_BAR_SIZE, 100);
}

int Darts::drawHand(int goToPower, int computer) {
	Events &events = *_vm->_events;
	Screen &screen = *_vm->_screen;
	const int HAND_OFFSET[2] = { 72, 44 };
	ImageFile *hands;
	int hand;

	goToPower = (goToPower * DARTBOARD_WIDTH) / 150;

	if (!computer) {
		hand = 0;
		hands = _hand1;
	} else {
		hand = 1;
		hands = _hand2;
	}

	_handSize.x = (*hands)[0]._offset.x + (*hands)[0]._width;
	_handSize.y = (*hands)[0]._offset.y + (*hands)[0]._height;

	// Clear keyboard buffer
	events.clearEvents();
	events.delay(100);

	Common::Point pt(DARTBOARD_LEFT - HAND_OFFSET[hand], SHERLOCK_SCREEN_HEIGHT - _handSize.y);
	int x = 0;

	while (!_vm->shouldQuit() && x < DARTBOARD_WIDTH) {
		if (computer && x >= (goToPower - 1))
			break;
		else if (goToPower == 0) {
			if (dartHit())
				break;
		}

		screen._backBuffer1.SHtransBlitFrom((*hands)[0], pt);
		screen.slamArea(pt.x - 1, pt.y, _handSize.x + 1, _handSize.y);
		screen.restoreBackground(Common::Rect(pt.x, pt.y, pt.x + _handSize.x, pt.y + _handSize.y));

		if (!(x % 8))
			events.wait(1);

		++x;
		++pt.x;
	}

	_handX = pt.x - 1;

	return MIN(x * 100 / DARTBOARD_WIDTH, 100);
}

Common::Point Darts::convertFromScreenToScoreCoords(const Common::Point &pt) const {
	return Common::Point(CLIP((int)pt.x, 0, DARTBOARD_WIDTH), CLIP((int)pt.y, 0, DARTBOARD_HEIGHT));
}

int Darts::dartScore(const Common::Point &pt) {
	Common::Point pos(pt.x - DARTBOARD_LEFT, pt.y - DARTBOARD_TOP);
	if (pos.x < 0 || pos.y < 0)
		return 0;
	int score;

	if (pos.x < DARTBOARD_WIDTH && pos.y < DARTBOARD_HEIGHT) {
		pos = convertFromScreenToScoreCoords(pos);
		score = *(const byte *)(*_dartMap)[0]._frame.getBasePtr(pos.x, pos.y);

		if (_gameType == GAME_301) {
			if (score >= 100) {
				if (score <= 120)
					// Hit a double
					score = (score - 100) * 2;
				else
					// Hit a triple
					score = (score - 120) * 3;
			}
		} else if (score >= 100) {
			if (score >= 120)
				// Hit a double
				score = (2 << 16) + (score - 100);
			else
				// Hit a triple
				score = (3 << 16) + (score - 120);
		}
	} else {
		score = 0;
	}

	return score;
}

void Darts::drawDartThrow(const Common::Point &dartPos, int computer) {
	Events &events = *_vm->_events;
	Screen &screen = *_vm->_screen;
	int cx, cy;
	int xSize = 0, ySize = 0, oldxSize = 0, oldySize = 0;
	int handOCx = 0, handOCy = 0;
	int ocx = 0, ocy = 0;
	int handOldxSize, handOldySize;
	int delta = 9;
	int dartNum;
	int hddy;
	Common::Point drawPos, oldDrawPos;

	// Draw the animation of the hand throwing the dart first
	// See which hand animation to use
	ImageFile &hands = !computer ? *_hand1 : *_hand2;
	int numFrames = !computer ? 14 : 13;

	oldxSize = oldySize = handOldxSize = handOldySize = 1;
	cx = dartPos.x;
	cy = SHERLOCK_SCREEN_HEIGHT - _handSize.y - 20;

	hddy = (cy - dartPos.y) / (numFrames - 7);
	hddy += 2;
	hddy = hddy * 10 / 8;
	if (dartPos.y > 275)
		hddy += 3;

	for (int idx = 0; idx < numFrames; ++idx) {
		_handSize.x = hands[idx]._offset.x + hands[idx]._width;
		_handSize.y = hands[idx]._offset.y + hands[idx]._height;
		int handCy = SHERLOCK_SCREEN_HEIGHT - _handSize.y;

		screen._backBuffer1.SHtransBlitFrom(hands[idx], Common::Point(_handX, handCy));
		screen.slamArea(_handX, handCy, _handSize.x + 1, _handSize.y);
		screen.slamArea(handOCx, handOCy, handOldxSize, handOldySize);
		screen.restoreBackground(Common::Rect(_handX, handCy, _handX + _handSize.x, handCy + _handSize.y));

		handOCx = _handX;
		handOCy = handCy;
		handOldxSize = _handSize.x;
		handOldySize = _handSize.y;

		if (idx > 6) {
			dartNum = idx - 6;
			if (computer)
				dartNum += 19;

			xSize = (*_dartGraphics)[dartNum]._width;
			ySize = (*_dartGraphics)[dartNum]._height;

			ocx = drawPos.x = cx - (*_dartGraphics)[dartNum]._width / 2;
			ocy = drawPos.y = cy - (*_dartGraphics)[dartNum]._height;

			// Draw dart
			screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], drawPos);

			if (drawPos.x < 0) {
				xSize += drawPos.x;
				if (xSize < 0)
					xSize = 1;
				drawPos.x = 0;
			}

			if (drawPos.y < 0) {
				ySize += drawPos.y;
				if (ySize < 0)
					ySize = 1;
				drawPos.y = 0;
			}

			// Flush the drawn dart to the screen
			screen.slamArea(drawPos.x, drawPos.y, xSize, ySize);
			if (oldDrawPos.x != -1)
				// Flush the erased dart area
				screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize);

			screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y),
				Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize));

			oldDrawPos.x = drawPos.x;
			oldDrawPos.y = drawPos.y;
			oldxSize = xSize;
			oldySize = ySize;

			cy -= hddy;
		}

		events.wait(1);
	}

	// Clear the last little bit of the hand from the screen
	screen.slamArea(handOCx, handOCy, handOldxSize, handOldySize);

	// Erase the old dart
	if (oldDrawPos.x != -1)
		screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize);

	screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y),
		Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize));

	cx = dartPos.x;
	cy = dartPos.y + 2;
	oldDrawPos.x = oldDrawPos.y = -1;

	for (int idx = 5; idx <= 23; ++idx) {
		dartNum = idx - 4;
		if (computer)
			dartNum += 19;

		if (idx < 14)
			cy -= delta--;
		else
			if (idx == 14)
				delta = 1;
		if (idx > 14)
			cy += delta++;

		xSize = (*_dartGraphics)[dartNum]._width;
		ySize = (*_dartGraphics)[dartNum]._height;

		ocx = drawPos.x = cx - (*_dartGraphics)[dartNum]._width / 2;
		ocy = drawPos.y = cy - (*_dartGraphics)[dartNum]._height;

		screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(drawPos.x, drawPos.y));

		if (drawPos.x < 0) {
			xSize += drawPos.x;
			if (xSize < 0)
				xSize = 1;
			drawPos.x = 0;
		}

		if (drawPos.y < 0) {
			ySize += drawPos.y;
			if (ySize < 0)
				ySize = 1;
			drawPos.y = 0;
		}

		// flush the dart
		screen.slamArea(drawPos.x, drawPos.y, xSize, ySize);
		if (oldDrawPos.x != -1)
			screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize);

		if (idx != 23)
			screen._backBuffer1.SHblitFrom(screen._backBuffer2, drawPos,
				Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize)); // erase dart

		events.wait(1);

		oldDrawPos = drawPos;
		oldxSize = xSize;
		oldySize = ySize;
	}

	dartNum = 19;
	if (computer)
		dartNum += 19;
	xSize = (*_dartGraphics)[dartNum]._width;
	ySize = (*_dartGraphics)[dartNum]._height;

	// Draw final dart on the board
	screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy));
	screen._backBuffer2.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy));
	screen.slamArea(ocx, ocy, xSize, ySize);
}

int Darts::findNumberOnBoard(int aim, Common::Point &pt) {
	ImageFrame &img = (*_dartMap)[0];

	if ((aim > 20) && ((aim != 25) && (aim != 50))) {
		if ((aim <= 40) && ((aim & 1) == 0)) {
			aim /= 2;
			aim += 100;
		} else {
			aim /= 3;
			aim += 120;
		}
	}

	bool done = false;
	for (int y = 0; y < img._width && !done; ++y) {
		for (int x = 0; x < img._height && !done; ++x) {
			byte score = *(const byte *)img._frame.getBasePtr(x, y);

			if (score == aim) {
				// Found a match. Aim at non-double/triple numbers whenever possible.
				// ie. Aim at 18 instead of triple 6 or double 9
				done = true;

				if (aim < 21) {
					pt.x = x + 10;
					pt.y = y + 10;

					score = *(const byte *)img._frame.getBasePtr(x, y);
					if (score != aim)
						done = false;
				} else {
					// Aiming at double or triple
					pt.x = x + 3;
					pt.y = y + 3;
				}
			}
		}
	}

	pt = convertFromScreenToScoreCoords(pt);

	if (aim == 3)
		pt.y += 30;
	if (aim == 17)
		pt.y += 10;

	if (aim == 15) {
		pt.y += 5;
		pt.x += 5;
	}

	pt.y = DARTBOARD_HEIGHT - pt.y;
	return done;
}

void Darts::getComputerNumber(int playerNum, Common::Point &targetPos) {
	int score;
	int aim = 0;
	Common::Point pt;
	bool shootBull = false;

	score = (playerNum == 0) ? _score1 : _score2;

	if (_gameType == GAME_301) {
		// Try to hit number
		aim = score;
		if(score > 60)
			shootBull = true;
	} else {
		bool cricketaimset = false;
		if (_cricketScore[playerNum][6] < 3) {
			// shoot at bull first
			aim = CRICKET_VALUE[6];
			cricketaimset = true;
		} else {
			// Now check and shoot in this order: 20,19,18,17,16,15
			for (int idx = 0; idx < 7; ++idx) {
				if (_cricketScore[playerNum][idx] < 3) {
					aim = CRICKET_VALUE[idx];
					cricketaimset = true;
					break;
				}
			}
		}

		if (!cricketaimset) {
			// Everything is closed
			// just in case we don't get set in loop below, which should never happen
			aim = 14;
			for (int idx = 0; idx < 7; ++idx) {
				if (_cricketScore[playerNum^1][idx] < 3) {
					// Opponent has this open
					aim = CRICKET_VALUE[idx];

					if (idx == 6)
						shootBull = true;
				}
			}
		}
	}

	if (shootBull) {
		// Aim at bulls eye
		targetPos.x = targetPos.y = 75;

		if (_level <= 1) {
			if (_vm->getRandomNumber(1) == 1) {
				targetPos.x += (_vm->getRandomNumber(20)-10);
				targetPos.y += (_vm->getRandomNumber(20)-10);
			}
		}
	} else {
		// Loop in case number does not exist on board
		bool done = false;
		do {
			done = findNumberOnBoard(aim, pt);
			--aim;
		} while (!done);

		pt.x += DARTBOARD_TOTALLEFT * 70 / 100;
		pt.y += DARTBOARD_TOTALTOP * 70 / 100;

		// old * 3/2
		targetPos.x = pt.x * 100 / DARTBOARD_TOTALX * 3 / 2;
		targetPos.y = pt.y * 100 / DARTBOARD_TOTALY * 3 / 2;
	}

	// the higher the level, the more accurate the throw
	int v = _vm->getRandomNumber(9);
	v += _level * 2;

	if (v <= 2) {
		targetPos.x += _vm->getRandomNumber(70) - 35;
		targetPos.y += _vm->getRandomNumber(70) - 35;
	} else if (v <= 4) {
		targetPos.x += _vm->getRandomNumber(50) - 25;
		targetPos.y += _vm->getRandomNumber(50) - 25;
	} else if (v <= 6) {
		targetPos.x += _vm->getRandomNumber(30) - 15;
		targetPos.y += _vm->getRandomNumber(30) - 15;
	} else if (v <= 8) {
		targetPos.x += _vm->getRandomNumber(20) -10;
		targetPos.y += _vm->getRandomNumber(20) -10;
	} else if (v <= 10) {
		targetPos.x += _vm->getRandomNumber(11) - 5;
		targetPos.y += _vm->getRandomNumber(11) - 5;
	}

	if (targetPos.x < 1)
		targetPos.x = 1;
	if (targetPos.y < 1)
		targetPos.y = 1;
}

int Darts::throwDart(int dartNum, int computer) {
	Events &events = *_vm->_events;
	Screen &screen = *_vm->_screen;
	int height;
	int horiz;
	Common::Point targetPos;
	Common::String temp;

	/* clear keyboard buffer */
	events.clearEvents();

	erasePowerBars();

	// "Dart # x"
	Common::String currentDart = Common::String::format(FIXED(DartsCurrentDart), dartNum);
	screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, "%s", currentDart.c_str());

	drawDartsLeft(dartNum, computer);

	if (!computer) {
		screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", FIXED(DartsStartPressKey1));
		screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 2), 0, "%s", FIXED(DartsStartPressKey2));
	}

	if (!computer) {
		// Wait for a hit
		while (!dartHit() && !_vm->shouldQuit())
			events.wait(1);
		if (_escapePressed)
			return 0;
	} else {
		events.wait(1);
	}

	drawDartsLeft(dartNum + 1, computer);
	screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1),
		Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));
	screen.SHblitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1),
		Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));

	if (computer) {
		getComputerNumber(computer - 1, targetPos);
	} else {
		// Keyboard control
		targetPos = Common::Point(0, 0);
	}

	horiz = drawHand(targetPos.x, computer);
	if (_escapePressed)
		return 0;

	height = doPowerBar(Common::Point(DART_BAR_VX, DART_HEIGHT_Y), DART_BAR_FORE, targetPos.y, 1);
	if (_escapePressed)
		return 0;

	// Invert height
	height = 101 - height;

	// Copy power bars to the secondary back buffer
	screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1),
		Common::Rect(DART_BAR_VX - 1, DART_HEIGHT_Y - 1, DART_BAR_VX - 1 + 10,
		DART_HEIGHT_Y - 1 + DART_BAR_SIZE + 2));

	Common::Point dartPos(DARTBOARD_TOTALLEFT + horiz*DARTBOARD_TOTALX / 100,
		DARTBOARD_TOTALTOP + height * DARTBOARD_TOTALY / 100);

	dartPos.x += 2 - _vm->getRandomNumber(4);
	dartPos.y += 2 - _vm->getRandomNumber(4);

	drawDartThrow(dartPos, computer);
	return dartScore(dartPos);
}

void Darts::doCricketScoreHits(int player, int scoreIndex, int numHits) {
	while (numHits--) {
		if (_cricketScore[player][scoreIndex] < 3)
			_cricketScore[player][scoreIndex]++;
		else if (_cricketScore[player ^ 1][scoreIndex] < 3) {
			if (player == 0)
				_score1 += CRICKET_VALUE[scoreIndex];
			else
				_score2 += CRICKET_VALUE[scoreIndex];
		}
	}
}

void Darts::updateCricketScore(int player, int dartVal, int multiplier) {
	if (dartVal < 15)
		return;

	if (dartVal <= 20)
		doCricketScoreHits(player, 20 - dartVal, multiplier);
	else if (dartVal == 25)
		doCricketScoreHits(player, 6, multiplier);
}

void Darts::drawDartsLeft(int dartNum, int computer) {
	Screen &screen = *_vm->_screen;
	const int DART_X1[3] = { 391, 451, 507 };
	const int DART_Y1[3] = { 373, 373, 373 };
	const int DART_X2[3] = { 393, 441, 502 };
	const int DART_Y2[3] = { 373, 373, 373 };

	screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_X1[0], DART_Y1[0]),
		Common::Rect(DART_X1[0], DART_Y1[0], SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));

	for (int idx = 2; idx >= dartNum - 1; --idx) {
		if (computer)
			screen._backBuffer1.SHtransBlitFrom((*_dartsLeft)[idx + 3], Common::Point(DART_X2[idx], DART_Y2[idx]));
		else
			screen._backBuffer1.SHtransBlitFrom((*_dartsLeft)[idx], Common::Point(DART_X1[idx], DART_Y1[idx]));
	}

	screen.slamArea(DART_X1[0], DART_Y1[0], SHERLOCK_SCREEN_WIDTH - DART_X1[0], SHERLOCK_SCREEN_HEIGHT - DART_Y1[0]);
}

} // End of namespace Tattoo

} // End of namespace Sherlock