/***************************************************************************
                          altvstime.cpp  -  description
                             -------------------
    begin                : wed nov 17 08:05:11 CET 2002
    copyright            : (C) 2002-2003 by Pablo de Vicente
    email                : vicente@oan.es
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "altvstime.h"

#include <QVBoxLayout>
#include <QFrame>

#include <KLocalizedString>
#include <QDialog>
#include <QPainter>
#include <QtPrintSupport/QPrinter>
#include <QtPrintSupport/QPrintDialog>

#include "dms.h"
#include "ksalmanac.h"
#include "kstarsdata.h"
#include "kstarsdatetime.h"
#include "ksnumbers.h"
#include "simclock.h"
#include "kssun.h"
#include "dialogs/finddialog.h"
#include "dialogs/locationdialog.h"
#include "geolocation.h"
#include "skyobjects/skypoint.h"
#include "skyobjects/skyobject.h"
#include "skyobjects/starobject.h"

#include <kplotwidget.h>
#include "avtplotwidget.h"
#include "ui_altvstime.h"

AltVsTimeUI::AltVsTimeUI(QWidget *p) : QFrame(p)
{
    setupUi(this);
}

AltVsTime::AltVsTime(QWidget *parent) : QDialog(parent)
{
#ifdef Q_OS_OSX
    setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
#endif

    setWindowTitle(i18n("Altitude vs. Time"));

    setModal(false);

    QVBoxLayout *topLayout = new QVBoxLayout;
    setLayout(topLayout);
    topLayout->setMargin(0);
    avtUI = new AltVsTimeUI(this);

    // Layers for setting up the plot's priority: the current curve should be above the other curves.
    // The Rise/Set/Transit markers should be on top, with highest priority.
    avtUI->View->addLayer("currentCurveLayer", avtUI->View->layer("main"), QCustomPlot::limAbove);
    avtUI->View->addLayer("markersLayer", avtUI->View->layer("currentCurveLayer"), QCustomPlot::limAbove);

    // Set up the Graph Window:
    avtUI->View->setBackground(QBrush(QColor(0, 0, 0)));
    avtUI->View->xAxis->grid()->setVisible(false);
    avtUI->View->yAxis->grid()->setVisible(false);
    QColor axisColor(Qt::white);
    QPen axisPen(axisColor, 1);
    avtUI->View->xAxis->setBasePen(axisPen);
    avtUI->View->xAxis->setTickPen(axisPen);
    avtUI->View->xAxis->setSubTickPen(axisPen);
    avtUI->View->xAxis->setTickLabelColor(axisColor);
    avtUI->View->xAxis->setLabelColor(axisColor);

    avtUI->View->xAxis2->setBasePen(axisPen);
    avtUI->View->xAxis2->setTickPen(axisPen);
    avtUI->View->xAxis2->setSubTickPen(axisPen);
    avtUI->View->xAxis2->setTickLabelColor(axisColor);
    avtUI->View->xAxis2->setLabelColor(axisColor);

    avtUI->View->yAxis->setBasePen(axisPen);
    avtUI->View->yAxis->setTickPen(axisPen);
    avtUI->View->yAxis->setSubTickPen(axisPen);
    avtUI->View->yAxis->setTickLabelColor(axisColor);
    avtUI->View->yAxis->setLabelColor(axisColor);

    avtUI->View->yAxis2->setBasePen(axisPen);
    avtUI->View->yAxis2->setTickPen(axisPen);
    avtUI->View->yAxis2->setSubTickPen(axisPen);
    avtUI->View->yAxis2->setTickLabelColor(axisColor);
    avtUI->View->yAxis2->setLabelColor(axisColor);

    // give the axis some labels:
    avtUI->View->xAxis2->setLabel("Local Sidereal Time");
    avtUI->View->xAxis2->setVisible(true);
    avtUI->View->yAxis2->setVisible(true);
    avtUI->View->yAxis2->setTickLength(0, 0);
    avtUI->View->xAxis->setLabel("Local Time");
    avtUI->View->yAxis->setLabel("Altitude");
    avtUI->View->xAxis->setRange(43200, 129600);
    avtUI->View->xAxis2->setRange(61200, 147600);

    // configure the bottom axis to show time instead of number:
    QSharedPointer<QCPAxisTickerTime> xAxisTimeTicker(new QCPAxisTickerTime);
    xAxisTimeTicker->setTimeFormat("%h:%m");
    xAxisTimeTicker->setTickCount(12);
    xAxisTimeTicker->setTickStepStrategy(QCPAxisTicker::tssReadability);
    xAxisTimeTicker->setTickOrigin(Qt::UTC);
    avtUI->View->xAxis->setTicker(xAxisTimeTicker);

    // configure the top axis to show time instead of number:
    QSharedPointer<QCPAxisTickerTime> xAxis2TimeTicker(new QCPAxisTickerTime);
    xAxis2TimeTicker->setTimeFormat("%h:%m");
    xAxis2TimeTicker->setTickCount(12);
    xAxis2TimeTicker->setTickStepStrategy(QCPAxisTicker::tssReadability);
    xAxis2TimeTicker->setTickOrigin(Qt::UTC);
    avtUI->View->xAxis2->setTicker(xAxis2TimeTicker);

    // set up the Zoom/Pan features for the Top X Axis
    avtUI->View->axisRect()->setRangeDragAxes(avtUI->View->xAxis2, avtUI->View->yAxis);
    avtUI->View->axisRect()->setRangeZoomAxes(avtUI->View->xAxis2, avtUI->View->yAxis);

    // set up the margins, for a nice view
    avtUI->View->axisRect()->setAutoMargins(QCP::msBottom | QCP::msLeft | QCP::msTop);
    avtUI->View->axisRect()->setMargins(QMargins(0, 0, 7, 0));

    // set up the interaction set:
    avtUI->View->setInteraction(QCP::iRangeZoom, true);
    avtUI->View->setInteraction(QCP::iRangeDrag, true);

    // set up the selection tolerance for checking if a certain graph is or not selected:
    avtUI->View->setSelectionTolerance(5);

    // draw the gradient:
    drawGradient();

    // set up the background image:
    background = new QCPItemPixmap(avtUI->View);
    background->setPixmap(*gradient);
    background->topLeft->setType(QCPItemPosition::ptPlotCoords);
    background->bottomRight->setType(QCPItemPosition::ptPlotCoords);
    background->setScaled(true, Qt::IgnoreAspectRatio);
    background->setLayer("background");
    background->setVisible(true);

    avtUI->raBox->setDegType(false);
    avtUI->decBox->setDegType(true);

    //FIXME:
    //Doesn't make sense to manually adjust long/lat unless we can modify TZ also
    avtUI->longBox->setReadOnly(true);
    avtUI->latBox->setReadOnly(true);

    topLayout->addWidget(avtUI);

    QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
    topLayout->addWidget(buttonBox);
    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));

    QPushButton *printB = new QPushButton(
        QIcon::fromTheme("document-print", QIcon(":/icons/breeze/default/document-print.svg")), i18n("&Print..."));
    printB->setToolTip(i18n("Print the Altitude vs. time plot"));
    buttonBox->addButton(printB, QDialogButtonBox::ActionRole);
    connect(printB, SIGNAL(clicked()), this, SLOT(slotPrint()));

    geo = KStarsData::Instance()->geo();

    DayOffset = 0;
    // set up the initial minimum and maximum altitude
    minAlt = 0;
    maxAlt = 0;
    showCurrentDate();
    if (getDate().time().hour() > 12)
        DayOffset = 1;

    avtUI->longBox->show(geo->lng());
    avtUI->latBox->show(geo->lat());

    computeSunRiseSetTimes();
    setLSTLimits();
    setDawnDusk();

    connect(avtUI->View->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(onYRangeChanged(QCPRange)));
    connect(avtUI->View->xAxis2, SIGNAL(rangeChanged(QCPRange)), this, SLOT(onXRangeChanged(QCPRange)));
    connect(avtUI->View, SIGNAL(plottableClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)), this,
            SLOT(plotMousePress(QCPAbstractPlottable *, int, QMouseEvent *)));
    connect(avtUI->View, SIGNAL(mouseMove(QMouseEvent *)), this, SLOT(mouseOverLine(QMouseEvent *)));

    connect(avtUI->browseButton, SIGNAL(clicked()), this, SLOT(slotBrowseObject()));
    connect(avtUI->cityButton, SIGNAL(clicked()), this, SLOT(slotChooseCity()));
    connect(avtUI->updateButton, SIGNAL(clicked()), this, SLOT(slotUpdateDateLoc()));
    connect(avtUI->clearButton, SIGNAL(clicked()), this, SLOT(slotClear()));
    connect(avtUI->addButton, SIGNAL(clicked()), this, SLOT(slotAddSource()));
    connect(avtUI->nameBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource()));
    connect(avtUI->raBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource()));
    connect(avtUI->decBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource()));
    connect(avtUI->clearFieldsButton, SIGNAL(clicked()), this, SLOT(slotClearBoxes()));
    connect(avtUI->longBox, SIGNAL(returnPressed()), this, SLOT(slotAdvanceFocus()));
    connect(avtUI->latBox, SIGNAL(returnPressed()), this, SLOT(slotAdvanceFocus()));
    connect(avtUI->PlotList, SIGNAL(currentRowChanged(int)), this, SLOT(slotHighlight(int)));
    connect(avtUI->computeButton, SIGNAL(clicked()), this, SLOT(slotComputeAltitudeByTime()));
    connect(avtUI->riseButton, SIGNAL(clicked()), this, SLOT(slotMarkRiseTime()));
    connect(avtUI->setButton, SIGNAL(clicked()), this, SLOT(slotMarkSetTime()));
    connect(avtUI->transitButton, SIGNAL(clicked()), this, SLOT(slotMarkTransitTime()));

    // Set up the Rise/Set/Transit buttons' icons:

    QPixmap redButton(100, 100);
    redButton.fill(Qt::transparent);
    QPainter p;
    p.begin(&redButton);
    p.setRenderHint(QPainter::Antialiasing, true);
    QPen pen(Qt::red, 2);
    p.setPen(pen);
    QBrush brush(Qt::red);
    p.setBrush(brush);
    p.drawEllipse(15, 15, 80, 80);
    p.end();

    QPixmap blueButton(100, 100);
    blueButton.fill(Qt::transparent);
    QPainter p1;
    p1.begin(&blueButton);
    p1.setRenderHint(QPainter::Antialiasing, true);
    QPen pen1(Qt::blue, 2);
    p1.setPen(pen1);
    QBrush brush1(Qt::blue);
    p1.setBrush(brush1);
    p1.drawEllipse(15, 15, 80, 80);
    p1.end();

    QPixmap greenButton(100, 100);
    greenButton.fill(Qt::transparent);
    QPainter p2;
    p2.begin(&greenButton);
    p2.setRenderHint(QPainter::Antialiasing, true);
    QPen pen2(Qt::green, 2);
    p2.setPen(pen2);
    QBrush brush2(Qt::green);
    p2.setBrush(brush2);
    p2.drawEllipse(15, 15, 80, 80);
    p2.end();

    avtUI->riseButton->setIcon(QIcon(redButton));
    avtUI->setButton->setIcon(QIcon(blueButton));
    avtUI->transitButton->setIcon(QIcon(greenButton));

    setMouseTracking(true);
}

AltVsTime::~AltVsTime()
{
    //WARNING: need to delete deleteList items!
}
void AltVsTime::slotAddSource()
{
    SkyObject *obj = KStarsData::Instance()->objectNamed(avtUI->nameBox->text());

    if (obj)
    {
        //An object with the current name exists.  If the object is not already
        //in the avt list, add it.
        bool found = false;
        foreach (SkyObject *o, pList)
        {
            //if ( o->name() == obj->name() ) {
            if (getObjectName(o, false) == getObjectName(obj, false))
            {
                found = true;
                break;
            }
        }
        if (!found)
            processObject(obj);
    }
    else
    {
        //Object with the current name doesn't exist.  It's possible that the
        //user is trying to add a custom object.  Assume this is the case if
        //the RA and Dec fields are filled in.

        if (!avtUI->nameBox->text().isEmpty() && !avtUI->raBox->text().isEmpty() && !avtUI->decBox->text().isEmpty())
        {
            bool okRA, okDec;
            dms newRA  = avtUI->raBox->createDms(false, &okRA);
            dms newDec = avtUI->decBox->createDms(true, &okDec);
            if (!okRA || !okDec)
                return;

            //If the epochName is blank (or any non-double), we assume J2000
            //Otherwise, precess to J2000.
            KStarsDateTime dt;
            dt.setFromEpoch(getEpoch(avtUI->epochName->text()));
            long double jd = dt.djd();
            if (jd != J2000)
            {
                SkyPoint ptest(newRA, newDec);
                ptest.precessFromAnyEpoch(jd, J2000);
                newRA.setH(ptest.ra().Hours());
                newDec.setD(ptest.dec().Degrees());
            }

            //make sure the coords do not already exist from another object
            bool found = false;
            foreach (SkyObject *p, pList)
            {
                //within an arcsecond?
                if (fabs(newRA.Degrees() - p->ra().Degrees()) < 0.0003 &&
                    fabs(newDec.Degrees() - p->dec().Degrees()) < 0.0003)
                {
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                SkyObject *obj = new SkyObject(8, newRA, newDec, 1.0, avtUI->nameBox->text());
                deleteList.append(obj); //this object will be deleted when window is destroyed
                processObject(obj);
            }
        }

        //If the Ra and Dec boxes are filled, but the name field is empty,
        //move input focus to nameBox.  If either coordinate box is empty,
        //move focus there
        if (avtUI->nameBox->text().isEmpty())
        {
            avtUI->nameBox->QWidget::setFocus();
        }
        if (avtUI->raBox->text().isEmpty())
        {
            avtUI->raBox->QWidget::setFocus();
        }
        else
        {
            if (avtUI->decBox->text().isEmpty())
                avtUI->decBox->QWidget::setFocus();
        }
    }

    avtUI->View->update();
}

//Use find dialog to choose an object
void AltVsTime::slotBrowseObject()
{
    QPointer<FindDialog> fd = new FindDialog(this);
    if (fd->exec() == QDialog::Accepted)
    {
        SkyObject *o = fd->targetObject();
        processObject(o);
    }
    delete fd;

    avtUI->View->update();
    avtUI->View->replot();
}

void AltVsTime::processObject(SkyObject *o, bool forceAdd)
{
    if (!o)
        return;

    KSNumbers *num    = new KSNumbers(getDate().djd());
    KSNumbers *oldNum = 0;

    //If the object is in the solar system, recompute its position for the given epochLabel
    KStarsData *data = KStarsData::Instance();
    if (o->isSolarSystem())
    {
        oldNum = new KSNumbers(data->ut().djd());
        o->updateCoords(num, true, geo->lat(), data->lst(), true);
    }

    //precess coords to target epoch
    o->updateCoordsNow(num);

    // vector used for computing the points needed for drawing the graph
    QVector<double> y(100), t(100);

    //If this point is not in list already, add it to list
    bool found(false);
    foreach (SkyObject *p, pList)
    {
        if (o->ra().Degrees() == p->ra().Degrees() && o->dec().Degrees() == p->dec().Degrees())
        {
            found = true;
            break;
        }
    }
    if (found && !forceAdd)
    {
        qDebug() << "This point is already displayed; I will not duplicate it.";
    }
    else
    {
        pList.append(o);

        // make sure existing curves are thin and red:

        for (int i = 0; i < avtUI->View->graphCount(); i++)
        {
            if (avtUI->View->graph(i)->pen().color() == Qt::white)
            {
                avtUI->View->graph(i)->setPen(QPen(Qt::red, 2));
            }
        }

        // SET up the curve's name
        avtUI->View->addGraph()->setName(o->name());

        // compute the current graph:
        // time range: 24h

        int offset = 3;
        for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
        {
            y[i] = findAltitude(o, h);
            if (y[i] > maxAlt)
                maxAlt = y[i];
            if (y[i] < minAlt)
                minAlt = y[i];
            t[i] = i * 900 + 43200;
            avtUI->View->graph(avtUI->View->graphCount() - 1)->addData(t[i], y[i]);
        }
        avtUI->View->graph(avtUI->View->graphCount() - 1)->setPen(QPen(Qt::white, 3));

        // Go into initial state: without Zoom/Pan
        avtUI->View->xAxis->setRange(43200, 129600);
        avtUI->View->xAxis2->setRange(61200, 147600);
        if (abs(minAlt) > maxAlt)
            maxAlt = abs(minAlt);
        else
            minAlt = -maxAlt;

        avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);

        // Update background coordonates:
        background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
        background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);

        avtUI->View->replot();

        avtUI->PlotList->addItem(getObjectName(o));
        avtUI->PlotList->setCurrentRow(avtUI->PlotList->count() - 1);
        avtUI->raBox->showInHours(o->ra());
        avtUI->decBox->showInDegrees(o->dec());
        avtUI->nameBox->setText(getObjectName(o));

        //Set epochName to epoch shown in date tab
        avtUI->epochName->setText(QString().setNum(getDate().epoch()));
    }
    qDebug() << "Currently, there are " << avtUI->View->graphCount() << " objects displayed.";

    //restore original position
    if (o->isSolarSystem())
    {
        o->updateCoords(oldNum, true, data->geo()->lat(), data->lst(), true);
        delete oldNum;
    }
    o->EquatorialToHorizontal(data->lst(), data->geo()->lat());
    delete num;
}

double AltVsTime::findAltitude(SkyPoint *p, double hour)
{
    hour += 24.0 * DayOffset;

    //getDate converts the user-entered local time to UT
    KStarsDateTime ut = getDate().addSecs(hour * 3600.0);

    CachingDms LST = geo->GSTtoLST(ut.gst());
    p->EquatorialToHorizontal(&LST, geo->lat());
    return p->alt().Degrees();
}

void AltVsTime::slotHighlight(int row)
{
    if (row < 0)
        return;

    int rowIndex = 0;
    //highlight the curve of the selected object
    for (int i = 0; i < avtUI->View->graphCount(); i++)
    {
        if (i == row)
            rowIndex = row;
        else
        {
            avtUI->View->graph(i)->setPen(QPen(Qt::red, 2));
            avtUI->View->graph(i)->setLayer("main");
        }
    }
    avtUI->View->graph(rowIndex)->setPen(QPen(Qt::white, 3));
    avtUI->View->graph(rowIndex)->setLayer("currentCurveLayer");
    avtUI->View->update();
    avtUI->View->replot();

    if (row >= 0 && row < pList.size())
    {
        SkyObject *p = pList.at(row);
        avtUI->raBox->showInHours(p->ra());
        avtUI->decBox->showInDegrees(p->dec());
        avtUI->nameBox->setText(avtUI->PlotList->currentItem()->text());
    }

    SkyObject *selectedObject = KStarsData::Instance()->objectNamed(avtUI->nameBox->text());
    const KStarsDateTime &ut  = KStarsData::Instance()->ut();
    if (selectedObject)
    {
        QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
        if (rt.isValid() == false)
        {
            avtUI->riseButton->setEnabled(false);
            avtUI->setButton->setEnabled(false);
        }
        else
        {
            avtUI->riseButton->setEnabled(true);
            avtUI->setButton->setEnabled(true);
        }
    }
}

void AltVsTime::onXRangeChanged(const QCPRange &range)
{
    QCPRange aux = avtUI->View->xAxis2->range();
    avtUI->View->xAxis->setRange(aux -= 18000);
    avtUI->View->xAxis2->setRange(range.bounded(61200, 147600));
    // if ZOOM is detected then remove the gold lines that indicate current position:
    if (avtUI->View->xAxis->range().size() != 86400)
    {
        // Refresh the background:
        background->setScaled(false);
        background->setScaled(true, Qt::IgnoreAspectRatio);
        background->setPixmap(*gradient);

        avtUI->View->update();
        avtUI->View->replot();
    }
}

void AltVsTime::onYRangeChanged(const QCPRange &range)
{
    int offset = 3;
    avtUI->View->yAxis->setRange(range.bounded(minAlt - offset, maxAlt + offset));
}

void AltVsTime::plotMousePress(QCPAbstractPlottable *abstractPlottable, int dataIndex, QMouseEvent *event)
{
    //Do we need this?
    Q_UNUSED(dataIndex);

    if (event->button() == Qt::RightButton)
    {
        QCPAbstractPlottable *plottable = abstractPlottable;
        if (plottable)
        {
            double x = avtUI->View->xAxis->pixelToCoord(event->localPos().x());
            double y = avtUI->View->yAxis->pixelToCoord(event->localPos().y());

            QCPGraph *graph = qobject_cast<QCPGraph *>(plottable);

            if (graph)
            {
                double yValue = y;
                double xValue = x;

                // Compute time value:
                QTime localTime(0, 0, 0, 0);
                QTime localSiderealTime(5, 0, 0, 0);

                localTime         = localTime.addSecs(int(xValue));
                localSiderealTime = localSiderealTime.addSecs(int(xValue));

                QToolTip::hideText();
                QToolTip::showText(event->globalPos(),
                                   tr("<table>"
                                      "<tr>"
                                      "<th colspan=\"2\">%L1</th>"
                                      "</tr>"
                                      "<tr>"
                                      "<td>LST:   </td>"
                                      "<td>%L3</td>"
                                      "</tr>"
                                      "<tr>"
                                      "<td>LT:   </td>"
                                      "<td>%L2</td>"
                                      "</tr>"
                                      "<tr>"
                                      "<td>Altitude:   </td>"
                                      "<td>%L4</td>"
                                      "</tr>"
                                      "</table>")
                                       .arg(graph->name().isEmpty() ? "???" : graph->name())
                                       .arg(localTime.toString())
                                       .arg(localSiderealTime.toString())
                                       .arg(QString::number(yValue, 'f', 2) + " " + QChar(176)),
                                   avtUI->View, avtUI->View->rect());
            }
        }
    }
}

//move input focus to the next logical widget
void AltVsTime::slotAdvanceFocus()
{
    if (sender()->objectName() == QString("nameBox"))
        avtUI->addButton->setFocus();
    if (sender()->objectName() == QString("raBox"))
        avtUI->decBox->setFocus();
    if (sender()->objectName() == QString("decbox"))
        avtUI->addButton->setFocus();
    if (sender()->objectName() == QString("longBox"))
        avtUI->latBox->setFocus();
    if (sender()->objectName() == QString("latBox"))
        avtUI->updateButton->setFocus();
}

void AltVsTime::slotClear()
{
    pList.clear();
    //Need to delete the pointers in deleteList
    while (!deleteList.isEmpty())
        delete deleteList.takeFirst();

    avtUI->PlotList->clear();
    avtUI->nameBox->clear();
    avtUI->raBox->clear();
    avtUI->decBox->clear();
    avtUI->epochName->clear();
    // remove all graphs from the plot:
    avtUI->View->clearGraphs();
    // we remove all the dots (rise/set/transit) from the chart
    // without removing the background image
    int indexItem = 0, noItems = avtUI->View->itemCount();
    QCPAbstractItem *item;
    QCPItemPixmap *background;
    // remove every item at a time:
    while (noItems > 1 && indexItem < noItems)
    {
        // test if the current item is the background:
        item       = avtUI->View->item(indexItem);
        background = qobject_cast<QCPItemPixmap *>(item);
        if (background)
            indexItem++;
        else
        {
            // if not, then remove this item:
            avtUI->View->removeItem(indexItem);
            noItems--;
        }
    }
    // update & replot the chart:
    avtUI->View->update();
    avtUI->View->replot();
}

void AltVsTime::slotClearBoxes()
{
    avtUI->nameBox->clear();
    avtUI->raBox->clear();
    avtUI->decBox->clear();
    avtUI->epochName->clear();
}

void AltVsTime::slotComputeAltitudeByTime()
{
// FIXME Migrate to QCustomPlot 2.0
#if 0
    // check if at least one graph exists in the plot
    if( avtUI->View->graphCount() > 0 )
    {
        // get the time from the time spin box
        QTime timeFormat = avtUI->timeSpin->time();
        double hours = timeFormat.hour();
        double minutes = timeFormat.minute();
        // convert the hours over 24 to correct their values
        if( hours < 12 )
            hours += 24;
        double timeValue = hours * 3600 + minutes * 60;
        QCPGraph * selectedGraph;
        // get the graph's name from the name box
        QString graphName = avtUI->nameBox->text();
        // find the graph index
        int graphIndex = 0;
        for( int i=0; i<avtUI->View->graphCount(); i++ )
            if( avtUI->View->graph(i)->name().compare(graphName) == 0 )
            {
                graphIndex = i;
                break;
            }
        selectedGraph = avtUI->View->graph(graphIndex);
        // get the data from the selected graph
        QCPDataMap * dataMap = selectedGraph->data();
        double averageAltitude = 0;
        double altitude1 = dataMap->lowerBound(timeValue-899).value().value;
        double altitude2 = dataMap->lowerBound(timeValue).value().value;
        double time1 = dataMap->lowerBound(timeValue-899).value().key;
        averageAltitude = (altitude1+altitude2)/2;
        // short algorithm to compute the right altitude for a certain time
        if( timeValue > time1 )
        {
            if( timeValue - time1 < 225 )
                averageAltitude = altitude1;
            else if( timeValue - time1 < 675 )
                averageAltitude = (altitude1+altitude2)/2;
            else
                averageAltitude = altitude2;
        }
        // set the altitude in the altitude box
        avtUI->altitudeBox->setText( QString::number(averageAltitude, 'f', 2) );
    }
#endif
}

void AltVsTime::slotMarkRiseTime()
{
    const KStarsDateTime &ut  = KStarsData::Instance()->ut();
    SkyObject *selectedObject = KStarsData::Instance()->objectNamed(avtUI->nameBox->text());
    QCPItemTracer *riseTimeTracer;
    // check if at least one graph exists in the plot
    if (avtUI->View->graphCount() > 0)
    {
        double time = 0;
        double hours, minutes;

        QCPGraph *selectedGraph;
        // get the graph's name from the name box
        QString graphName = avtUI->nameBox->text();
        // find the graph index
        int graphIndex = 0;
        for (int i = 0; i < avtUI->View->graphCount(); i++)
            if (avtUI->View->graph(i)->name().compare(graphName) == 0)
            {
                graphIndex = i;
                break;
            }
        selectedGraph = avtUI->View->graph(graphIndex);

        QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
        // mark the Rise time with a solid red circle
        if (rt.isValid() && selectedGraph)
        {
            hours   = rt.hour();
            minutes = rt.minute();
            if (hours < 12)
                hours += 24;
            time           = hours * 3600 + minutes * 60;
            riseTimeTracer = new QCPItemTracer(avtUI->View);
            riseTimeTracer->setLayer("markersLayer");
            riseTimeTracer->setGraph(selectedGraph);
            riseTimeTracer->setInterpolating(true);
            riseTimeTracer->setStyle(QCPItemTracer::tsCircle);
            riseTimeTracer->setPen(QPen(Qt::red));
            riseTimeTracer->setBrush(Qt::red);
            riseTimeTracer->setSize(10);
            riseTimeTracer->setGraphKey(time);
            riseTimeTracer->setVisible(true);
            avtUI->View->update();
            avtUI->View->replot();
        }
    }
}

void AltVsTime::slotMarkSetTime()
{
    const KStarsDateTime &ut  = KStarsData::Instance()->ut();
    SkyObject *selectedObject = KStarsData::Instance()->objectNamed(avtUI->nameBox->text());
    QCPItemTracer *setTimeTracer;
    // check if at least one graph exists in the plot
    if (avtUI->View->graphCount() > 0)
    {
        double time = 0;
        double hours, minutes;

        QCPGraph *selectedGraph;
        // get the graph's name from the name box
        QString graphName = avtUI->nameBox->text();
        // find the graph index
        int graphIndex = 0;
        for (int i = 0; i < avtUI->View->graphCount(); i++)
            if (avtUI->View->graph(i)->name().compare(graphName) == 0)
            {
                graphIndex = i;
                break;
            }
        selectedGraph = avtUI->View->graph(graphIndex);

        QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
        //If set time is before rise time, use set time for tomorrow
        QTime st = selectedObject->riseSetTime(ut, geo, false); //false = use set time
        if (st < rt)
            st = selectedObject->riseSetTime(ut.addDays(1), geo, false); //false = use set time
        // mark the Set time with a solid blue circle
        if (rt.isValid())
        {
            hours   = st.hour();
            minutes = st.minute();
            if (hours < 12)
                hours += 24;
            time          = hours * 3600 + minutes * 60;
            setTimeTracer = new QCPItemTracer(avtUI->View);
            setTimeTracer->setLayer("markersLayer");
            setTimeTracer->setGraph(selectedGraph);
            setTimeTracer->setInterpolating(true);
            setTimeTracer->setStyle(QCPItemTracer::tsCircle);
            setTimeTracer->setPen(QPen(Qt::blue));
            setTimeTracer->setBrush(Qt::blue);
            setTimeTracer->setSize(10);
            setTimeTracer->setGraphKey(time);
            setTimeTracer->setVisible(true);
            avtUI->View->update();
            avtUI->View->replot();
        }
    }
}

void AltVsTime::slotMarkTransitTime()
{
    const KStarsDateTime &ut  = KStarsData::Instance()->ut();
    SkyObject *selectedObject = KStarsData::Instance()->objectNamed(avtUI->nameBox->text());
    QCPItemTracer *transitTimeTracer;
    // check if at least one graph exists in the plot
    if (avtUI->View->graphCount() > 0)
    {
        double time = 0;
        double hours, minutes;

        QCPGraph *selectedGraph;
        // get the graph's name from the name box
        QString graphName = avtUI->nameBox->text();
        // find the graph index
        int graphIndex = 0;
        for (int i = 0; i < avtUI->View->graphCount(); i++)
            if (avtUI->View->graph(i)->name().compare(graphName) == 0)
            {
                graphIndex = i;
                break;
            }
        selectedGraph = avtUI->View->graph(graphIndex);

        QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
        //If transit time is before rise time, use transit time for tomorrow
        QTime tt = selectedObject->transitTime(ut, geo);

        if (tt < rt)
            tt = selectedObject->transitTime(ut.addDays(1), geo);
        // mark the Transit time with a solid green circle
        hours   = tt.hour();
        minutes = tt.minute();
        if (hours < 12)
            hours += 24;
        time              = hours * 3600 + minutes * 60;
        transitTimeTracer = new QCPItemTracer(avtUI->View);
        transitTimeTracer->setLayer("markersLayer");
        transitTimeTracer->setGraph(selectedGraph);
        transitTimeTracer->setInterpolating(true);
        transitTimeTracer->setStyle(QCPItemTracer::tsCircle);
        transitTimeTracer->setPen(QPen(Qt::green));
        transitTimeTracer->setBrush(Qt::green);
        transitTimeTracer->setSize(10);
        transitTimeTracer->setGraphKey(time);
        transitTimeTracer->setVisible(true);
        avtUI->View->update();
        avtUI->View->replot();
    }
}

void AltVsTime::computeSunRiseSetTimes()
{
    //Determine the time of sunset and sunrise for the desired date and location
    //expressed as doubles, the fraction of a full day.
    KStarsDateTime today = getDate();
    KSAlmanac ksal;
    ksal.setDate(&today);
    ksal.setLocation(geo);
    double sunRise = ksal.getSunRise();
    double sunSet  = ksal.getSunSet();
    this->setSunRiseSetTimes(sunRise, sunSet);
}

void AltVsTime::setSunRiseSetTimes(double sunRise, double sunSet)
{
    this->sunRise = sunRise;
    this->sunSet  = sunSet;
}

//FIXME
/*
void AltVsTime::mouseOverLine(QMouseEvent *event){
    // Get the mouse position's coordinates relative to axes:
    double x = avtUI->View->xAxis->pixelToCoord(event->pos().x());
    double y = avtUI->View->yAxis->pixelToCoord(event->pos().y());
    // Save the actual values:
    double yValue = y;
    double xValue = x;
    // The offset used for the Y axis: top/bottom
    int offset = 3;
    // Compute the Y axis maximum value:
    int yAxisMaxValue = maxAlt + offset;
    // Compute the X axis minimum and maximum values:
    int xAxisMinValue = 43200;
    int xAxisMaxValue = 129600;
    // Ignore the upper and left margins:
    y = yAxisMaxValue - y;
    x -= xAxisMinValue;
    // We make a copy to gradient background in order to have one set of lines at a time:
    // Otherwise, the chart would have been covered by lines
    QPixmap copy = gradient->copy(gradient->rect());
    // If ZOOM is not active, then draw the gold lines that indicate current mouse pisition:
    if(avtUI->View->xAxis->range().size() == 86400){
        QPainter p;

        p.begin(&copy);
        p.setPen( QPen( QBrush("gold"), 2, Qt::SolidLine ) );

        // Get the gradient background's width and height:
        int pW = gradient->rect().width();
        int pH = gradient->rect().height();

        // Compute the real coordinates within the chart:
        y = (y*pH/2)/yAxisMaxValue;
        x = (x*pW)/(xAxisMaxValue-xAxisMinValue);

        // Draw the horizontal line (altitude):
        p.drawLine( QLineF( 0.5, y, avtUI->View->rect().width()-0.5,y ) );
        // Draw the altitude value:
        p.setPen( QPen( QBrush("gold"), 3, Qt::SolidLine ) );
        p.drawText( 25, y + 15, QString::number(yValue,'f',2) + QChar(176) );
        p.setPen( QPen( QBrush("gold"), 1, Qt::SolidLine ) );
        // Draw the vertical line (time):
        p.drawLine( QLineF( x, 0.5, x, avtUI->View->rect().height()-0.5 ) );
        // Compute and draw the time value:
        QTime localTime(0,0,0,0);
        localTime = localTime.addSecs(int(xValue));
        p.save();
        p.translate( x + 10, pH - 20 );
        p.rotate(-90);
        p.setPen( QPen( QBrush("gold"), 3, Qt::SolidLine ) );
        p.drawText( 5, 5, QLocale().toString( localTime, QLocale::ShortFormat ) ); // short format necessary to avoid false time-zone labeling
        p.restore();
        p.end();
    }
    // Refresh the background:
    background->setScaled(false);
    background->setScaled(true, Qt::IgnoreAspectRatio);
    background->setPixmap(copy);

    avtUI->View->update();
    avtUI->View->replot();
}
*/

