/*
 * Copyright (C) 2020 Georg Zotti
 *
 * 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, Suite 500, Boston, MA  02110-1335, USA.
 */

#include <stdexcept>
#include <QDebug>
#include <QStyleOptionGraphicsItem>
#include <QPainter>
#include <QFont>
#include "StelGuiItems.hpp"
#include "StelApp.hpp"
#include "StelCore.hpp"
#include "StelGui.hpp"
#include "SkyGui.hpp"
#include "StelModuleMgr.hpp"
#include "StelTranslator.hpp"
#include "StelScriptMgr.hpp"
#include "StelFileMgr.hpp"
#include "Calendars.hpp"
#include "CalendarsDialog.hpp"

#include "JulianCalendar.hpp"
#include "RevisedJulianCalendar.hpp"
#include "GregorianCalendar.hpp"
#include "ISOCalendar.hpp"
#include "IcelandicCalendar.hpp"
#include "RomanCalendar.hpp"
#include "ByzantineCalendar.hpp"
#include "OlympicCalendar.hpp"
#include "EgyptianCalendar.hpp"
#include "ArmenianCalendar.hpp"
#include "ZoroastrianCalendar.hpp"
#include "CopticCalendar.hpp"
#include "EthiopicCalendar.hpp"
#include "IslamicCalendar.hpp"
#include "HebrewCalendar.hpp"
#include "OldHinduSolarCalendar.hpp"
#include "OldHinduLuniSolarCalendar.hpp"
#include "NewHinduCalendar.hpp"
#include "NewHinduLunarCalendar.hpp"
#include "AstroHinduSolarCalendar.hpp"
#include "AstroHinduLunarCalendar.hpp"
#include "TibetanCalendar.hpp"
#include "MayaLongCountCalendar.hpp"
#include "MayaHaabCalendar.hpp"
#include "MayaTzolkinCalendar.hpp"
#include "AztecXihuitlCalendar.hpp"
#include "AztecTonalpohualliCalendar.hpp"
#include "BalinesePawukonCalendar.hpp"
#include "FrenchAstronomicalCalendar.hpp"
#include "FrenchArithmeticCalendar.hpp"
#include "PersianArithmeticCalendar.hpp"
#include "PersianAstronomicalCalendar.hpp"
#include "BahaiArithmeticCalendar.hpp"
#include "BahaiAstronomicalCalendar.hpp"
#include "ChineseCalendar.hpp"
#include "JapaneseCalendar.hpp"
#include "KoreanCalendar.hpp"
#include "VietnameseCalendar.hpp"

/*************************************************************************
 This method is the one called automatically by the StelModuleMgr just 
 after loading the dynamic library
*************************************************************************/
StelModule* CalendarsStelPluginInterface::getStelModule() const
{
	return new Calendars();
}

StelPluginInfo CalendarsStelPluginInterface::getPluginInfo() const
{
	// Allow to load the resources when used as a static plugin
	Q_INIT_RESOURCE(Calendars);

	StelPluginInfo info;
	info.id = "Calendars";
	info.displayedName = N_("Calendars");
	info.authors = "Georg Zotti";
	info.contact = STELLARIUM_DEV_URL;
	info.description = N_("Calendars of the world");
	info.version = CALENDARS_PLUGIN_VERSION;
	info.license = CALENDARS_PLUGIN_LICENSE;
	return info;
}

/*************************************************************************
 Constructor
*************************************************************************/
Calendars::Calendars():
	infoPanel(nullptr),
#ifndef NO_GUI
	toolbarButton(nullptr),
#endif
	enabled(true),
	flagTextColorOverride(false),
	textColor(0.75f),
	flagShowJulian(true),
	flagShowRevisedJulian(false),
	flagShowGregorian(true),
	flagShowISO(true),
	flagShowIcelandic(false),
	flagShowRoman(false),
	flagShowByzantine(false),
	flagShowOlympic(false),
	flagShowEgyptian(false),
	flagShowArmenian(false),
	flagShowZoroastrian(false),
	flagShowCoptic(false),
	flagShowEthiopic(false),
	flagShowChinese(true),
	flagShowJapanese(true),
	flagShowKorean(true),
	flagShowVietnamese(true),
	flagShowIslamic(true),
	flagShowHebrew(true),
	flagShowOldHinduSolar(false),
	flagShowOldHinduLunar(false),
	flagShowNewHinduSolar(true),
	flagShowNewHinduLunar(true),
	flagShowAstroHinduSolar(false),
	flagShowAstroHinduLunar(false),
	flagShowMayaLongCount(true),
	flagShowMayaHaab(false),
	flagShowMayaTzolkin(false),
	flagShowAztecXihuitl(false),
	flagShowAztecTonalpohualli(false),
	flagShowBalinese(false),
	flagShowFrenchAstronomical(false),
	flagShowFrenchArithmetic(false),
	flagShowPersianArithmetic(false),
	flagShowPersianAstronomical(false),
	flagShowBahaiArithmetic(false),
	flagShowBahaiAstronomical(false),
	flagShowTibetan(false)
{
	setObjectName("Calendars");
	int fontSize = StelApp::getInstance().getScreenFontSize();

	conf = StelApp::getInstance().getSettings();
#ifndef NO_GUI
	configDialog = new CalendarsDialog();
	infoPanel=new CalendarsInfoPanel(this, static_cast<StelGui*>(StelApp::getInstance().getGui())->getSkyGui());
#endif

	QFont pFont=QGuiApplication::font();
	pFont.setPixelSize(fontSize);
	if (infoPanel)
	{
		infoPanel->setFont(pFont);
		connect(&StelApp::getInstance(), &StelApp::fontChanged, this, [=](const QFont &font){
			QFont pFont=font;
			pFont.setPixelSize(fontSize);
			infoPanel->setFont(pFont);
			infoPanel->updatePosition(true);});
		connect(&StelApp::getInstance(), &StelApp::screenFontSizeChanged, this, [=](int size){
			QFont f=infoPanel->font();
			f.setPixelSize(size);
			infoPanel->setFont(f);
			infoPanel->updatePosition(true);});
	}
}

