/*
 * Copyright 2009- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

#include "ServerComInfoWidget.hpp"

#include <cmath>

#include <QAction>
#include <QHBoxLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QTimer>
#include <QToolButton>
#include <boost/current_function.hpp>

#include "ConnectState.hpp"
#include "FontMetrics.hpp"
#include "PropertyMapper.hpp"
#include "ServerHandler.hpp"
#include "TextFormat.hpp"
#include "UIDebug.hpp"
#include "UiLog.hpp"
#include "VNode.hpp"
#include "ViewerUtil.hpp"

QIcon* ServerRefreshInfoWidget::icon_ = nullptr;
QBrush ServerRefreshInfoWidget::buttonBgBrush_(QColor(229, 228, 227));
QBrush ServerRefreshInfoWidget::serverBgBrush_(QColor(241, 242, 243));
QPen ServerRefreshInfoWidget::borderPen_(QColor(197, 197, 197));
QPen ServerRefreshInfoWidget::buttonBorderPen_(QColor(167, 167, 167));
QPen ServerRefreshInfoWidget::disabledBorderPen_(QColor(182, 182, 182));
QBrush ServerRefreshInfoWidget::buttonBgHoverBrush_(QColor(249, 248, 248));
QPen ServerRefreshInfoWidget::buttonHoverPen_(QColor(160, 160, 160));
QBrush ServerRefreshInfoWidget::buttonBgRefreshBrush_(QColor(214, 227, 213));
QBrush ServerRefreshInfoWidget::periodBgBrush_(QColor(241, 242, 243));
QBrush ServerRefreshInfoWidget::progBrush_(QColor(117, 165, 230));
QBrush ServerRefreshInfoWidget::progBgBrush_(QColor(255, 255, 255));
QBrush ServerRefreshInfoWidget::lastBgBrush_(QColor(238, 238, 238));
QPen ServerRefreshInfoWidget::buttonRefreshPen_(QColor(79, 179, 100), 2);
QPen ServerRefreshInfoWidget::serverTextPen_(QColor(80, 80, 80));
QPen ServerRefreshInfoWidget::periodTextPen_(QColor(45, 45, 45));
QPen ServerRefreshInfoWidget::driftTextPen_(QColor(120, 120, 120));
QPen ServerRefreshInfoWidget::lastTextPen_(QColor(45, 45, 45));
QPen ServerRefreshInfoWidget::disabledTextPen_(QColor(180, 180, 180));

// #define _UI_SERVERCOMINFOWIDGET_DEBUG

#if 0
ServerRefreshInfoWidget::ServerRefreshInfoWidget(QAction* refreshAction,QWidget *parent) :
    QWidget(parent),
    refreshAction_(refreshAction)
{
    Q_ASSERT(refreshAction_);

    QHBoxLayout *hb=new QHBoxLayout(this);
    hb->setContentsMargins(0,0,0,0);
    hb->setSpacing(0);

    //QToolButton* refreshTb=new QToolButton(this);
   //refreshTb->setAutoRaise(true);
    //refreshTb->setDefaultAction(refreshAction_);
    //hb->addWidget(refreshTb);

    infoW_=new ServerComLineDisplay(this);
    hb->addWidget(infoW_);

    IconProvider::add(":/viewer/reload_one.svg","reload_one");


}
#endif

ServerRefreshInfoWidget::ServerRefreshInfoWidget(QAction* refreshAction, QWidget* parent)
    : QWidget(parent),
      refreshAction_(refreshAction),
      server_(nullptr),
      fontServer_(QFont()),
      fontPeriod_(QFont()),
      fontLast_(QFont()),
      fmServer_(QFont()),
      fmServerReal_(QFont()),
      fmPeriod_(QFont()),
      fmLast_(QFont()),
      periodTextWidth_(0),
      periodTextWidthMin_(0),
      periodDummyText_(" D=300s "),
      periodDummyFullText_(" D=300s d=99s"),
      currentComponent_(NoComponent),
      progRectHeight_(2),
      serverRectHeight_(10),
      serverYPadding_(2),
      prop_(nullptr),
      mode_(NoMode),
      noBlinkLimit_(15),
      hasInfo_(false),
      inRefresh_(true),
      userInitiatedRefresh_(false),
      showLastAutoRefresh_(true),
      total_(-1),
      period_(-1),
      toNext_(-1),
      drift_(-1),
      needBorder_(true) {
    Q_ASSERT(refreshAction_);

    // The icon for the round refresh button
    if (!icon_)
        icon_ = new QIcon(QPixmap(":/viewer/reload_green.svg"));

    // Init fonts
    fontServer_ = QFont();
    fontServer_.setPointSize(fontServer_.pointSize() - 1);
    fontServer_.setBold(true);
    fmServer_     = QFontMetrics(fontServer_);
    fmServerReal_ = FontMetrics(fontServer_);

    fontPeriod_ = QFont();
    fontPeriod_.setPointSize(fontPeriod_.pointSize() - 2);
    fmPeriod_ = QFontMetrics(fontPeriod_);

    fontLast_ = QFont();
    fontLast_.setPointSize(fontLast_.pointSize() - 2);
    fmLast_ = QFontMetrics(fontLast_);

    int w             = 200;
    serverRectHeight_ = fmServerReal_.realHeight() + 2 * serverYPadding_;
    int h             = serverRectHeight_ + 2 * 4;

    // timer
    timer_ = new QTimer(this);

    connect(timer_, SIGNAL(timeout()), this, SLOT(slotTimeOut()));

    // set size
    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
    setMinimumSize(w, h);

    buttonRect_    = QRect(1, 1, h - 2, h - 2);
    buttonRadius2_ = pow(buttonRect_.width() / 2, 2);

    adjustGeometry(false);

    // we need this for the mousemove event
    setMouseTracking(true);

    // properties to use
    std::vector<std::string> propVec;
    propVec.emplace_back("server.update.blinkUpdateButtonLimit");
    propVec.emplace_back("server.update.showLastRefreshTimeInAutoMode");
    prop_ = new PropertyMapper(propVec, this);
    updateSettings();
}

ServerRefreshInfoWidget::~ServerRefreshInfoWidget() {
    // Detach from the server
    if (server_) {
        server_->removeServerObserver(this);
        server_->removeServerComObserver(this);
    }
    delete prop_;
}

bool ServerRefreshInfoWidget::isActionEnabled() const {
    return refreshAction_->isEnabled();
}

void ServerRefreshInfoWidget::notifyChange(VProperty*) {
    updateSettings();
}

void ServerRefreshInfoWidget::updateSettings() {
    bool fetched = false;
    bool changed = false;
    if (VProperty* p = prop_->find("server.update.showLastRefreshTimeInAutoMode")) {
        bool v = p->value().toBool();
        if (showLastAutoRefresh_ != v) {
            showLastAutoRefresh_ = v;
            adjustGeometry(true);
            fetched = true;
            changed = true;
        }
    }

    if (VProperty* p = prop_->find("server.update.blinkUpdateButtonLimit")) {
        int v = p->value().toInt();
        if (noBlinkLimit_ != v) {
            noBlinkLimit_ = v;
            changed       = true;
        }
    }

    if (changed) {
        if (!fetched) {
            fetchInfo();
        }
        update();
    }
}

void ServerRefreshInfoWidget::setServer(ServerHandler* server) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
#endif

    if (server_ != server && server_) {
        server_->removeServerObserver(this);
        server_->removeServerComObserver(this);
    }

    server_ = server;

    if (server_) {
        server_->addServerObserver(this);
        server_->addServerComObserver(this);
    }
    else {
        hasInfo_ = false;
    }

    refreshAction_->setEnabled(true);
    inRefresh_            = false;
    userInitiatedRefresh_ = false;
    mode_                 = NoMode;

    // Cache some data
    serverName_.clear();
    serverText_.clear();
    if (server_) {
        serverName_ = QString::fromStdString(server_->name());
        serverText_ = " " + serverName_ + " ";
    }

    periodText_.clear();
    driftText_.clear();
    periodTextWidthMin_ = 0;
    periodTextWidth_    = 0;
    lastTextWidth_      = 0;

    if (server_ && server_->isEnabled()) {
        setEnabled(true);
    }
    else {
        refreshAction_->setEnabled(false);
        setEnabled(false);
    }

    // Adjust width + get info
    adjustGeometry(true);

    // rerender
    update();
}

//-------------------------------
// Server observer notifications
//-------------------------------

void ServerRefreshInfoWidget::notifyServerDelete(ServerHandler* server) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
#endif

    Q_ASSERT(server_ == server);
    if (server_ == server) {
        server_->removeServerObserver(this);
        server_->removeServerComObserver(this);
        server_ = nullptr;
        serverName_.clear();
        serverText_.clear();
        hasInfo_              = false;
        mode_                 = NoMode;
        inRefresh_            = false;
        userInitiatedRefresh_ = false;
        periodText_.clear();
        driftText_.clear();
        periodTextWidthMin_ = 0;
        periodTextWidth_    = 0;
        lastTextWidth_      = 0;

        refreshAction_->setEnabled(false);

        // get info and rerender
        reloadAll();

        adjustGeometry(false);
    }
}

// While the server is being reloaded the refresh button is disabled. It must be followed by a
// notifyEndServerScan() call!
void ServerRefreshInfoWidget::notifyBeginServerClear(ServerHandler* server) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
#endif
    Q_ASSERT(server_ == server);
    timer_->stop();
    refreshAction_->setEnabled(false);
    hasInfo_              = false;
    inRefresh_            = false;
    userInitiatedRefresh_ = false;
    mode_                 = NoMode;
    periodText_.clear();
    driftText_.clear();
    periodTextWidthMin_ = 0;
    periodTextWidth_    = 0;
    lastTextWidth_      = 0;

    update();
}

// The server has been reloaded. We must get the current state.
void ServerRefreshInfoWidget::notifyEndServerScan(ServerHandler* server) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
#endif
    Q_ASSERT(server_ == server);

    if (server_ && server_->isEnabled()) {
        refreshAction_->setEnabled(true);
        setEnabled(true);
    }
    else {
        refreshAction_->setEnabled(false);
        setEnabled(false);
    }

    reloadAll();
}

void ServerRefreshInfoWidget::notifyServerConnectState(ServerHandler* server) {
    Q_ASSERT(server_ == server);

    if (server_ && server_->isEnabled()) {
        setEnabled(true);
    }
    else {
        refreshAction_->setEnabled(false);
        setEnabled(false);
    }
}

void ServerRefreshInfoWidget::notifyServerRenamed(ServerHandler* server, const std::string& /*oldName*/) {
    setServer(server);
}