void AltVsTime::mouseOverLine(QMouseEvent *event)
{
    double x                            = avtUI->View->xAxis->pixelToCoord(event->localPos().x());
    double y                            = avtUI->View->yAxis->pixelToCoord(event->localPos().y());
    QCPAbstractPlottable *abstractGraph = avtUI->View->plottableAt(event->pos(), false);
    QCPGraph *graph                     = qobject_cast<QCPGraph *>(abstractGraph);

    if (x > avtUI->View->xAxis->range().lower && x < avtUI->View->xAxis->range().upper)
        if (y > avtUI->View->yAxis->range().lower && y < avtUI->View->yAxis->range().upper)
        {
            if (graph)
            {
                double yValue = y;
                double xValue = x;

                // Compute time value:
                QTime localTime(0, 0, 0, 0);
                QTime localSiderealTime(5, 0, 0, 0);

                localTime         = localTime.addSecs(int(xValue));
                localSiderealTime = localSiderealTime.addSecs(int(xValue));

                QToolTip::hideText();
                QToolTip::showText(event->globalPos(),
                                   tr("<table>"
                                      "<tr>"
                                      "<th colspan=\"2\">%L1</th>"
                                      "</tr>"
                                      "<tr>"
                                      "<td>LST:   </td>"
                                      "<td>%L3</td>"
                                      "</tr>"
                                      "<tr>"
                                      "<td>LT:   </td>"
                                      "<td>%L2</td>"
                                      "</tr>"
                                      "<tr>"
                                      "<td>Altitude:   </td>"
                                      "<td>%L4</td>"
                                      "</tr>"
                                      "</table>")
                                       .arg(graph->name().isEmpty() ? "???" : graph->name())
                                       .arg(localTime.toString())
                                       .arg(localSiderealTime.toString())
                                       .arg(QString::number(yValue, 'f', 2) + " " + QChar(176)),
                                   avtUI->View, avtUI->View->rect());
            }
            else
                QToolTip::hideText();
        }

    avtUI->View->update();
    avtUI->View->replot();
}

