/***************************************************************************
                          avtplotwidget.cpp  -  description
                             -------------------
    begin                : Sat Nov 10 2007
    copyright            : (C) 2007 by Jason Harris
    email                : kstars@30doradus.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 "avtplotwidget.h"

#include "kstarsdata.h"
#include "Options.h"

#include <QWidget>
#include <QMouseEvent>
#include <QPainter>
#include <QTime>
#include <QLinearGradient>

#include <KLocalizedString>
#include <KPlotting/KPlotObject>
#include <QDebug>

AVTPlotWidget::AVTPlotWidget(QWidget *parent) : KPlotWidget(parent), geo(nullptr)
{
    setAntialiasing(true);

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

    MousePoint = QPoint(-1, -1);
}

void AVTPlotWidget::mousePressEvent(QMouseEvent *e)
{
    mouseMoveEvent(e);
}

void AVTPlotWidget::mouseDoubleClickEvent(QMouseEvent *)
{
    MousePoint = QPoint(-1, -1);
    update();
}

void AVTPlotWidget::mouseMoveEvent(QMouseEvent *e)
{
    QRect checkRect(leftPadding(), topPadding(), pixRect().width(), pixRect().height());
    int Xcursor = e->x();
    int Ycursor = e->y();

    if (!checkRect.contains(e->x(), e->y()))
    {
        if (e->x() < checkRect.left())
            Xcursor = checkRect.left();
        if (e->x() > checkRect.right())
            Xcursor = checkRect.right();
        if (e->y() < checkRect.top())
            Ycursor = checkRect.top();
        if (e->y() > checkRect.bottom())
            Ycursor = checkRect.bottom();
    }

    Xcursor -= leftPadding();
    Ycursor -= topPadding();

    MousePoint = QPoint(Xcursor, Ycursor);
    update();
}

void AVTPlotWidget::paintEvent(QPaintEvent *e)
{
    Q_UNUSED(e)

    QPainter p;

    p.begin(this);
    p.setRenderHint(QPainter::Antialiasing, antialiasing());
    p.fillRect(rect(), backgroundColor());
    p.translate(leftPadding(), topPadding());

    setPixRect();
    p.setClipRect(pixRect());
    p.setClipping(true);

    int pW = pixRect().width();
    int pH = pixRect().height();

    QColor SkyColor(0, 100, 200);
    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
        {
            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);
        }
    }
    //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);
        }
    }

    //draw ground
    p.fillRect(0, int(0.5 * pH), pW, int(0.5 * pH),
               KStarsData::Instance()->colorScheme()->colorNamed(
                   "HorzColor")); // asimha changed to use color from scheme. Formerly was QColor( "#002200" )

    foreach (KPlotObject *po, plotObjects())
    {
        po->draw(&p, this);
    }

    p.setClipping(false);
    drawAxes(&p);

    //Add vertical line indicating "now"
    QFont smallFont = p.font();
    smallFont.setPointSize(smallFont.pointSize()); // wat?
    if (geo)
    {
        QTime t = geo->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);

        //Label this vertical line with the current time
        p.save();
        p.setFont(smallFont);
        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();
    }

    //Draw crosshairs at clicked position
    if (MousePoint.x() > 0)
    {
        p.setPen(QPen(QBrush("gold"), 1.0, Qt::SolidLine));
        p.drawLine(QLineF(MousePoint.x() + 0.5, 0.5, MousePoint.x() + 0.5, pixRect().height() - 0.5));
        p.drawLine(QLineF(0.5, MousePoint.y() + 0.5, pixRect().width() - 0.5, MousePoint.y() + 0.5));

        //Label each crosshair line (time and altitude)
        p.setFont(smallFont);
        double a = (pH - MousePoint.y()) * 180.0 / pH - 90.0;
        p.drawText(20, MousePoint.y() + 10, QString::number(a, 'f', 2) + QChar(176));

        double h = MousePoint.x() * 24.0 / pW - 12.0;
        if (h < 0.0)
            h += 24.0;
        QTime t = QTime(int(h), int(60. * (h - int(h))));
        p.save();
        p.translate(MousePoint.x() + 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();
}

void AVTPlotWidget::setDawnDuskTimes(double da, double du)
{
    Dawn = da;
    Dusk = du;
    update(); // fixme: should we always be calling update? It's probably cheap enough that we can.
}

void AVTPlotWidget::setMinMaxSunAlt(double min, double max)
{
    SunMinAlt = min;
    SunMaxAlt = max;
    update();
}

void AVTPlotWidget::setSunRiseSetTimes(double sr, double ss)
{
    SunRise = sr;
    SunSet  = ss;
    update();
}

void AVTPlotWidget::setMoonRiseSetTimes(double mr, double ms)
{
    MoonRise = mr;
    MoonSet  = ms;
    update();
}

void AVTPlotWidget::setMoonIllum(double mi)
{
    MoonIllum = mi;
    update();
}