/*************************************************************************
 Destructor
*************************************************************************/
Calendars::~Calendars()
{
#ifndef NO_GUI
	delete configDialog; configDialog=nullptr;
#endif
	QMutableMapIterator<QString, Calendar*> i(calendars);
	while (i.hasNext())
	{
		i.next();
		Calendar *cal = calendars.take(i.key());
		delete cal;
	}
	if (infoPanel) delete infoPanel;
}

bool Calendars::configureGui(bool show)
{
#ifdef NO_GUI
	return false;
#else
	if (show)
		configDialog->setVisible(true);
	return true;
#endif
}
/*************************************************************************
 Reimplementation of the getCallOrder method
*************************************************************************/
double Calendars::getCallOrder(StelModuleActionName actionName) const
{
	if (actionName==StelModule::ActionDraw)
		return StelApp::getInstance().getModuleMgr().getModule("LandscapeMgr")->getCallOrder(actionName)+10.;
	return 0;
}


/*************************************************************************
 Init our module
*************************************************************************/
void Calendars::init()
{
	loadSettings();
	// Create action for enable/disable & hook up signals
	QString section=N_("Calendars");
	addAction("actionShow_Calendars",         section, N_("Calendars"), "enabled", "Alt+K");
#ifndef NO_GUI
	addAction("actionShow_Calendars_dialog",  section, N_("Show settings dialog"),  configDialog,  "visible",           "Alt+Shift+K");
#endif
	// No hotkey here, but users can define their own
	addAction("actionShow_Calendars_export",  section, N_("Export to Calendars.html"),  this,  "exportCalendars()");

#ifndef NO_GUI
	// Add a toolbar button
	StelApp& app=StelApp::getInstance();
	try
	{
		StelGui* gui = dynamic_cast<StelGui*>(app.getGui());
		if (gui)
		{
			//qDebug() << "button...";
			toolbarButton = new StelButton(nullptr,
						       QPixmap(":/Calendars/bt_Calendars_On.png"),
						       QPixmap(":/Calendars/bt_Calendars_Off.png"),
						       QPixmap(":/graphicGui/miscGlow32x32.png"),
						       "actionShow_Calendars",
						       false,
						       "actionShow_Calendars_dialog");
			//qDebug() << "add button...";
			gui->getButtonBar()->addButton(toolbarButton, "065-pluginsGroup");
		}
	}
	catch (std::runtime_error& e)
	{
		qWarning() << "Unable to create toolbar button for Calendars plugin: " << e.what();
	}

	infoPanel->setPos(600, 300);
#endif
	const double jd=StelApp::getInstance().getCore()->getJD();
	calendars.insert("Calendar", new Calendar(jd)); // For scripting use only.
	calendars.insert("Julian", new JulianCalendar(jd));
	calendars.insert("RevisedJulian", new RevisedJulianCalendar(jd));
	calendars.insert("Gregorian", new GregorianCalendar(jd));
	calendars.insert("ISO", new ISOCalendar(jd));
	calendars.insert("Icelandic", new IcelandicCalendar(jd));
	calendars.insert("Roman", new RomanCalendar(jd));
	calendars.insert("Byzantine", new ByzantineCalendar(jd));
	calendars.insert("Olympic", new OlympicCalendar(jd));
	calendars.insert("Egyptian", new EgyptianCalendar(jd));
	calendars.insert("Armenian", new ArmenianCalendar(jd));
	calendars.insert("Zoroastrian", new ZoroastrianCalendar(jd));
	calendars.insert("Coptic", new CopticCalendar(jd));
	calendars.insert("Ethiopic", new EthiopicCalendar(jd));
	calendars.insert("Islamic", new IslamicCalendar(jd));
	calendars.insert("Hebrew", new HebrewCalendar(jd));
	calendars.insert("OldHinduSolar", new OldHinduSolarCalendar(jd));
	calendars.insert("OldHinduLunar", new OldHinduLuniSolarCalendar(jd));
	calendars.insert("NewHinduSolar", new NewHinduCalendar(jd));
	calendars.insert("NewHinduLunar", new NewHinduLunarCalendar(jd));
	calendars.insert("AstroHinduSolar", new AstroHinduSolarCalendar(jd));
	calendars.insert("AstroHinduLunar", new AstroHinduLunarCalendar(jd));
	calendars.insert("MayaLongCount", new MayaLongCountCalendar(jd));
	calendars.insert("MayaHaab", new MayaHaabCalendar(jd));
	calendars.insert("MayaTzolkin", new MayaTzolkinCalendar(jd));
	calendars.insert("AztecXihuitl", new AztecXihuitlCalendar(jd));
	calendars.insert("AztecTonalpohualli", new AztecTonalpohualliCalendar(jd));
	calendars.insert("Balinese", new BalinesePawukonCalendar(jd));
	calendars.insert("FrenchAstronomical", new FrenchAstronomicalCalendar(jd));
	calendars.insert("FrenchArithmetic", new FrenchArithmeticCalendar(jd));
	calendars.insert("PersianArithmetic", new PersianArithmeticCalendar(jd));
	calendars.insert("PersianAstronomical", new PersianAstronomicalCalendar(jd));
	calendars.insert("BahaiArithmetic", new BahaiArithmeticCalendar(jd));
	calendars.insert("BahaiAstronomical", new BahaiAstronomicalCalendar(jd));
	calendars.insert("Tibetan", new TibetanCalendar(jd));
	calendars.insert("Chinese", new ChineseCalendar(jd));
	calendars.insert("Japanese", new JapaneseCalendar(jd));
	calendars.insert("Korean", new KoreanCalendar(jd));
	calendars.insert("Vietnamese", new VietnameseCalendar(jd));
	// TODO: Add your Calendar subclasses here.

	foreach (Calendar* cal, calendars)
	{
#ifndef NDEBUG
		// We must set JD explicitly to enforce creation of parts for a debug mode test in StelMainView::init().
		cal->setJD(jd);
#endif
		connect(cal, SIGNAL(jdChanged(double)), StelApp::getInstance().getCore(), SLOT(setJD(double)));
		connect(&StelApp::getInstance(), SIGNAL(languageChanged()), cal, SLOT(retranslate()));
	}
}