void AltVsTime::slotUpdateDateLoc()
{
    KStarsData *data     = KStarsData::Instance();
    KStarsDateTime today = getDate();
    KSNumbers *num       = new KSNumbers(today.djd());
    KSNumbers *oldNum    = 0;
    CachingDms LST       = geo->GSTtoLST(today.gst());

    //First determine time of sunset and sunrise
    computeSunRiseSetTimes();
    // Determine dawn/dusk time and min/max sun elevation
    setDawnDusk();

    for (int i = 0; i < avtUI->PlotList->count(); ++i)
    {
        QString oName = avtUI->PlotList->item(i)->text().toLower();

        SkyObject *o = data->objectNamed(oName);
        if (o)
        {
            //If the object is in the solar system, recompute its position for the given date
            if (o->isSolarSystem())
            {
                oldNum = new KSNumbers(data->ut().djd());
                o->updateCoords(num, true, geo->lat(), &LST, true);
            }

            //precess coords to target epoch
            o->updateCoordsNow(num);

            //update pList entry
            pList.replace(i, o);

            // We are creating a new data set (time, altitude) for the new date:
            QVector<double> time_dataSet, altitude_dataSet;
            double point_altitudeValue, point_timeValue;
            // compute the new graph values:
            // time range: 24h
            int offset = 3;
            for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
            {
                point_altitudeValue = findAltitude(o, h);
                altitude_dataSet.push_back(point_altitudeValue);
                if (point_altitudeValue > maxAlt)
                    maxAlt = point_altitudeValue;
                if (point_altitudeValue < minAlt)
                    minAlt = point_altitudeValue;
                point_timeValue = i * 900 + 43200;
                time_dataSet.push_back(point_timeValue);
            }

            // Replace graph data set:
            avtUI->View->graph(i)->setData(time_dataSet, altitude_dataSet);

            // Go into initial state: without Zoom/Pan
            avtUI->View->xAxis->setRange(43200, 129600);
            avtUI->View->xAxis2->setRange(61200, 147600);

            // Center the altitude axis in 0 value:
            if (abs(minAlt) > maxAlt)
                maxAlt = abs(minAlt);
            else
                minAlt = -maxAlt;
            avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);

            // Update background coordonates:
            background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
            background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);

            // Redraw the plot:
            avtUI->View->replot();

            //restore original position
            if (o->isSolarSystem())
            {
                o->updateCoords(oldNum, true, data->geo()->lat(), data->lst());
                delete oldNum;
                oldNum = 0;
            }
            o->EquatorialToHorizontal(data->lst(), data->geo()->lat());
        }
        else //assume unfound object is a custom object
        {
            pList.at(i)->updateCoordsNow(num); //precess to desired epoch

            // We are creating a new data set (time, altitude) for the new date:
            QVector<double> time_dataSet, altitude_dataSet;
            double point_altitudeValue, point_timeValue;
            // compute the new graph values:
            // time range: 24h
            int offset = 3;
            for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
            {
                point_altitudeValue = findAltitude(pList.at(i), h);
                altitude_dataSet.push_back(point_altitudeValue);
                if (point_altitudeValue > maxAlt)
                    maxAlt = point_altitudeValue;
                if (point_altitudeValue < minAlt)
                    minAlt = point_altitudeValue;
                point_timeValue = i * 900 + 43200;
                time_dataSet.push_back(point_timeValue);
            }

            // Replace graph data set:
            avtUI->View->graph(i)->setData(time_dataSet, altitude_dataSet);

            // Go into initial state: without Zoom/Pan
            avtUI->View->xAxis->setRange(43200, 129600);
            avtUI->View->xAxis2->setRange(61200, 147600);

            // Center the altitude axis in 0 value:
            if (abs(minAlt) > maxAlt)
                maxAlt = abs(minAlt);
            else
                minAlt = -maxAlt;
            avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);

            // Update background coordonates:
            background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
            background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);

            // Redraw the plot:
            avtUI->View->replot();
        }
    }

    if (getDate().time().hour() > 12)
        DayOffset = 1;
    else
        DayOffset = 0;

    setLSTLimits();
    slotHighlight(avtUI->PlotList->currentRow());
    avtUI->View->update();

    delete num;
}