void ServerRefreshInfoWidget::notifyServerActivityChanged(ServerHandler* /*server*/) {
}

//-----------------------------------
// Server com observer notifications
//-----------------------------------

void ServerRefreshInfoWidget::notifyRefreshTimerStarted(ServerHandler* server) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
#endif
    Q_ASSERT(server_ == server);
    reloadAll(); // get info and rerender
}

void ServerRefreshInfoWidget::notifyRefreshTimerStopped(ServerHandler* server) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
#endif
    Q_ASSERT(server_ == server);
    reloadAll(); // get info and rerender
}

void ServerRefreshInfoWidget::notifyRefreshTimerChanged(ServerHandler* server) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
    printStatus();
#endif
    Q_ASSERT(server_ == server);

    // if we are in refresh we do not want to fetch any new information unless we are in
    // NoMode
    if (!inRefresh_ || mode_ == NoMode) {
        reloadAll(); // get info and rerender
    }
}

// While the refresh is being executed the the refresh button
void ServerRefreshInfoWidget::notifyRefreshScheduled(ServerHandler* server) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
#endif
    Q_ASSERT(server_ == server);
    inRefresh_ = true;
    inRefreshElapsed_.restart();
    if (mode_ == NormalMode) {
        timer_->stop();

#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
        printStatus();
#endif
        // redraw
        update();
    }
}