#ifdef ENABLE_SCRIPTING
// Add calendar as scriptable object! Some scripting functions won't work though, as they use object types unknown to the scripting engine.
void Calendars::makeCalendarsScriptable(StelScriptMgr *ssm)
{
	foreach (Calendar* cal, calendars)
	{
		cal->setObjectName(cal->metaObject()->className());
		ssm->addObject(cal);
	}
}
#endif

void Calendars::loadSettings()
{
	QString defaultColor =  conf->value("color/default_color", "0.5,0.5,0.7").toString();
	// Now activate calendar displays if needed.
	enable(                 conf->value("Calendars/show", true).toBool());
	setFlagTextColorOverride(conf->value("Calendars/flag_text_color_override", false).toBool());
	setTextColor(           Vec3f(conf->value("Calendars/text_color", defaultColor).toString()));
	showJulian(             conf->value("Calendars/show_julian", true).toBool());
	showRevisedJulian(      conf->value("Calendars/show_revised_julian", false).toBool());
	showGregorian(          conf->value("Calendars/show_gregorian", true).toBool());
	showISO(                conf->value("Calendars/show_iso", true).toBool());
	showIcelandic(          conf->value("Calendars/show_icelandic", false).toBool());
	showRoman(              conf->value("Calendars/show_roman", false).toBool());
	showByzantine(          conf->value("Calendars/show_byzantine", false).toBool());
	showOlympic(            conf->value("Calendars/show_olympic", false).toBool());
	showEgyptian(           conf->value("Calendars/show_egyptian", false).toBool());
	showArmenian(           conf->value("Calendars/show_armenian", false).toBool());
	showZoroastrian(        conf->value("Calendars/show_zoroastrian", false).toBool());
	showCoptic(	        conf->value("Calendars/show_coptic", false).toBool());
	showEthiopic(	        conf->value("Calendars/show_ethiopic", false).toBool());
	showChinese(            conf->value("Calendars/show_chinese", false).toBool());
	showJapanese(           conf->value("Calendars/show_japanese", false).toBool());
	showKorean(             conf->value("Calendars/show_korean", false).toBool());
	showVietnamese(         conf->value("Calendars/show_vietnamese", false).toBool());
	showIslamic(            conf->value("Calendars/show_islamic", true).toBool());
	showHebrew(             conf->value("Calendars/show_hebrew", true).toBool());
	showOldHinduSolar(      conf->value("Calendars/show_old_hindu_solar", false).toBool());
	showOldHinduLunar(      conf->value("Calendars/show_old_hindu_lunar", false).toBool());
	showNewHinduSolar(      conf->value("Calendars/show_new_hindu_solar", true).toBool());
	showNewHinduLunar(      conf->value("Calendars/show_new_hindu_lunar", true).toBool());
	showAstroHinduSolar(    conf->value("Calendars/show_astro_hindu_solar", false).toBool());
	showAstroHinduLunar(    conf->value("Calendars/show_astro_hindu_lunar", false).toBool());
	showMayaLongCount(      conf->value("Calendars/show_maya_long_count", true).toBool());
	showMayaHaab(           conf->value("Calendars/show_maya_haab", false).toBool());
	showMayaTzolkin(        conf->value("Calendars/show_maya_tzolkin", false).toBool());
	showAztecXihuitl(       conf->value("Calendars/show_aztec_xihuitl", false).toBool());
	showAztecTonalpohualli( conf->value("Calendars/show_aztec_tonalpohualli", false).toBool());
	showBalinese(           conf->value("Calendars/show_balinese_pawukon", false).toBool());
	showFrenchAstronomical( conf->value("Calendars/show_french_astronomical", false).toBool());
	showFrenchArithmetic(   conf->value("Calendars/show_french_arithmetic", false).toBool());
	showPersianArithmetic(  conf->value("Calendars/show_persian_arithmetic", false).toBool());
	showPersianAstronomical(conf->value("Calendars/show_persian_astronomical", false).toBool());
	showBahaiArithmetic(    conf->value("Calendars/show_bahai_arithmetic", false).toBool());
	showBahaiAstronomical(  conf->value("Calendars/show_bahai_astronomical", false).toBool());
	showTibetan(            conf->value("Calendars/show_tibetan", false).toBool());
}

