/* 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 "common/file.h"
#include "common/tokenizer.h"
#include "common/translation.h"

#include "graphics/font.h"
#include "graphics/fontman.h"

#include "gui/gui-manager.h"
#include "gui/ThemeEval.h"
#include "gui/textviewer.h"
#include "gui/widgets/scrollbar.h"

namespace GUI {

#define kDialogWidthPercent 0.8f
#define kDialogHeightPercent 0.8f

#define kPadX 0.05
#define kPadY 0.05

TextViewerDialog::TextViewerDialog(const Common::Path &fname)
	: Dialog(0, 0, 1, 1), _fname(fname) {
	_font = &g_gui.getFont(ThemeEngine::kFontStyleConsole);
	_charWidth = _font->getMaxCharWidth();
	_lineHeight = _font->getFontHeight() + 2;

	// Add scrollbar
	_scrollBar = new ScrollBarWidget(this, 0, 0, 1, 1);
	_scrollBar->setTarget(this);

	// I18N: Close dialog button
	_closeButton = new ButtonWidget(this, 0, 0, 1, 1, _("Close"), Common::U32String(), kCloseCmd);

	_currentPos = 0;
	_scrollLine = _linesPerPage - 1;

	reflowLayout();

	loadFile(fname);
}

TextViewerDialog::~TextViewerDialog() {
	destroy();
}

bool TextViewerDialog::loadFile(const Common::Path &fname) {
	Common::FSNode file(fname);

	if (!file.exists()) {
		warning("TextViewerDialog::loadFile(): Cannot open file %s", fname.toString(Common::Path::kNativeSeparator).c_str());

		return false;
	}

	Common::SeekableReadStream *stream = file.createReadStream();

	if (!stream) {
		warning("TextViewerDialog::loadFile(): Cannot load file %s", fname.toString(Common::Path::kNativeSeparator).c_str());

		return false;
	}

	warning("TextViewerDialog::loadFile(): File size is: %ld", long(stream->size()));

	_linesArray.clear();

	while (!stream->eos()) {
		Common::String line = stream->readString('\n');

		line.wordWrap(_lineWidth);

		Common::StringTokenizer lines(line, "\n");
		while (!lines.empty())
			_linesArray.push_back(lines.nextToken());
	}

	delete stream;

	return true;
}

void TextViewerDialog::destroy() {
	_linesArray.clear();
}

void TextViewerDialog::reflowLayout() {
	// Calculate the real width/height (rounded to char/line multiples)
	_w = (uint16)(kDialogWidthPercent * g_system->getOverlayWidth());
	_h = (uint16)((kDialogHeightPercent * g_system->getOverlayHeight() - 2) / _lineHeight);
	_h = _h * _lineHeight + 2;

	_x = (g_system->getOverlayWidth() - _w) / 2;
	_y = (g_system->getOverlayHeight() - _h) / 2;

	_padX = _w * kPadX;
	_padY = _h * kPadY;

	int16 bW = g_gui.xmlEval()->getVar("Globals.Button.Width", 0);
	int16 bH = g_gui.xmlEval()->getVar("Globals.Button.Height", 0);
	int16 padR = g_gui.xmlEval()->getVar("Globals.Padding.Right", 5);
	int16 padB = g_gui.xmlEval()->getVar("Globals.Padding.Bottom", 5);
	int16 scrollbarWidth = g_gui.xmlEval()->getVar("Globals.Scrollbar.Width", 0);

	int16 buttonOffset = bH + padB;

	_closeButton->setPos(_w - bW - padR, _h - buttonOffset);
	_closeButton->setSize(bW, bH);

	// Calculate depending values
	_lineWidth = (_w - scrollbarWidth - _padX * 2) / _charWidth;
	_linesPerPage = (_h - _padY * 2 - buttonOffset) / _lineHeight;

	_scrollBar->setPos(_w - scrollbarWidth - 1, 0);
	_scrollBar->setSize(scrollbarWidth, _h - buttonOffset);
}

void TextViewerDialog::open() {
	Dialog::open();
}

void TextViewerDialog::drawDialog(DrawLayer layerToDraw) {
	Dialog::drawDialog(layerToDraw);

	setTextDrawableArea(Common::Rect(_x, _y, _x + _w, _y + _h));

	// Draw a border
	//g_gui.hLine(_x, _y + _h - 1, _x + _w - 1, g_gui._color);

	// Draw text
	int y = _y + _padY;

	for (int line = 0; (line < _linesPerPage) && ((_currentPos + line) < (int)_linesArray.size()); line++) {
		int x = _x + _padX;
		const char *text = _linesArray[line + _currentPos].c_str();
		int w = MIN<int>(_lineWidth,  _linesArray[line + _currentPos].size());

		for (int column = 0; column < w; column++) {
			byte c = text[column];
			g_gui.theme()->drawChar(Common::Rect(x, y, x + _charWidth, y + _lineHeight), c, _font);
			x += _charWidth;
		}

		y += _lineHeight;
	}

	// Draw the scrollbar
	_scrollBar->_numEntries = _linesArray.size();
	_scrollBar->_currentPos = _currentPos;
	_scrollBar->_entriesPerPage = _linesPerPage;
	_scrollBar->recalc();
	_scrollBar->draw();
}


void TextViewerDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
	switch (cmd) {
	case kSetPositionCmd:
		_currentPos = _scrollBar->_currentPos;

		if (_currentPos < 0) {
			_currentPos = 0;
		}
		if ((_currentPos + _linesPerPage) >= (int)_linesArray.size()) {
			_currentPos = MAX<int32>(_linesArray.size() - _linesPerPage, 0);
		}

		drawDialog(kDrawLayerForeground);
		break;
	case kCloseCmd:
		close();
		break;
	default:
		return;
	}
}

void TextViewerDialog::handleMouseWheel(int x, int y, int direction) {
	_scrollBar->handleMouseWheel(x, y, direction);
}

void TextViewerDialog::handleKeyDown(Common::KeyState state) {
	switch (state.keycode) {
	case Common::KEYCODE_ESCAPE:
		close();
		break;
	case Common::KEYCODE_UP:
		_currentPos--;
		break;
	case Common::KEYCODE_DOWN:
		_currentPos++;
		break;
	case Common::KEYCODE_HOME:
		_currentPos = 0;
		break;
	case Common::KEYCODE_END:
		_currentPos = _linesArray.size() - _linesPerPage;
		break;
	case Common::KEYCODE_PAGEUP:
		_currentPos -= _linesPerPage;
		break;
	case Common::KEYCODE_PAGEDOWN:
		_currentPos += _linesPerPage;
		break;
	default:
		return;
	}

	if (_currentPos < 0) {
		_currentPos = 0;
	}
	if ((_currentPos + _linesPerPage) >= (int)_linesArray.size()) {
		_currentPos = MAX<int32>(_linesArray.size() - _linesPerPage, 0);
	}

	drawDialog(kDrawLayerForeground);
}


} // End of namespace GUI