void ServerRefreshInfoWidget::notifyRefreshFinished(ServerHandler* server) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
#endif
    Q_ASSERT(server_ == server);
    int elapsed = inRefreshElapsed_.elapsed();

    if ((mode_ == NormalMode) && elapsed < 450) {
        Q_ASSERT(500 - elapsed > 0);
        // We keep the button in inRefresh state for 0.5 sec (the timer is stopped now!!)
        QTimer::singleShot(500 - elapsed, this, SLOT(slotTimeOutRefreshFinished()));
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
        printStatus();
#endif
    }
    else {
        inRefresh_            = false;
        userInitiatedRefresh_ = false;
    }
}

void ServerRefreshInfoWidget::reloadAll() {
    fetchInfo(); // get info
    update();    // renrender
}

void ServerRefreshInfoWidget::slotTimeOut() {
    reloadAll();
}

void ServerRefreshInfoWidget::slotTimeOutRefreshFinished() {
    inRefresh_            = false;
    userInitiatedRefresh_ = false;
    reloadAll();
}

void ServerRefreshInfoWidget::fetchInfo() {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
    printStatus();
#endif

    if (!server_ || !isEnabled()) {
        mode_    = NoMode;
        hasInfo_ = false;
        timer_->stop();
    }
    else {
        QDateTime currentTime = QDateTime::currentDateTime();
        bool v                = server_->updateInfo(period_, total_, drift_, toNext_);

        bool geoUpdateNeeded = (v != hasInfo_);
        hasInfo_             = v;

#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
        UiLog().dbg() << " period=" << period_ << " total=" << total_ << " toNext=" << toNext_;
#endif

        // the server has an automatic update
        if (hasInfo_) {
            lastRefresh_ =
                server_->lastRefresh().time().toString(); // currentTime.addSecs(-total_+toNext_).time().toString();
            nextRefresh_ = currentTime.addSecs(toNext_).time().toString();

            mode_ = NormalMode;
            if (!inRefresh_)
                refreshAction_->setEnabled(true);
        }
        // the server's automatic refresh is switched off
        else {
            lastRefresh_ = server_->lastRefresh().time().toString();
            mode_        = ManualMode;
            if (!inRefresh_)
                refreshAction_->setEnabled(true);
        }

        // Determine period text
        determinePeriodText();

        if (geoUpdateNeeded || periodTextWidthAboutToChange())
            adjustGeometry(false);

        // if(currentComponent_ == TextComponent)
        adjustToolTip();

        adjustTimer(toNext_);
    }

#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    printStatus();
#endif
}