void Calendars::restoreDefaultSettings()
{
	Q_ASSERT(conf);
	// Remove the old values...
	conf->remove("Calendars");
	// ...load the default values...
	loadSettings();
}

/*********************************************************************************
 Draw our module. This tabulates the activated calendars in the lower right corner
**********************************************************************************/
void Calendars::draw(StelCore* core)
{
	if (!infoPanel) return;
	if (!enabled)
	{
		infoPanel->hide();
		return;
	}
	if (calendars.count()==0) return;

	QString str = createTableToDraw();
	Vec3f color(1);
	if (getFlagTextColorOverride())
	{
		color=textColor;
	}
	else
	{
		if (StelApp::getInstance().getFlagOverwriteInfoColor())
		{
			// make info text more readable...
			color = StelApp::getInstance().getOverwriteInfoColor();
		}
		if (core->isBrightDaylight() && !StelApp::getInstance().getVisionModeNight())
		{
			// make info text more readable when atmosphere enabled at daylight.
			color = StelApp::getInstance().getDaylightInfoColor();
		}
	}

	infoPanel->setDefaultTextColor(color.toQColor());
	infoPanel->setHtml(str);
	infoPanel->updatePosition();
	infoPanel->show();
}

QString Calendars::createTableToDraw()
{
	QString str;
	QTextStream oss(&str);
	oss << "<table>";
	// Select the drawable calendars from GUI or settings.
	if (flagShowGregorian)          oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Gregorian",             "calendar"), getCal("Gregorian")->getFormattedDateString());
	if (flagShowJulian)             oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Julian",                "calendar"), getCal("Julian")->getFormattedDateString());
	if (flagShowRevisedJulian)      oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Revised Julian",        "calendar"), getCal("RevisedJulian")->getFormattedDateString());
	if (flagShowISO)                oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("ISO week",              "calendar"), getCal("ISO")->getFormattedDateString());
	if (flagShowIcelandic)          oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Icelandic",             "calendar"), getCal("Icelandic")->getFormattedDateString());
	if (flagShowRoman)              oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Roman",                 "calendar"), getCal("Roman")->getFormattedDateString());
	if (flagShowByzantine)          oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Byzantine",             "calendar"), getCal("Byzantine")->getFormattedDateString());
	if (flagShowOlympic)            oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Olympic",               "calendar"), getCal("Olympic")->getFormattedDateString());
	if (flagShowEgyptian)           oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Egyptian",              "calendar"), getCal("Egyptian")->getFormattedDateString());
	if (flagShowArmenian)           oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Armenian",              "calendar"), getCal("Armenian")->getFormattedDateString());
	if (flagShowZoroastrian)        oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Zoroastrian",           "calendar"), getCal("Zoroastrian")->getFormattedDateString());
	if (flagShowCoptic)             oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Coptic",                "calendar"), getCal("Coptic")->getFormattedDateString());
	if (flagShowEthiopic)           oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Ethiopic",              "calendar"), getCal("Ethiopic")->getFormattedDateString());
	if (flagShowFrenchAstronomical) oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("French Rev. (Astron.)", "calendar"), getCal("FrenchAstronomical")->getFormattedDateString());
	if (flagShowFrenchArithmetic)   oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("French Rev. (Arithm.)", "calendar"), getCal("FrenchArithmetic")->getFormattedDateString());
	if (flagShowIslamic)            oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Islamic",               "calendar"), getCal("Islamic")->getFormattedDateString());
	if (flagShowHebrew)             oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Hebrew",                "calendar"), getCal("Hebrew")->getFormattedDateString());
	if (flagShowPersianArithmetic)  oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Persian (Arithm.)",     "calendar"), getCal("PersianArithmetic")->getFormattedDateString());
	if (flagShowPersianAstronomical)oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Persian (Astron.)",     "calendar"), getCal("PersianAstronomical")->getFormattedDateString());
	if (flagShowBahaiArithmetic)    oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Bahá’í (Arithm.)",      "calendar"), getCal("BahaiArithmetic")->getFormattedDateString());
	if (flagShowBahaiAstronomical)  oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Bahá’í (Astron.)",      "calendar"), getCal("BahaiAstronomical")->getFormattedDateString());
	if (flagShowOldHinduSolar)      oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Old Hindu Solar",       "calendar"), getCal("OldHinduSolar")->getFormattedDateString());
	if (flagShowOldHinduLunar)      oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Old Hindu Lunisolar",   "calendar"), getCal("OldHinduLunar")->getFormattedDateString());
	if (flagShowNewHinduSolar)      oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("New Hindu Solar",       "calendar"), getCal("NewHinduSolar")->getFormattedDateString());
	if (flagShowNewHinduLunar)      oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("New Hindu Lunisolar",   "calendar"), getCal("NewHinduLunar")->getFormattedDateString());
	if (flagShowNewHinduLunar)      oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("New Hindu Panchang",    "calendar"),
												    static_cast<NewHinduLunarCalendar*>(getCal("NewHinduLunar"))->getFormattedPanchangString());
	if (flagShowAstroHinduSolar)    oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Hindu Astro Solar",     "calendar"), getCal("AstroHinduSolar")->getFormattedDateString());
	if (flagShowAstroHinduLunar)    oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Hindu Astro Lunisolar", "calendar"), getCal("AstroHinduLunar")->getFormattedDateString());
	if (flagShowTibetan)            oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Tibetan",               "calendar"), getCal("Tibetan")->getFormattedDateString());
	if (flagShowChinese) {
					oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Chinese",               "calendar"), getCal("Chinese")->getFormattedDateString());
					oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Chinese Solar Terms",   "calendar"), static_cast<ChineseCalendar*>(getCal("Chinese"))->getFormattedSolarTermsString());
	}
	if (flagShowJapanese) {
					oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Japanese",              "calendar"), getCal("Japanese")->getFormattedDateString());
					oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Japanese Solar Terms",  "calendar"), static_cast<JapaneseCalendar*>(getCal("Japanese"))->getFormattedSolarTermsString());
	}
	if (flagShowKorean) {
					oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Korean",                "calendar"), getCal("Korean")->getFormattedDateString());
					oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Korean Solar Terms",    "calendar"), static_cast<KoreanCalendar*>(getCal("Korean"))->getFormattedSolarTermsString());
	}
	if (flagShowVietnamese) {
					oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Vietnamese",            "calendar"), getCal("Vietnamese")->getFormattedDateString());
					oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Vietnamese Solar Terms","calendar"), static_cast<VietnameseCalendar*>(getCal("Vietnamese"))->getFormattedSolarTermsString());
	}
	if (flagShowBalinese)           oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Balinese Pawukon",      "calendar"), getCal("Balinese")->getFormattedDateString());
	if (flagShowMayaLongCount)      oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Maya Long Count",       "calendar"), getCal("MayaLongCount")->getFormattedDateString());
	if (flagShowMayaHaab)           oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Maya Haab",             "calendar"), getCal("MayaHaab")->getFormattedDateString());
	if (flagShowMayaTzolkin)        oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Maya Tzolkin",          "calendar"), getCal("MayaTzolkin")->getFormattedDateString());
	if (flagShowAztecXihuitl)       oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Aztec Xihuitl",         "calendar"), getCal("AztecXihuitl")->getFormattedDateString());
	if (flagShowAztecTonalpohualli) oss << QString("<tr><td>%1&nbsp;</td><td>%2</td></tr>").arg(qc_("Aztec Tonalpohualli",   "calendar"), getCal("AztecTonalpohualli")->getFormattedDateString());
	oss << "</table>";
	return str;
}