void AltVsTime::slotChooseCity()
{
    QPointer<LocationDialog> ld = new LocationDialog(this);
    if (ld->exec() == QDialog::Accepted)
    {
        GeoLocation *newGeo = ld->selectedCity();
        if (newGeo)
        {
            geo = newGeo;
            avtUI->latBox->showInDegrees(geo->lat());
            avtUI->longBox->showInDegrees(geo->lng());
        }
    }
    delete ld;
}

// FIXME: should we remove this method?
void AltVsTime::setLSTLimits()
{
    /*
    //UT at noon on target date
    KStarsDateTime ut = getDate().addSecs(((double)DayOffset + 0.5)*86400.);

    dms lst = geo->GSTtoLST(ut.gst());
    double h1 = lst.Hours();
    if(h1 > 12.0)
        h1 -= 24.0;
    double h2 = h1 + 24.0;
    avtUI->View->setSecondaryLimits(h1, h2, -90.0, 90.0);
    */
}

void AltVsTime::showCurrentDate()
{
    KStarsDateTime dt = KStarsDateTime::currentDateTime();
    if (dt.time() > QTime(12, 0, 0))
        dt = dt.addDays(1);
    avtUI->DateWidget->setDate(dt.date());
}

void AltVsTime::drawGradient()
{
    // Things needed for Gradient:
    KSAlmanac *ksal;
    KStarsDateTime dtt;
    GeoLocation *geoLoc;
    dtt                = KStarsDateTime::currentDateTime();
    geoLoc             = KStarsData::Instance()->geo();
    ksal               = new KSAlmanac;
    QDateTime midnight = QDateTime(dtt.date(), QTime());
    KStarsDateTime utt = geoLoc->LTtoUT(midnight);

    // Variables needed for Gradient:
    double SunRise, SunSet, Dawn, Dusk, SunMinAlt, SunMaxAlt;
    double MoonRise, MoonSet, MoonIllum;

    //Default SunRise/SunSet values
    SunRise = 0.25;
    SunSet  = 0.75;

    ksal->setLocation(geoLoc);
    ksal->setDate(&utt);

    // Get the values:
    SunRise   = ksal->getSunRise();
    SunSet    = ksal->getSunSet();
    SunMaxAlt = ksal->getSunMaxAlt();
    SunMinAlt = ksal->getSunMinAlt();
    MoonRise  = ksal->getMoonRise();
    MoonSet   = ksal->getMoonSet();
    MoonIllum = ksal->getMoonIllum();
    Dawn      = ksal->getDawnAstronomicalTwilight();
    Dusk      = ksal->getDuskAstronomicalTwilight();

    gradient = new QPixmap(avtUI->View->rect().width(), avtUI->View->rect().height());

    QPainter p;

    p.begin(gradient);
    KPlotWidget *kPW = new KPlotWidget;
    p.setRenderHint(QPainter::Antialiasing, kPW->antialiasing());
    p.fillRect(gradient->rect(), kPW->backgroundColor());

    p.setClipRect(gradient->rect());
    p.setClipping(true);

    int pW = gradient->rect().width();
    int pH = gradient->rect().height();

    QColor SkyColor(0, 100, 200);
    //    TODO
    //    if( Options::darkAppColors() )
    //        SkyColor = QColor( 200, 0, 0 ); // use something red, visible through a red filter

    // Draw gradient representing lunar interference in the sky
    if (MoonIllum > 0.01) // do this only if Moon illumination is reasonable so it's important
    {
        int moonrise = int(pW * (0.5 + MoonRise));
        int moonset  = int(pW * (MoonSet - 0.5));
        if (moonset < 0)
            moonset += pW;
        if (moonrise > pW)
            moonrise -= pW;
        int moonalpha = int(10 + MoonIllum * 130);
        int fadewidth =
            pW *
            0.01; // pW * fraction of day to fade the moon brightness over (0.01 corresponds to roughly 15 minutes, 0.007 to 10 minutes), both before and after actual set.
        QColor MoonColor(255, 255, 255, moonalpha);

        if (moonset < moonrise)
        {
            QLinearGradient grad =
                QLinearGradient(QPointF(moonset - fadewidth, 0.0), QPointF(moonset + fadewidth, 0.0));
            grad.setColorAt(0, MoonColor);
            grad.setColorAt(1, Qt::transparent);
            p.fillRect(QRectF(0.0, 0.0, moonset + fadewidth, pH),
                       grad); // gradient should be padded until moonset - fadewidth (see QLinearGradient docs)
            grad.setStart(QPointF(moonrise + fadewidth, 0.0));
            grad.setFinalStop(QPointF(moonrise - fadewidth, 0.0));
            p.fillRect(QRectF(moonrise - fadewidth, 0.0, pW - moonrise + fadewidth, pH), grad);
        }
        else
        {
            qreal opacity = p.opacity();
            p.setOpacity(opacity / 4);
            p.fillRect(QRectF(moonrise + fadewidth, 0.0, moonset - moonrise - 2 * fadewidth, pH), MoonColor);
            QLinearGradient grad =
                QLinearGradient(QPointF(moonrise + fadewidth, 0.0), QPointF(moonrise - fadewidth, 0.0));
            grad.setColorAt(0, MoonColor);
            grad.setColorAt(1, Qt::transparent);
            p.fillRect(QRectF(0.0, 0.0, moonrise + fadewidth, pH), grad);
            grad.setStart(QPointF(moonset - fadewidth, 0.0));
            grad.setFinalStop(QPointF(moonset + fadewidth, 0.0));
            p.fillRect(QRectF(moonset - fadewidth, 0.0, pW - moonset, pH), grad);
            p.setOpacity(opacity);
        }
    }

    //draw daytime sky if the Sun rises for the current date/location
    if (SunMaxAlt > -18.0)
    {
        //Display centered on midnight, so need to modulate dawn/dusk by 0.5
        int rise = int(pW * (0.5 + SunRise));
        int set  = int(pW * (SunSet - 0.5));
        int da   = int(pW * (0.5 + Dawn));
        int du   = int(pW * (Dusk - 0.5));

        if (SunMinAlt > 0.0)
        {
            // The sun never set and the sky is always blue
            p.fillRect(rect(), SkyColor);
        }
        else if (SunMaxAlt < 0.0 && SunMinAlt < -18.0)
        {
            // The sun never rise but the sky is not completely dark
            QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(du, 0.0));

            QColor gradStartColor = SkyColor;
            gradStartColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255);

            grad.setColorAt(0, gradStartColor);
            grad.setColorAt(1, Qt::transparent);
            p.fillRect(QRectF(0.0, 0.0, du, pH), grad);
            grad.setStart(QPointF(pW, 0.0));
            grad.setFinalStop(QPointF(da, 0.0));
            p.fillRect(QRectF(da, 0.0, pW, pH), grad);
        }
        else if (SunMaxAlt < 0.0 && SunMinAlt > -18.0)
        {
            // The sun never rise but the sky is NEVER completely dark
            QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(pW, 0.0));

            QColor gradStartEndColor = SkyColor;
            gradStartEndColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255);
            QColor gradMidColor = SkyColor;
            gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255);

            grad.setColorAt(0, gradStartEndColor);
            grad.setColorAt(0.5, gradMidColor);
            grad.setColorAt(1, gradStartEndColor);
            p.fillRect(QRectF(0.0, 0.0, pW, pH), grad);
        }
        else if (Dawn < 0.0)
        {
            // The sun sets and rises but the sky is never completely dark
            p.fillRect(0, 0, set, int(0.5 * pH), SkyColor);
            p.fillRect(rise, 0, pW, int(0.5 * pH), SkyColor);

            QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(rise, 0.0));

            QColor gradMidColor = SkyColor;
            gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255);

            grad.setColorAt(0, SkyColor);
            grad.setColorAt(0.5, gradMidColor);
            grad.setColorAt(1, SkyColor);
            p.fillRect(QRectF(set, 0.0, rise - set, pH), grad);
        }
        else
        {
            p.fillRect(0, 0, set, pH, SkyColor);
            p.fillRect(rise, 0, pW, pH, SkyColor);

            QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(du, 0.0));
            grad.setColorAt(0, SkyColor);
            grad.setColorAt(
                1,
                Qt::transparent); // FIXME?: The sky appears black well before the actual end of twilight if the gradient is too slow (eg: latitudes above arctic circle)
            p.fillRect(QRectF(set, 0.0, du - set, pH), grad);

            grad.setStart(QPointF(rise, 0.0));
            grad.setFinalStop(QPointF(da, 0.0));
            p.fillRect(QRectF(da, 0.0, rise - da, pH), grad);
        }
    }

    p.fillRect(0, int(0.5 * pH), pW, int(0.5 * pH), KStarsData::Instance()->colorScheme()->colorNamed("HorzColor"));

    p.setClipping(false);

    //Add vertical line indicating "now"
    if (geoLoc)
    {
        QTime t = geoLoc->UTtoLT(KStarsDateTime::currentDateTimeUtc())
                      .time(); // convert the current system clock time to the TZ corresponding to geo
        double x = 12.0 + t.hour() + t.minute() / 60.0 + t.second() / 3600.0;
        while (x > 24.0)
            x -= 24.0;
        int ix = int(x * pW / 24.0); //convert to screen pixel coords
        p.setPen(QPen(QBrush("white"), 2.0, Qt::DotLine));
        p.drawLine(ix, 0, ix, pH);

        QFont largeFont = p.font();
        largeFont.setPointSize(largeFont.pointSize() + 1);

        //Label this vertical line with the current time
        p.save();
        p.setFont(largeFont);
        p.translate(ix + 10, pH - 20);
        p.rotate(-90);
        p.drawText(
            0, 0,
            QLocale().toString(t, QLocale::ShortFormat)); // short format necessary to avoid false time-zone labeling
        p.restore();
    }
    p.end();
}