// Adjust timer interval
void ServerRefreshInfoWidget::adjustTimer(int toNext) {
    // we stop the timer when:
    //  -the action is disabled
    //  -we are in fast mode
    //  -the countdown is not shown
    if (!refreshAction_->isEnabled() || mode_ == NoMode || mode_ == ManualMode) {
        timer_->stop();
    }
    else if (hasInfo_) {
        if (total_ <= 1) {
            timer_->stop();
            return;
        }
        else if (total_ == 2)
            timer_->setInterval(750);
        else {
            Q_ASSERT(total_ > 0);
            int progWidth = periodTextWidth_;
            Q_ASSERT(progWidth > 0);
            float secPerPix = static_cast<float>(total_) / static_cast<float>(progWidth);
            float r         = ceil(secPerPix);
            Q_ASSERT(r >= 1.);

            if (toNext > 135) {
                if (r > 30)
                    r = 60;
                else if (r > 15)
                    r = 30;
                else
                    r = 15;
            }
            else if (toNext > 60) {
                if (r < 10)
                    r = 10;
            }
            else if (toNext > 30) {
                if (r < 5)
                    r = 5;
            }
            else if (toNext > 5) {
                if (r < 2.5)
                    r = 2.5;
            }
            else {
                r = 1;
            }

            timer_->setInterval(static_cast<int>(r * 1000.));
        }

        if (!timer_->isActive())
            timer_->start();
    }
    else {
        timer_->stop();
    }
}

// Check if a point is inside the (round) button
bool ServerRefreshInfoWidget::isInButton(const QPoint& pos) const {
    QPoint d = pos - buttonRect_.center();
    return d.x() * d.x() + d.y() * d.y() <= buttonRadius2_;
}

bool ServerRefreshInfoWidget::isInText(const QPoint& pos) const {
    return (pos.x() > buttonRect_.right());
}

void ServerRefreshInfoWidget::resizeEvent(QResizeEvent*) {
    buttonRect_    = QRect(1, 1, height() - 2, height() - 2);
    buttonRadius2_ = pow(buttonRect_.width() / 2, 2);
}

void ServerRefreshInfoWidget::mouseDoubleClickEvent(QMouseEvent* event) {
    if (server_ && !isInButton(event->pos())) {
        Q_EMIT serverSettingsEditRequested(server_);
    }
}

void ServerRefreshInfoWidget::mousePressEvent(QMouseEvent* event) {
    if (!refreshAction_->isEnabled())
        return;

    // We are in the button
    if (isInButton(event->pos())) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
//        UiLog().dbg() << "pressed";
#endif
        if (currentComponent_ != ButtonComponent) {
            currentComponent_ = ButtonComponent;
        }
        userInitiatedRefresh_ = true;
        refreshAction_->trigger();
    }
}

void ServerRefreshInfoWidget::mouseMoveEvent(QMouseEvent* event) {
    // We are in the button
    if (isInButton(event->pos())) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
//        UiLog().dbg() << "inButton";
#endif
        // we just entered the button
        if (currentComponent_ != ButtonComponent) {
            currentComponent_ = ButtonComponent;
            adjustToolTip();
            if (refreshAction_->isEnabled()) {
                update(); // rerender
            }
        }
    }
    // We are in the progress part
    else if (isInText(event->pos())) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