void Calendars::exportCalendars()
{
	QString html=createTableToDraw();
	QString fileName=StelFileMgr::getUserDir() + "/Calendar.html";
	QFile file(fileName);
	if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text | QIODevice::Unbuffered))
	{
		qWarning() << "ERROR: Cannot open file" << fileName;
		return;
	}
	QTextStream out(&file);
	out << (html);
	file.close();
}

// Get a pointer to the respective calendar
Calendar* Calendars::getCal(const QString &name)
{
	return calendars.value(name, nullptr);
}

void Calendars::update(double)
{
	if (!enabled
#ifndef NO_GUI
			&& (configDialog && !configDialog->visible())
#endif
			) // allow playing in dialog while main display is off! (#4917)
		return;
	const double jd=StelApp::getInstance().getCore()->getJD();
	foreach (Calendar* cal, calendars)
	{
		cal->setJD(jd);
	}
}

/*****************************************************************************
 * Boilerplate: Setters/getters.
 * */

bool Calendars::isEnabled() const {return enabled;}
void Calendars::enable(bool b)
{
	if (b!=enabled)
	{
		enabled=b;
		conf->setValue("Calendars/show", b);
		emit enabledChanged(b);
	}
}
bool Calendars::isJulianDisplayed() const { return flagShowJulian;}
void Calendars::showJulian(bool b)
{
	if (b!=flagShowJulian)
	{
		flagShowJulian=b;
		conf->setValue("Calendars/show_julian", b);
		emit showJulianChanged(b);
	}
}
bool Calendars::isRevisedJulianDisplayed() const { return flagShowRevisedJulian;}
void Calendars::showRevisedJulian(bool b)
{
	if (b!=flagShowRevisedJulian)
	{
		flagShowRevisedJulian=b;
		conf->setValue("Calendars/show_revised_julian", b);
		emit showJulianChanged(b);
	}
}
bool Calendars::isGregorianDisplayed() const { return flagShowGregorian;}
void Calendars::showGregorian(bool b)
{
	if (b!=flagShowGregorian)
	{
		flagShowGregorian=b;
		conf->setValue("Calendars/show_gregorian", b);
		emit showGregorianChanged(b);
	}
}
bool Calendars::isISODisplayed() const { return flagShowISO;}
void Calendars::showISO(bool b)
{
	if (b!=flagShowISO)
	{
		flagShowISO=b;
		conf->setValue("Calendars/show_iso", b);
		emit showISOChanged(b);
	}
}