KStarsDateTime AltVsTime::getDate()
{
    //convert midnight local time to UT:
    QDateTime lt(avtUI->DateWidget->date(), QTime());
    return geo->LTtoUT(lt);
}

double AltVsTime::getEpoch(const QString &eName)
{
    //If Epoch field not a double, assume J2000
    bool ok;
    double epoch = eName.toDouble(&ok);
    if (!ok)
    {
        qDebug() << "Invalid Epoch.  Assuming 2000.0.";
        return 2000.0;
    }
    return epoch;
}

void AltVsTime::setDawnDusk()
{
    KStarsDateTime today = getDate();
    KSNumbers num(today.djd());
    CachingDms LST = geo->GSTtoLST(today.gst());

    KSSun sun;
    sun.updateCoords(&num, true, geo->lat(), &LST, true);

    /* TODO:
    double last_h = -12.0;
    double last_alt = findAltitude( &sun, last_h );
    double dawn = -13.0;
    double dusk = -13.0;
    double max_alt = -100.0;
    double min_alt = 100.0;
    for ( double h=-11.95; h<=12.0; h+=0.05 ) {
        double alt = findAltitude( &sun, h );
        bool asc = alt - last_alt > 0;
        if ( alt > max_alt )
            max_alt = alt;
        if ( alt < min_alt )
            min_alt = alt;

        if ( asc && last_alt <= -18.0 && alt >= -18.0 )
            dawn = h;
        if ( !asc && last_alt >= -18.0 && alt <= -18.0 )
            dusk = h;

        last_h   = h;
        last_alt = alt;
    }

    if ( dawn < -12.0 || dusk < -12.0 ) {
        da = -1.0;
        du = -1.0;
    } else {
        da = dawn / 24.0;
        du = ( dusk + 24.0 ) / 24.0;
    }
    avtUI->View->setDawnDuskTimes( da, du );
    avtUI->View->setMinMaxSunAlt( min_alt, max_alt );
    */
}