//        UiLog().dbg() << "inText";
#endif
        // we came from the button
        if (currentComponent_ == ButtonComponent) {
            currentComponent_ = TextComponent;
            adjustToolTip();
            if (refreshAction_->isEnabled()) {
                update(); // rerender
            }
        }
        // we came from outside
        else if (currentComponent_ != TextComponent) {
            currentComponent_ = TextComponent;
            adjustToolTip();
        }
    }
}

void ServerRefreshInfoWidget::leaveEvent(QEvent*) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    // UI_FUNCTION_LOG
#endif
    currentComponent_ = NoComponent;
    update(); // rerender
}

void ServerRefreshInfoWidget::paintEvent(QPaintEvent*) {
    QPainter painter(this);
    drawProgress(&painter);
    drawButton(&painter);
}

QString ServerRefreshInfoWidget::formatPeriodTime(int timeInSec) const {
    int h = timeInSec / 3600;
    int r = timeInSec % 3600;
    int m = r / 60;
    int s = r % 60;

    QTime t(h, m, s);
    if (h > 0)
        return QString::number(h) + QString(":%1h").arg(m, 2, 10, QChar('0'));
    else if (m >= 5)
        return QString::number(m) + QString(":%1s").arg(s, 2, 10, QChar('0'));
    else if (m > 0)
        return QString::number(m * 60 + s) + "s";
    else
        return QString::number(s) + "s";

    return QString();
}

void ServerRefreshInfoWidget::determinePeriodText() {
    periodText_.clear();
    driftText_.clear();
    if (hasInfo_) {
        // Unicode 916=Greek capital delta
        periodText_ = QString(" ") + QChar(916) + QString("=") + formatPeriodTime(total_) + " ";
        if (drift_ > 0) {
            driftText_ = "d=" + formatPeriodTime(total_ - period_) + " ";
        }
    }
}

QString ServerRefreshInfoWidget::fullPeriodText() const {
    return periodText_ + driftText_;
}

int ServerRefreshInfoWidget::determinePeriodTextWidthMin() const {
    QString s = periodDummyText_;
    if (hasInfo_ && drift_ > 0) {
        s = periodDummyFullText_;
    }
    return ViewerUtil::textWidth(fmPeriod_, s);
}

// Indicate if the full period text's size will change in such a way that the
// geometry needs to be adjusted
bool ServerRefreshInfoWidget::periodTextWidthAboutToChange() const {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
#endif

    int mval = determinePeriodTextWidthMin();

    QString pt = fullPeriodText();
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UiLog().dbg() << " full=" << pt << " pt.size=" << pt.size() << " mval=" << mval
                  << " periodTextWidthMin=" << periodTextWidthMin_ << " periodTextSize " << periodTextWidth_;
#endif
    bool changed = false;
    if (periodTextWidthMin_ == mval) {
        int w = ViewerUtil::textWidth(fmPeriod_, pt);
        if (w > periodTextWidth_) {
            changed = w > periodTextWidthMin_;
        }
        else if (w < periodTextWidth_) {
            changed = periodTextWidth_ >= periodTextWidthMin_;
        }
    }
    else {
        changed = true;
    }
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UiLog().dbg() << " changed=" << changed;
#endif
    return changed;
}