bool Calendars::isIcelandicDisplayed() const { return flagShowIcelandic;}
void Calendars::showIcelandic(bool b)
{
	if (b!=flagShowIcelandic)
	{
		flagShowIcelandic=b;
		conf->setValue("Calendars/show_icelandic", b);
		emit showIcelandicChanged(b);
	}
}

bool Calendars::isRomanDisplayed() const { return flagShowRoman;}
void Calendars::showRoman(bool b)
{
	if (b!=flagShowRoman)
	{
		flagShowRoman=b;
		conf->setValue("Calendars/show_roman", b);
		emit showRomanChanged(b);
	}
}

bool Calendars::isByzantineDisplayed() const { return flagShowByzantine;}
void Calendars::showByzantine(bool b)
{
	if (b!=flagShowByzantine)
	{
		flagShowByzantine=b;
		conf->setValue("Calendars/show_byzantine", b);
		emit showByzantineChanged(b);
	}
}

bool Calendars::isOlympicDisplayed() const { return flagShowOlympic;}
void Calendars::showOlympic(bool b)
{
	if (b!=flagShowOlympic)
	{
		flagShowOlympic=b;
		conf->setValue("Calendars/show_olympic", b);
		emit showOlympicChanged(b);
	}
}

bool Calendars::isEgyptianDisplayed() const { return flagShowEgyptian;}
void Calendars::showEgyptian(bool b)
{
	if (b!=flagShowEgyptian)
	{
		flagShowEgyptian=b;
		conf->setValue("Calendars/show_egyptian", b);
		emit showEgyptianChanged(b);
	}
}

bool Calendars::isArmenianDisplayed() const { return flagShowArmenian;}
void Calendars::showArmenian(bool b)
{
	if (b!=flagShowArmenian)
	{
		flagShowArmenian=b;
		conf->setValue("Calendars/show_armenian", b);
		emit showArmenianChanged(b);
	}
}

bool Calendars::isZoroastrianDisplayed() const { return flagShowZoroastrian;}
void Calendars::showZoroastrian(bool b)
{
	if (b!=flagShowZoroastrian)
	{
		flagShowZoroastrian=b;
		conf->setValue("Calendars/show_zoroastrian", b);
		emit showZoroastrianChanged(b);
	}
}

bool Calendars::isCopticDisplayed() const { return flagShowCoptic;}
void Calendars::showCoptic(bool b)
{
	if (b!=flagShowCoptic)
	{
		flagShowCoptic=b;
		conf->setValue("Calendars/show_coptic", b);
		emit showCopticChanged(b);
	}
}

bool Calendars::isEthiopicDisplayed() const { return flagShowEthiopic;}
void Calendars::showEthiopic(bool b)
{
	if (b!=flagShowEthiopic)
	{
		flagShowEthiopic=b;
		conf->setValue("Calendars/show_ethiopic", b);
		emit showEthiopicChanged(b);
	}
}