void AltVsTime::slotPrint()
{
    QPainter p;            // Our painter object
    QPrinter printer;      // Our printer object
    QString str_legend;    // Text legend
    QString str_year;      // Calendar's year
    int text_height = 200; // Height of legend text zone in points
    QSize plot_size;       // Initial plot widget size
    QFont plot_font;       // Initial plot widget font
    int plot_font_size;    // Initial plot widget font size

    // Set printer resolution to 300 dpi
    printer.setResolution(300);

    // Open print dialog
    //QPointer<QPrintDialog> dialog( KdePrint::createPrintDialog( &printer, this ) );
    //QPointer<QPrintDialog> dialog( &printer, this );
    QPrintDialog dialog(&printer, this);
    dialog.setWindowTitle(i18n("Print elevation vs time plot"));
    if (dialog.exec() == QDialog::Accepted)
    {
        // Change mouse cursor
        QApplication::setOverrideCursor(Qt::WaitCursor);

        // Save plot widget font
        plot_font = avtUI->View->font();
        // Save plot widget font size
        plot_font_size = plot_font.pointSize();
        // Save calendar widget size
        plot_size = avtUI->View->size();

        // Set text legend
        str_legend = i18n("Elevation vs. Time Plot");
        str_legend += "\n";
        str_legend += geo->fullName();
        str_legend += " - ";
        str_legend += avtUI->DateWidget->date().toString("dd/MM/yyyy");

        // Create a rectangle for legend text zone
        QRect text_rect(0, 0, printer.width(), text_height);

        // Increase plot widget font size so it looks good in 300 dpi
        plot_font.setPointSize(plot_font_size * 2.5);
        avtUI->View->setFont(plot_font);
        // Increase plot widget size to fit the entire page
        avtUI->View->resize(printer.width(), printer.height() - text_height);

        // Create a pixmap and render plot widget into it
        QPixmap pixmap(avtUI->View->size());
        avtUI->View->render(&pixmap);

        // Begin painting on printer
        p.begin(&printer);
        // Draw legend
        p.drawText(text_rect, Qt::AlignLeft, str_legend);
        // Draw plot widget
        p.drawPixmap(0, text_height, pixmap);
        // Ending painting
        p.end();

        // Restore plot widget font size
        plot_font.setPointSize(plot_font_size);
        avtUI->View->setFont(plot_font);
        // Restore calendar widget size
        avtUI->View->resize(plot_size);

        // Restore mouse cursor
        QApplication::restoreOverrideCursor();
    }
    //delete dialog;
}

QString AltVsTime::getObjectName(const SkyObject *o, bool translated)
{
    QString finalObjectName;
    if (o->name() == "star")
    {
        StarObject *s = (StarObject *)o;

        // JM: Enable HD Index stars to be added to the observing list.
        if (s->getHDIndex() != 0)
            finalObjectName = QString("HD %1").arg(QString::number(s->getHDIndex()));
    }
    else
        finalObjectName = translated ? o->translatedName() : o->name();

    return finalObjectName;
}