void ServerRefreshInfoWidget::adjustGeometry(bool doFetchInfo) {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
    UI_FUNCTION_LOG
#endif

    if (server_) {
        // timeTextLen_=qMax(fmTime_.width(" <9:59m"),fmUpdate_.width("updating"));

        // Define server rect
        int h            = serverRectHeight_; // fmServer_.height()+progRectHeight_; //;2*yPadding;
        int currentRight = 0;

        serverRect_  = QRect(buttonRect_.center().x() + 4,
                            (height() - h) / 2,
                            buttonRect_.width() / 2 - 4 + 4 + ViewerUtil::textWidth(fmServer_, serverText_),
                            h);
        currentRight = serverRect_.x() + serverRect_.width();

        if (doFetchInfo)
            fetchInfo();

        // Determine the minimum size for the period text
        periodTextWidthMin_ = determinePeriodTextWidthMin();

        // Compute physical width of the period text
        QString pt = fullPeriodText();
        int w      = ViewerUtil::textWidth(fmPeriod_, pt);
        if (w <= periodTextWidthMin_) {
            periodTextWidth_ = periodTextWidthMin_;
        }
        else {
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
            UiLog().dbg() << " width changed before=" << periodTextWidth_;
#endif
            periodTextWidth_ = w;
#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG
            UiLog().dbg() << " width changed after=" << periodTextWidth_;
#endif
        }
        periodRect_ = QRect();
        progRect_   = QRect();
        lastRect_   = QRect();

        if (hasInfo_) {
            periodRect_ = serverRect_;
            periodRect_.setX(serverRect_.x() + serverRect_.width());
            periodRect_.setWidth(periodTextWidth_);
            // periodRect_.setHeight(fmPeriod_.height()-2);
            periodRect_.setHeight(serverRect_.height() - progRectHeight_);

            currentRight += periodRect_.width();

            progRect_ = periodRect_;
            progRect_.setY(periodRect_.y() + periodRect_.height());
            progRect_.setHeight(serverRect_.height() - periodRect_.height());
        }

        lastTextWidth_ = 0;
        if ((hasInfo_ && showLastAutoRefresh_) || !hasInfo_) {
            // Compute physical width of the last refresh text
            lastTextWidth_ = ViewerUtil::textWidth(fmLast_, " last: 22:22:22 ");

            lastRect_ = QRect(currentRight, serverRect_.y(), lastTextWidth_, h);
            currentRight += lastRect_.width() + 6;
        }
        else
            currentRight += 6;

        setFixedWidth(currentRight);
    }
    else {
        periodTextWidthMin_ = 0;
        periodTextWidth_    = 0;
        lastTextWidth_      = 0;
        setFixedWidth(buttonRect_.x() + buttonRect_.width() + 4);
    }
}

void ServerRefreshInfoWidget::drawButton(QPainter* painter) {
    painter->setRenderHint(QPainter::Antialiasing, true);

    if (server_ && isEnabled()) {
        // blink
        if (inRefresh_ &&
            ((mode_ == NormalMode && period_ >= noBlinkLimit_) || userInitiatedRefresh_ || mode_ == ManualMode)) {
            painter->setBrush(buttonBgRefreshBrush_);
            painter->setPen(buttonRefreshPen_);
        }
        else {
            painter->setBrush((currentComponent_ == ButtonComponent) ? buttonBgHoverBrush_ : buttonBgBrush_);

            if (!refreshAction_->isEnabled())
                painter->setPen(disabledBorderPen_);
            else
                painter->setPen((currentComponent_ == ButtonComponent) ? buttonHoverPen_ : buttonBorderPen_);
        }
    }
    else {
        painter->setBrush(buttonBgBrush_);
        painter->setPen(buttonBorderPen_);
    }

    // The filled circle
    painter->drawEllipse(buttonRect_);

    // The reload icon
    QRect r1 = buttonRect_.adjusted(2, 2, -2, -2);
    QRect r2 = r1.adjusted(3, 3, -3, -3);
    QPixmap pix =
        icon_->pixmap(QSize(r2.width(), r2.width()), refreshAction_->isEnabled() ? QIcon::Normal : QIcon::Disabled);
    painter->drawPixmap(r2, pix);
}