bool Calendars::isChineseDisplayed() const { return flagShowChinese;}
void Calendars::showChinese(bool b)
{
	if (b!=flagShowChinese)
	{
		flagShowChinese=b;
		conf->setValue("Calendars/show_chinese", b);
		emit showChineseChanged(b);
	}
}

bool Calendars::isJapaneseDisplayed() const { return flagShowJapanese;}
void Calendars::showJapanese(bool b)
{
	if (b!=flagShowJapanese)
	{
		flagShowJapanese=b;
		conf->setValue("Calendars/show_japanese", b);
		emit showJapaneseChanged(b);
	}
}

bool Calendars::isKoreanDisplayed() const { return flagShowKorean;}
void Calendars::showKorean(bool b)
{
	if (b!=flagShowKorean)
	{
		flagShowKorean=b;
		conf->setValue("Calendars/show_korean", b);
		emit showKoreanChanged(b);
	}
}

bool Calendars::isVietnameseDisplayed() const { return flagShowVietnamese;}
void Calendars::showVietnamese(bool b)
{
	if (b!=flagShowVietnamese)
	{
		flagShowVietnamese=b;
		conf->setValue("Calendars/show_vietnamese", b);
		emit showVietnameseChanged(b);
	}
}

bool Calendars::isIslamicDisplayed() const { return flagShowIslamic;}
void Calendars::showIslamic(bool b)
{
	if (b!=flagShowIslamic)
	{
		flagShowIslamic=b;
		conf->setValue("Calendars/show_islamic", b);
		emit showIslamicChanged(b);
	}
}

bool Calendars::isHebrewDisplayed() const { return flagShowHebrew;}
void Calendars::showHebrew(bool b)
{
	if (b!=flagShowHebrew)
	{
		flagShowHebrew=b;
		conf->setValue("Calendars/show_hebrew", b);
		emit showHebrewChanged(b);
	}
}

bool Calendars::isOldHinduSolarDisplayed() const { return flagShowOldHinduSolar;}
void Calendars::showOldHinduSolar(bool b)
{
	if (b!=flagShowOldHinduSolar)
	{
		flagShowOldHinduSolar=b;
		conf->setValue("Calendars/show_old_hindu_solar", b);
		emit showOldHinduSolarChanged(b);
	}
}

bool Calendars::isOldHinduLunarDisplayed() const { return flagShowOldHinduLunar;}
void Calendars::showOldHinduLunar(bool b)
{
	if (b!=flagShowOldHinduLunar)
	{
		flagShowOldHinduLunar=b;
		conf->setValue("Calendars/show_old_hindu_lunar", b);
		emit showOldHinduLunarChanged(b);
	}
}

bool Calendars::isNewHinduSolarDisplayed() const { return flagShowNewHinduSolar;}
void Calendars::showNewHinduSolar(bool b)
{
	if (b!=flagShowNewHinduSolar)
	{
		flagShowNewHinduSolar=b;
		conf->setValue("Calendars/show_new_hindu_solar", b);
		emit showNewHinduSolarChanged(b);
	}
}

bool Calendars::isNewHinduLunarDisplayed() const { return flagShowNewHinduLunar;}
void Calendars::showNewHinduLunar(bool b)
{
	if (b!=flagShowNewHinduLunar)
	{
		flagShowNewHinduLunar=b;
		conf->setValue("Calendars/show_new_hindu_lunar", b);
		emit showNewHinduLunarChanged(b);
	}
}

bool Calendars::isAstroHinduSolarDisplayed() const { return flagShowAstroHinduSolar;}
void Calendars::showAstroHinduSolar(bool b)
{
	if (b!=flagShowAstroHinduSolar)
	{
		flagShowAstroHinduSolar=b;
		conf->setValue("Calendars/show_astro_hindu_solar", b);
		emit showAstroHinduSolarChanged(b);
	}
}

bool Calendars::isAstroHinduLunarDisplayed() const { return flagShowAstroHinduLunar;}
void Calendars::showAstroHinduLunar(bool b)
{
	if (b!=flagShowAstroHinduLunar)
	{
		flagShowAstroHinduLunar=b;
		conf->setValue("Calendars/show_astro_hindu_lunar", b);
		emit showAstroHinduLunarChanged(b);
	}
}

bool Calendars::isMayaLongCountDisplayed() const { return flagShowMayaLongCount;}
void Calendars::showMayaLongCount(bool b)
{
	if (b!=flagShowMayaLongCount)
	{
		flagShowMayaLongCount=b;
		conf->setValue("Calendars/show_maya_long_count", b);
		emit showMayaLongCountChanged(b);
	}
}

bool Calendars::isMayaHaabDisplayed() const { return flagShowMayaHaab;}
void Calendars::showMayaHaab(bool b)
{
	if (b!=flagShowMayaHaab)
	{
		flagShowMayaHaab=b;
		conf->setValue("Calendars/show_maya_haab", b);
		emit showMayaHaabChanged(b);
	}
}

