/*
 * Copyright (C) 2004-2012 Geometer Plus <contact@geometerplus.com>
 *
 * 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 <algorithm>

#include <ZLStringUtil.h>
#include <ZLDialogManager.h>
#include <ZLDialog.h>
#include <ZLOptionsDialog.h>
#include <ZLSimpleOptionEntry.h>
#include <ZLibrary.h>

#include <ZLBlockTreeView.h>
#include <ZLTextView.h>
#include <ZLTextSelectionModel.h>

#include "Reader.h"
#include "ReaderActions.h"
#include "BookTextView.h"
#include "ContentsView.h"
#include "../optionsDialog/bookInfo/BookInfoDialog.h"
#include "../optionsDialog/library/LibraryOptionsDialog.h"
#include "../optionsDialog/network/NetworkOptionsDialog.h"
#include "../optionsDialog/system/SystemOptionsDialog.h"
#include "../optionsDialog/reading/ReadingOptionsDialog.h"
#include "../optionsDialog/lookAndFeel/OptionsPage.h"
#include "../optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.h"

#include "../bookmodel/BookModel.h"
#include "../options/FBTextStyle.h"

#include "../database/booksdb/BooksDBUtil.h"
#include "../database/booksdb/BooksDB.h"
#include "../library/Library.h"
#include "../library/Book.h"

ModeDependentAction::ModeDependentAction(int visibleInModes) : myVisibleInModes(visibleInModes) {
}

bool ModeDependentAction::isVisible() const {
	return (Reader::Instance().mode() & myVisibleInModes) != 0;
}

SetModeAction::SetModeAction(Reader::ViewMode modeToSet, int visibleInModes) : ModeDependentAction(visibleInModes), myModeToSet(modeToSet) {
}

void SetModeAction::run() {
	Reader::Instance().setMode(myModeToSet);
}

void ShowHelpAction::run() {
	Reader &reader = Reader::Instance();
	shared_ptr<Book> book = BooksDBUtil::getBook(reader.helpFileName(ZLibrary::Language()));
	if (book.isNull()) {
		book = BooksDBUtil::getBook(reader.helpFileName("en"));
	}
	if (!book.isNull()) {
		reader.openBook(book);
		reader.setMode(Reader::BOOK_TEXT_MODE);
		reader.refreshWindow();
	} else {
		ZLDialogManager::Instance().errorBox(ZLResourceKey("noHelpBox"));
	}
}

void ShowOptionsDialogAction::run() {
	std::string actionId = Reader::Instance().LastOpenedPreferencesDialog.value();
	if (actionId.empty()) {
		return;
	}
	Reader::Instance().doAction(actionId);
}

void ShowLibraryOptionsDialogAction::run() {
	Reader::Instance().LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_LIBRARY_OPTIONS_DIALOG);
	LibraryOptionsDialog().dialog().run();
}

void ShowNetworkOptionsDialogAction::run() {
	Reader::Instance().LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_NETWORK_OPTIONS_DIALOG);
	NetworkOptionsDialog().dialog().run();
}

void ShowSystemOptionsDialogAction::run() {
	Reader::Instance().LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_SYSTEM_OPTIONS_DIALOG);
	SystemOptionsDialog().dialog().run();
}

void ShowReadingOptionsDialogAction::run() {
	Reader::Instance().LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_READING_OPTIONS_DIALOG);
	ReadingOptionsDialog().dialog().run();
}

void ShowLookAndFeelOptionsDialogAction::run() {
	Reader::Instance().LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_LOOKANDFEEL_OPTIONS_DIALOG);
	LookAndFeelOptionsDialog().dialog().run();
}

ShowContentsAction::ShowContentsAction() : SetModeAction(Reader::CONTENTS_MODE, Reader::BOOK_TEXT_MODE) {
}

bool ShowContentsAction::isVisible() const {
	return ModeDependentAction::isVisible() && !((ContentsView&)*Reader::Instance().myContentsView).isEmpty();
}

ScrollToHomeAction::ScrollToHomeAction() : ModeDependentAction(Reader::BOOK_TEXT_MODE) {
}

bool ScrollToHomeAction::isEnabled() const {
	if (!isVisible()) {
		return false;
	}
	ZLTextWordCursor cursor = Reader::Instance().bookTextView().textArea().startCursor();
	return cursor.isNull() || !cursor.isStartOfParagraph() || !cursor.paragraphCursor().isFirst();
}

void ScrollToHomeAction::run() {
	Reader::Instance().bookTextView().scrollToHome();
}

ScrollToStartOfTextAction::ScrollToStartOfTextAction() : ModeDependentAction(Reader::BOOK_TEXT_MODE) {
}

bool ScrollToStartOfTextAction::isEnabled() const {
	if (!isVisible()) {
		return false;
	}
	ZLTextWordCursor cursor = Reader::Instance().bookTextView().textArea().startCursor();
	return cursor.isNull() || !cursor.isStartOfParagraph() || !cursor.paragraphCursor().isFirst();
}

void ScrollToStartOfTextAction::run() {
	Reader::Instance().bookTextView().scrollToStartOfText();
}

ScrollToEndOfTextAction::ScrollToEndOfTextAction() : ModeDependentAction(Reader::BOOK_TEXT_MODE) {
}

bool ScrollToEndOfTextAction::isEnabled() const {
	if (!isVisible()) {
		return false;
	}
	ZLTextWordCursor cursor = Reader::Instance().bookTextView().textArea().endCursor();
	return cursor.isNull() || !cursor.isEndOfParagraph() || !cursor.paragraphCursor().isLast();
}

void ScrollToEndOfTextAction::run() {
	Reader::Instance().bookTextView().scrollToEndOfText();
}

ShowBookInfoAction::ShowBookInfoAction() : ModeDependentAction(Reader::BOOK_TEXT_MODE | Reader::CONTENTS_MODE | Reader::FOOTNOTE_MODE) {
}

void ShowBookInfoAction::run() {
	Reader &reader = Reader::Instance();
	reader.LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_BOOK_INFO_DIALOG);
	shared_ptr<Book> book = reader.myModel->book();
	if (BookInfoDialog(book).dialog().run()) {
		reader.openBook(book);
		reader.refreshWindow();
	}
}

UndoAction::UndoAction(int visibleInModes) : ModeDependentAction(visibleInModes) {
}

bool UndoAction::isEnabled() const {
	Reader &reader = Reader::Instance();
	return (reader.mode() != Reader::BOOK_TEXT_MODE) ||
					reader.bookTextView().canUndoPageMove();
}

void UndoAction::run() {
	Reader &reader = Reader::Instance();
	if (reader.mode() == Reader::BOOK_TEXT_MODE) {
		reader.bookTextView().undoPageMove();
	} else {
		reader.restorePreviousMode();
	}
}

RedoAction::RedoAction() : ModeDependentAction(Reader::BOOK_TEXT_MODE) {
}

bool RedoAction::isEnabled() const {
	return isVisible() && Reader::Instance().bookTextView().canRedoPageMove();
}

void RedoAction::run() {
	Reader::Instance().bookTextView().redoPageMove();
}

ChangeFontSizeAction::ChangeFontSizeAction(int delta) : myDelta(delta) {
}

bool ChangeFontSizeAction::isEnabled() const {
	ZLIntegerRangeOption &option = FBTextStyle::Instance().FontSizeOption;
	if (myDelta < 0) {
		return option.value() > option.minValue();
	} else {
		return option.value() < option.maxValue();
	}
}

void ChangeFontSizeAction::run() {
	Reader &reader = Reader::Instance();
	ZLIntegerRangeOption &option = FBTextStyle::Instance().FontSizeOption;
	option.setValue(option.value() + myDelta);
	reader.clearTextCaches();
	reader.refreshWindow();
}

bool OpenPreviousBookAction::isVisible() const {
	const Reader &reader = Reader::Instance();
	if ((reader.mode() != Reader::BOOK_TEXT_MODE) &&
			(reader.mode() != Reader::CONTENTS_MODE)) {
		return false;
	}
	return Library::Instance().recentBooks().size() > 1;
}

void OpenPreviousBookAction::run() {
	Reader &reader = Reader::Instance();
	const BookList &books = Library::Instance().recentBooks();
	reader.openBook(books[1]);
	reader.refreshWindow();
	reader.resetWindowCaption();
}

void CancelAction::run() {
	Reader &reader = Reader::Instance();
	switch (reader.myActionOnCancel) {
		case Reader::UNFULLSCREEN:
			if (reader.isFullscreen()) {
				reader.setFullscreen(false);
				return;
			} else if (reader.mode() != Reader::BOOK_TEXT_MODE) {
				reader.restorePreviousMode();
				return;
			}
			break;
		case Reader::RETURN_TO_TEXT_MODE:
			if (reader.mode() != Reader::BOOK_TEXT_MODE) {
				reader.restorePreviousMode();
				return;
			} else if (reader.isFullscreen()) {
				reader.setFullscreen(false);
				return;
			}
			break;
	}
	if (reader.QuitOnCancelOption.value()) {
		reader.quit();
	}
}

bool ToggleIndicatorAction::isVisible() const {
	ZLIntegerRangeOption &option = FBView::commonIndicatorInfo().TypeOption;
	switch (option.value()) {
		case FBIndicatorStyle::FB_INDICATOR:
		case FBIndicatorStyle::NONE:
			return true;
	}
	return false;
}

void ToggleIndicatorAction::run() {
	ZLIntegerRangeOption &option = FBView::commonIndicatorInfo().TypeOption;
	switch (option.value()) {
		case FBIndicatorStyle::OS_SCROLLBAR:
			break;
		case FBIndicatorStyle::FB_INDICATOR:
			option.setValue(FBIndicatorStyle::NONE);
			Reader::Instance().refreshWindow();
			break;
		case FBIndicatorStyle::NONE:
			option.setValue(FBIndicatorStyle::FB_INDICATOR);
			Reader::Instance().refreshWindow();
			break;
	}
}

void QuitAction::run() {
	Reader::Instance().closeView();
}

void ForceQuitAction::run() {
	Reader::Instance().quit();
}

bool GotoNextTOCSectionAction::isVisible() const {
	Reader &reader = Reader::Instance();
	if (reader.mode() != Reader::BOOK_TEXT_MODE) {
		return false;
	}
	const ContentsView &contentsView = (const ContentsView&)*reader.myContentsView;
	shared_ptr<ZLTextModel> model = contentsView.textArea().model();
	return !model.isNull() && (model->paragraphsNumber() > 1);
}

bool GotoNextTOCSectionAction::isEnabled() const {
	Reader &reader = Reader::Instance();
	const ContentsView &contentsView = (const ContentsView&)*reader.myContentsView;
	shared_ptr<ZLTextModel> model = contentsView.textArea().model();
	return !model.isNull() && ((int)contentsView.currentTextViewParagraph() < (int)model->paragraphsNumber() - 1);
}

void GotoNextTOCSectionAction::run() {
	Reader &reader = Reader::Instance();
	ContentsView &contentsView = (ContentsView&)*reader.myContentsView;
	std::size_t current = contentsView.currentTextViewParagraph();
	const ContentsModel &contentsModel = (const ContentsModel&)*contentsView.textArea().model();
	int reference = contentsModel.reference(((const ZLTextTreeParagraph*)contentsModel[current + 1]));
	if (reference != -1) {
		((ZLTextView&)*reader.myBookTextView).gotoParagraph(reference);
		reader.refreshWindow();
	}
}

bool GotoPreviousTOCSectionAction::isVisible() const {
	const Reader &reader = Reader::Instance();
	if (reader.mode() != Reader::BOOK_TEXT_MODE) {
		return false;
	}
	const ContentsView &contentsView = (const ContentsView&)*reader.myContentsView;
	shared_ptr<ZLTextModel> model = contentsView.textArea().model();
	return !model.isNull() && (model->paragraphsNumber() > 1);
}

bool GotoPreviousTOCSectionAction::isEnabled() const {
	const Reader &reader = Reader::Instance();
	const ContentsView &contentsView = (const ContentsView&)*reader.myContentsView;
	shared_ptr<ZLTextModel> model = contentsView.textArea().model();
	if (model.isNull()) {
		return false;
	}
	const ContentsModel &contentsModel = (const ContentsModel&)*model;
	int tocIndex = contentsView.currentTextViewParagraph(false);
	if (tocIndex > 0) {
		return true;
	}
	if (tocIndex == 0) {
		const ZLTextWordCursor &cursor = reader.bookTextView().textArea().startCursor();
		if (cursor.isNull()) {
			return false;
		}
		if (cursor.elementIndex() > 0) {
			return true;
		}
		return
			contentsModel.reference(((const ZLTextTreeParagraph*)contentsModel[0])) >
			(int)cursor.paragraphCursor().index();
	}
	return false;
}

void GotoPreviousTOCSectionAction::run() {
	Reader &reader = Reader::Instance();
	ContentsView &contentsView = (ContentsView&)*reader.myContentsView;
	std::size_t current = contentsView.currentTextViewParagraph(false);
	const ContentsModel &contentsModel = (const ContentsModel&)*contentsView.textArea().model();

	int reference = contentsModel.reference(((const ZLTextTreeParagraph*)contentsModel[current]));
	const ZLTextWordCursor &cursor = reader.bookTextView().textArea().startCursor();
	if (!cursor.isNull() &&
			(cursor.elementIndex() == 0)) {
		int paragraphIndex = cursor.paragraphCursor().index();
		if (reference == paragraphIndex) {
			reference = contentsModel.reference(((const ZLTextTreeParagraph*)contentsModel[current - 1]));
		} else if (reference == paragraphIndex - 1) {
			const ZLTextModel &textModel = *reader.bookTextView().textArea().model();
			const ZLTextParagraph *para = textModel[paragraphIndex];
			if ((para != 0) && (para->kind() == ZLTextParagraph::END_OF_SECTION_PARAGRAPH)) {
				reference = contentsModel.reference(((const ZLTextTreeParagraph*)contentsModel[current - 1]));
			}
		}
	}
	if (reference != -1) {
		((ZLTextView&)*reader.myBookTextView).gotoParagraph(reference);
		reader.refreshWindow();
	}
}

GotoPageNumberAction::GotoPageNumberAction(const std::string &parameter) : ModeDependentAction(Reader::BOOK_TEXT_MODE), myParameter(parameter) {
}

bool GotoPageNumberAction::isVisible() const {
	return
		ModeDependentAction::isVisible() &&
		!Reader::Instance().bookTextView().hasMultiSectionModel();
}

bool GotoPageNumberAction::isEnabled() const {
	return ModeDependentAction::isEnabled() && (Reader::Instance().bookTextView().pageNumber() > 1);
}

void GotoPageNumberAction::run() {
	Reader &reader = Reader::Instance();
	int pageIndex = 0;
	const int pageNumber = reader.bookTextView().pageNumber();

	if (!myParameter.empty()) {
		const std::string value = reader.visualParameter(myParameter);
		if (value.empty()) {
			return;
		}
		pageIndex = std::atoi(value.c_str());
	} else {
		shared_ptr<ZLDialog> gotoPageDialog = ZLDialogManager::Instance().createDialog(ZLResourceKey("gotoPageDialog"));

		ZLIntegerRangeOption pageIndexOption(ZLCategoryKey::CONFIG, "gotoPageDialog", "Index", 1, pageNumber, pageIndex);
		gotoPageDialog->addOption(ZLResourceKey("pageNumber"), new ZLSimpleSpinOptionEntry(pageIndexOption, 1));
		gotoPageDialog->addButton(ZLDialogManager::OK_BUTTON, true);
		gotoPageDialog->addButton(ZLDialogManager::CANCEL_BUTTON, false);
		if (gotoPageDialog->run()) {
			gotoPageDialog->acceptValues();
			pageIndex = pageIndexOption.value();
		} else {
			return;
		}
	}

	reader.bookTextView().gotoPage(std::max(1, std::min(pageIndex, pageNumber)));
	reader.refreshWindow();
}

bool SelectionAction::isVisible() const {
	shared_ptr<ZLView> view = Reader::Instance().currentView();
	return !view.isNull() && view->isInstanceOf(ZLTextView::TYPE_ID);
}

bool SelectionAction::isEnabled() const {
	if (!isVisible()) {
		return false;
	}
	const ZLTextSelectionModel &selectionModel = textView().selectionModel();
	return !selectionModel.text().empty() || !selectionModel.image().isNull();
}

ZLTextView &SelectionAction::textView() const {
	return (ZLTextView&)*Reader::Instance().currentView();
}

bool CopySelectedTextAction::isVisible() const {
	return SelectionAction::isVisible() && ZLDialogManager::Instance().isClipboardSupported(ZLDialogManager::CLIPBOARD_MAIN);
}

void CopySelectedTextAction::run() {
	textView().selectionModel().copySelectionToClipboard(ZLDialogManager::CLIPBOARD_MAIN);
}

bool OpenSelectedTextInDictionaryAction::isVisible() const {
	return SelectionAction::isVisible() && Reader::Instance().isDictionarySupported();
}

void OpenSelectedTextInDictionaryAction::run() {
	Reader::Instance().openInDictionary(textView().selectionModel().text());
}

void ClearSelectionAction::run() {
	textView().selectionModel().clear();
	Reader::Instance().refreshWindow();
}

void FBFullscreenAction::run() {
	Reader &reader = Reader::Instance();
	if (!reader.isFullscreen()) {
		reader.myActionOnCancel = Reader::UNFULLSCREEN;
	}
	FullscreenAction::run();
}

FilterLibraryAction::FilterLibraryAction() : ModeDependentAction(Reader::LIBRARY_MODE) {
}

void FilterLibraryAction::run() {
}