void ServerRefreshInfoWidget::drawProgress(QPainter* painter) {
    if (!server_)
        return;

    // server bg
    painter->setBrush(serverBgBrush_);
    painter->drawRect(serverRect_);

    if (needBorder_) {
        painter->setBrush(Qt::NoBrush);
        painter->setPen((refreshAction_->isEnabled()) ? borderPen_ : disabledBorderPen_);
        painter->drawRect(serverRect_);
    }

    // Server text
    QRect serverTextRect = serverRect_.adjusted(buttonRect_.width() / 2 - 4 + 4, 0, 0, 0);
    painter->setFont(fontServer_);

    painter->setPen((refreshAction_->isEnabled()) ? serverTextPen_ : disabledTextPen_);
    painter->drawText(serverTextRect, Qt::AlignHCenter | Qt::AlignVCenter, serverText_);

    if (!isEnabled()) {
        if (showLastAutoRefresh_) {
            painter->setBrush(lastBgBrush_);
            painter->setPen((refreshAction_->isEnabled()) ? borderPen_ : disabledBorderPen_);
            painter->drawRect(lastRect_);
        }
        return;
    }

    // The time rects and texts
    if (hasInfo_) {
        // backgrounds
        painter->setPen(Qt::NoPen);
        painter->setBrush(periodBgBrush_);
        painter->drawRect(periodRect_);
        painter->setBrush(progBgBrush_);
        painter->drawRect(progRect_);

        // border
        if (needBorder_) {
            painter->setPen((refreshAction_->isEnabled()) ? borderPen_ : disabledBorderPen_);
            painter->setBrush(Qt::NoBrush);
            painter->drawRect(periodRect_.adjusted(0, 0, 0, progRect_.height()));
        }

        painter->setFont(fontPeriod_);

        // period + drift text
        if (drift_ > 0) {
            int tw   = ViewerUtil::textWidth(fmPeriod_, fullPeriodText());
            int w    = periodRect_.width();
            QRect rr = periodRect_.adjusted((w - tw) / 2, 0, -(w - tw) / 2, 0);

            painter->setPen(periodTextPen_);
            painter->drawText(rr, Qt::AlignLeft | Qt::AlignVCenter, periodText_);
            painter->setPen(driftTextPen_);
            painter->drawText(rr, Qt::AlignRight | Qt::AlignVCenter, driftText_);
        }
        else {
            painter->setPen(periodTextPen_);
            painter->drawText(periodRect_, Qt::AlignHCenter | Qt::AlignVCenter, periodText_);
        }

        // Progress
        float progress;
        QRect actProgRect = progRect_;

        if (total_ < 2 || inRefresh_)
            progress = 1;
        else {
            UI_ASSERT(total_ != 0, "total_=" << total_);
            progress = (static_cast<float>(total_ - toNext_) / static_cast<float>(total_));
            UI_ASSERT(progress >= 0. && progress <= 1.0001, "progress=" << progress);
            if (progress >= 1.)
                progress = 1;

            auto progressW = static_cast<int>(static_cast<float>(actProgRect.width()) * progress);
            if (progressW < 0)
                progressW = 0;
            else if (progressW > progRect_.width())
                progressW = progRect_.width();
            actProgRect.setWidth(progressW);
        }

        painter->setPen((refreshAction_->isEnabled()) ? borderPen_ : disabledBorderPen_);
        painter->drawLine(progRect_.topLeft(), progRect_.topRight());

        painter->fillRect(actProgRect, progBrush_);
    }

    // last refresh time
    if ((hasInfo_ && showLastAutoRefresh_) || !hasInfo_) {
        painter->setBrush(lastBgBrush_);
        painter->setPen((refreshAction_->isEnabled()) ? borderPen_ : disabledBorderPen_);
        painter->drawRect(lastRect_);

        painter->setFont(fontLast_);
        painter->setPen(lastTextPen_);
        painter->drawText(lastRect_, Qt::AlignHCenter | Qt::AlignVCenter, "last: " + lastRefresh_);
    }
}

void ServerRefreshInfoWidget::adjustToolTip() {
    QString txt;

    /*
    if(mode_ == ContMode)
    {
        Q_ASSERT(server_);
        Q_ASSERT(hasInfo_);
        txt=tr("Refresh period is too short! Manual refreshing is disabled for server <b>") +
                serverName_ +
                "</b><br>--------------------------------------------"+
                tr("<br><b>Refresh period:</b> ") + QString::number(total_) + "s" +
                " (base=" + QString::number(period_) + "s" + ",drifted=" + QString::number(total_-period_) +"s)";

    }
    */
    if (true) {
        if (currentComponent_ == ButtonComponent) {
            if (!server_) {
                txt = tr("Refresh <b>selected</b> server ");
            }
            else {
                txt = tr("Refresh server <b>") + serverName_ + "</b> ";
            }

            txt += Viewer::formatShortCut(refreshAction_);
        }

        if (hasInfo_) {
            Q_ASSERT(server_);
            if (!txt.isEmpty())
                txt += "<br>--------------------------------------------";
            else
                txt += "<b>Server:</b> " + serverName_;

            txt += "<br><b>Last refresh:</b> " + lastRefresh_ + "<br><b>Next refresh:</b> " + nextRefresh_ +
                   "<br><b>Total refresh period:</b> " + QString::number(total_) + "s" +
                   " (base=" + QString::number(period_) + "s" + ",drifted=" + QString::number(total_ - period_) + "s)";

            /*if(drift_ > 0)
            {
                txt+="<br>--------------------------------------------<br>";
                txt+="When <b>drift</b> is enabled the server refresh period is increased at every automatic refresh
            until \ the maximum period is reached. The drift is reset to zero when the user interacts with the server.";

            }*/
        }
        else if (server_) {
            if (!txt.isEmpty())
                txt += "<br>--------------------------------------------";
            else
                txt += "<b>Server:</b> " + serverName_;

            txt += "<br><b>Last refresh:</b> " + lastRefresh_ + "<br>" + "Automatic refresh is disabled!";
        }

        if (currentComponent_ != ButtonComponent && server_) {
            txt += "<br>" + Viewer::formatShortCut("Double click to change refresh settings for server");
        }
    }

    setToolTip(txt);
}