bool Calendars::isMayaTzolkinDisplayed() const { return flagShowMayaTzolkin;}
void Calendars::showMayaTzolkin(bool b)
{
	if (b!=flagShowMayaTzolkin)
	{
		flagShowMayaTzolkin=b;
		conf->setValue("Calendars/show_maya_tzolkin", b);
		emit showMayaTzolkinChanged(b);
	}
}

bool Calendars::isAztecXihuitlDisplayed() const { return flagShowAztecXihuitl;}
void Calendars::showAztecXihuitl(bool b)
{
	if (b!=flagShowAztecXihuitl)
	{
		flagShowAztecXihuitl=b;
		conf->setValue("Calendars/show_aztec_xihuitl", b);
		emit showAztecXihuitlChanged(b);
	}
}

bool Calendars::isAztecTonalpohualliDisplayed() const { return flagShowAztecTonalpohualli;}
void Calendars::showAztecTonalpohualli(bool b)
{
	if (b!=flagShowAztecTonalpohualli)
	{
		flagShowAztecTonalpohualli=b;
		conf->setValue("Calendars/show_aztec_tonalpohualli", b);
		emit showAztecTonalpohualliChanged(b);
	}
}

bool Calendars::isBalineseDisplayed() const { return flagShowBalinese;}
void Calendars::showBalinese(bool b)
{
	if (b!=flagShowBalinese)
	{
		flagShowBalinese=b;
		conf->setValue("Calendars/show_balinese_pawukon", b);
		emit showBalineseChanged(b);
	}
}

bool Calendars::isFrenchAstronomicalDisplayed() const { return flagShowFrenchAstronomical;}
void Calendars::showFrenchAstronomical(bool b)
{
	if (b!=flagShowFrenchAstronomical)
	{
		flagShowFrenchAstronomical=b;
		conf->setValue("Calendars/show_french_astronomical", b);
		emit showFrenchAstronomicalChanged(b);
	}
}

bool Calendars::isFrenchArithmeticDisplayed() const { return flagShowFrenchArithmetic;}
void Calendars::showFrenchArithmetic(bool b)
{
	if (b!=flagShowFrenchArithmetic)
	{
		flagShowFrenchArithmetic=b;
		conf->setValue("Calendars/show_french_arithmetic", b);
		emit showFrenchArithmeticChanged(b);
	}
}

bool Calendars::isPersianArithmeticDisplayed() const { return flagShowPersianArithmetic;}
void Calendars::showPersianArithmetic(bool b)
{
	if (b!=flagShowPersianArithmetic)
	{
		flagShowPersianArithmetic=b;
		conf->setValue("Calendars/show_persian_arithmetic", b);
		emit showPersianArithmeticChanged(b);
	}
}

bool Calendars::isPersianAstronomicalDisplayed() const { return flagShowPersianAstronomical;}
void Calendars::showPersianAstronomical(bool b)
{
	if (b!=flagShowPersianAstronomical)
	{
		flagShowPersianAstronomical=b;
		conf->setValue("Calendars/show_persian_astronomical", b);
		emit showPersianAstronomicalChanged(b);
	}
}

bool Calendars::isBahaiArithmeticDisplayed() const { return flagShowBahaiArithmetic;}
void Calendars::showBahaiArithmetic(bool b)
{
	if (b!=flagShowBahaiArithmetic)
	{
		flagShowBahaiArithmetic=b;
		conf->setValue("Calendars/show_bahai_arithmetic", b);
		emit showBahaiArithmeticChanged(b);
	}
}

bool Calendars::isBahaiAstronomicalDisplayed() const { return flagShowBahaiAstronomical;}
void Calendars::showBahaiAstronomical(bool b)
{
	if (b!=flagShowBahaiAstronomical)
	{
		flagShowBahaiAstronomical=b;
		conf->setValue("Calendars/show_bahai_astronomical", b);
		emit showBahaiAstronomicalChanged(b);
	}
}

bool Calendars::isTibetanDisplayed() const { return flagShowTibetan;}
void Calendars::showTibetan(bool b)
{
	if (b!=flagShowTibetan)
	{
		flagShowTibetan=b;
		conf->setValue("Calendars/show_tibetan", b);
		emit showTibetanChanged(b);
	}
}

bool Calendars::getFlagTextColorOverride() const { return flagTextColorOverride;}
void Calendars::setFlagTextColorOverride(bool b)
{
	if (b!=flagTextColorOverride)
	{
		flagTextColorOverride=b;
		conf->setValue("Calendars/flag_text_color_override", b);
		emit flagTextColorOverrideChanged(b);
	}
}

Vec3f Calendars::getTextColor() const
{
	return textColor;
}
void Calendars::setTextColor(const Vec3f& newColor)
{
	if(newColor != textColor)
	{
		textColor=newColor;
		emit textColorChanged(textColor);
	}
}