#if 0
QString ServerRefreshInfoWidget::formatTime(int timeInSec) const
{
    int h=timeInSec/3600;
    int r=timeInSec%3600;
    int m=r/60;
    int s=r%60;

    QTime t(h,m,s);
    if(h > 0)
       return QString::number(h) + "h";
    else if(m > 2)
    {
        if(s >= 30)
            return QString::number(m+1) + "m";
        else
            return QString::number(m) + "m";
    }
    else if(m >= 1)
    {
        if(s >= 45)
            return "1:45m";
        else if(s >= 30)
            return "1:30m";
        else if(s >= 15)
            return "1:15m";
        else
           return "1m";
    }
    else if(s > 50)
       return "<1m";
    else if(s > 45)
       return "<50s";
    else if(s > 40)
        return "<45s";
    else if(s > 35)
       return "<40s";
    else if(s > 30)
       return "<35s";
    else if(s > 25)
       return "<30s";
    else if(s > 20)
        return "<25s";
    else if(s > 15)
       return "<20s";
    else if(s > 10)
       return "<15s";
    else if(s > 5)
        return "<10s";
    else
        return "<5s"; //return QString("%1s").arg(s, 2, 10, QChar('0'));

    return QString();
}
#endif

void ServerRefreshInfoWidget::printStatus() const {
    UiLog().dbg() << "  server=" << server_ << " action=" << refreshAction_->isEnabled() << " hasInfo=" << hasInfo_
                  << " mode=" << mode_ << " inRefresh=" << inRefresh_ << " timer=" << timer_->isActive()
                  << " timeout=" << timer_->interval() / 1000. << "s";
}

ServerComActivityLine::ServerComActivityLine(QWidget* parent) : QWidget(parent), font_(QFont()), fm_(font_) {
    font_.setPointSize(font_.pointSize() - 1);
    fm_ = QFontMetrics(font_);

    int width_  = 200;
    int height_ = fm_.height() + 4 + 6;

    timer_ = new QTimer(this);

    connect(timer_, SIGNAL(timeout()), this, SLOT(update()));

    timer_->setInterval(1000);
    timer_->start();

    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
    setMinimumSize(width_, height_);
}

void ServerComActivityLine::setServer(ServerHandler* server) {
    server_ = server;
    if (server_ && server_->isEnabled()) {
        setEnabled(true);
    }
    else {
        setEnabled(false);
    }
}

void ServerComActivityLine::paintEvent(QPaintEvent*) {
#if 0
    if(!server_)
        return;

    int currentRight=0;
    int offset=4;
    int yPadding=2;

    int period,total,drift,toNext;
    bool hasUpdate=server_->updateInfo(period,total,drift,toNext);
    bool hasDrift=(hasUpdate && drift > 0);
    int h=height()-2*yPadding;
    int progHeight=h;
    int driftHeight=0;


    if(hasDrift)
    {
        progHeight=fm_.height()+2;
        driftHeight=h-progHeight;
    }

    //progress rect - with the server name in it
    QRect progRect=QRect(offset,yPadding,
                       fm_.width("ABCDEABDCEABCD"),
                       progHeight);

    QString progText=fm_.elidedText(QString::fromStdString(server_->name()),
                               Qt::ElideRight,progRect.width());

    //drif rect
    QRect driftRect;
    if(hasDrift)
    {
        driftRect=progRect;
        driftRect.setY(yPadding+progHeight);
        driftRect.setHeight(driftHeight);
    }

   //toNext rect
   currentRight=progRect.x()+progRect.width()+offset;

   QString toNextText=QString::number(total) + "s";
   if(hasUpdate)
       toNextText=formatTime(toNext);

   QRect toNextRect=progRect;
   toNextRect.setX(currentRight);
   toNextRect.setWidth(fm_.wi

#endif
}
