/*
   Bacula(R) - The Network Backup Solution

   Copyright (C) 2000-2017 Kern Sibbald

   The original author of Bacula is Kern Sibbald, with contributions
   from many others, a complete list can be found in the file AUTHORS.

   You may use this file and others of this release according to the
   license defined in the LICENSE file, which includes the Affero General
   Public License, v3.0 ("AGPLv3") and some additional permissions and
   terms pursuant to its AGPLv3 Section 7.

   This notice must be preserved when any source code is
   conveyed and/or propagated.

   Bacula(R) is a registered trademark of Kern Sibbald.
*/

#ifndef TRAYUI_H
#define TRAYUI_H


#include "common.h"
#include <QAction>
#include <QApplication>
#include <QButtonGroup>
#include <QDialogButtonBox>
#include <QHeaderView>
#include <QMainWindow>
#include <QPlainTextEdit>
#include <QStatusBar>
#include <QTabWidget>
#include <QVBoxLayout>
#include <QWidget>
#include <QLabel>
#include <QSpinBox>
#include <QMenu>
#include <QIcon>
#include <QSystemTrayIcon>
#include <QTimer>
#include <QDebug>
#include <QMessageBox>
#include <QFont>
#include <QInputDialog>

#include "fdstatus.h"
#include "sdstatus.h"
#include "dirstatus.h"
#include "conf.h"
#include "runjob.h"
#include "restorewizard.h"

void display_error(const char *fmt, ...);

int tls_pem_callback(char *buf, int size, const void *userdata);

class TrayUI: public QMainWindow
{
   Q_OBJECT

public:
    QWidget *centralwidget;
    QTabWidget *tabWidget;
    QStatusBar *statusbar;

    QSystemTrayIcon *tray;
    QSpinBox *spinRefresh;
    QTimer *timer;
    bool    have_systray;
    RestoreWizard *restorewiz;

    TrayUI():
    QMainWindow(),
       tabWidget(NULL),
       statusbar(NULL),
       tray(NULL),
       spinRefresh(NULL),
       timer(NULL),
       have_systray(QSystemTrayIcon::isSystemTrayAvailable()),
       restorewiz(NULL)
       {
       }

    ~TrayUI() {
    }
    void addTab(RESMON *r)
    {
       QWidget *tab;
       QString t = QString(r->hdr.name);
       if (r->tls_enable) {
          char buf[512];
          /* Generate passphrase prompt */
          bsnprintf(buf, sizeof(buf), "Passphrase for \"%s\" TLS private key: ", r->hdr.name);

          /* Initialize TLS context:
           * Args: CA certfile, CA certdir, Certfile, Keyfile,
           * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
           */
          r->tls_ctx = new_tls_context(r->tls_ca_certfile,
                                       r->tls_ca_certdir, r->tls_certfile,
                                       r->tls_keyfile, tls_pem_callback, &buf, NULL, true);

          if (!r->tls_ctx) {
             display_error(_("Failed to initialize TLS context for \"%s\".\n"), r->hdr.name);
          }
       }
       switch(r->type) {
       case R_CLIENT:
          tab = new FDStatus(r);
          break;
       case R_STORAGE:
          tab = new SDStatus(r);
          break;
       case R_DIRECTOR:
          tab = new DIRStatus(r);
          break;
       default:
          return;
       }
       tabWidget->setUpdatesEnabled(false);
       tabWidget->addTab(tab, t);
       tabWidget->setUpdatesEnabled(true);       
    }
    void clearTabs()
    {
       tabWidget->setUpdatesEnabled(false);
       for(int i = tabWidget->count() - 1; i >= 0; i--) {
          QWidget *w = tabWidget->widget(i);
          tabWidget->removeTab(i);
          delete w;
       }
       tabWidget->setUpdatesEnabled(true);
       tabWidget->update();
    }
    void startTimer()
    {
       if (!timer) {
          timer = new QTimer(this);
          connect(timer, SIGNAL(timeout()), this, SLOT(refresh_screen()));
       }
       timer->start(spinRefresh->value()*1000);
    }
    void setupUi(QMainWindow *TrayMonitor, MONITOR *mon)
    {
        QPushButton *menubp = NULL;
        timer = NULL;
        if (TrayMonitor->objectName().isEmpty())
            TrayMonitor->setObjectName(QString::fromUtf8("TrayMonitor"));
        TrayMonitor->setWindowIcon(QIcon(":/images/cartridge1.png")); 
        TrayMonitor->resize(789, 595);
        centralwidget = new QWidget(TrayMonitor);
        centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
        QVBoxLayout *verticalLayout = new QVBoxLayout(centralwidget);
        verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
        tabWidget = new QTabWidget(centralwidget);
        tabWidget->setObjectName(QString::fromUtf8("tabWidget"));
        tabWidget->setTabPosition(QTabWidget::North);
        tabWidget->setTabShape(QTabWidget::Rounded);
        tabWidget->setTabsClosable(false);
        verticalLayout->addWidget(tabWidget);

        QDialogButtonBox *buttonBox = new QDialogButtonBox(centralwidget);
        buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
        if (have_systray) {
           buttonBox->setStandardButtons(QDialogButtonBox::Close);
           connect(buttonBox, SIGNAL(rejected()), this, SLOT(cb_show()));
        } else {
           /* Here we can display something else, now it's just a simple menu */
           menubp = new QPushButton(tr("&Options"));
           buttonBox->addButton(menubp, QDialogButtonBox::ActionRole);
        }
        TrayMonitor->setCentralWidget(centralwidget);
        statusbar = new QStatusBar(TrayMonitor);
        statusbar->setObjectName(QString::fromUtf8("statusbar"));
        TrayMonitor->setStatusBar(statusbar);

        QHBoxLayout *hLayout = new QHBoxLayout();
        QLabel *refreshlabel = new QLabel(centralwidget);
        refreshlabel->setText("Refresh:");
        hLayout->addWidget(refreshlabel);
        spinRefresh = new QSpinBox(centralwidget);
        QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
        sizePolicy.setHorizontalStretch(0);
        sizePolicy.setVerticalStretch(0);
        sizePolicy.setHeightForWidth(spinRefresh->sizePolicy().hasHeightForWidth());
        spinRefresh->setSizePolicy(sizePolicy);
        spinRefresh->setMinimum(1);
        spinRefresh->setMaximum(600);
        spinRefresh->setSingleStep(10);
        spinRefresh->setValue(mon?mon->RefreshInterval:60);
        hLayout->addWidget(spinRefresh);
        hLayout->addWidget(buttonBox);

        verticalLayout->addLayout(hLayout);
        //QSystemTrayIcon::isSystemTrayAvailable

        tray = new QSystemTrayIcon(TrayMonitor);
        QMenu* stmenu = new QMenu(TrayMonitor);

#if QT_VERSION >= 0x050000
        QAction *actShow = new QAction(QApplication::translate("TrayMonitor",
                               "Display", 0),TrayMonitor);
        QAction* actQuit = new QAction(QApplication::translate("TrayMonitor",
                               "Quit", 0),TrayMonitor);
        QAction* actAbout = new QAction(QApplication::translate("TrayMonitor",
                               "About", 0),TrayMonitor);
        QAction* actRun = new QAction(QApplication::translate("TrayMonitor",
                               "Run...", 0),TrayMonitor);
        QAction* actRes = new QAction(QApplication::translate("TrayMonitor",
                              "Restore...", 0),TrayMonitor);

        QAction* actConf = new QAction(QApplication::translate("TrayMonitor",
                               "Configure...", 0),TrayMonitor);
#else
        QAction *actShow = new QAction(QApplication::translate("TrayMonitor",
                               "Display",
                                0, QApplication::UnicodeUTF8),TrayMonitor);
        QAction* actQuit = new QAction(QApplication::translate("TrayMonitor",
                               "Quit",
                                0, QApplication::UnicodeUTF8),TrayMonitor);
        QAction* actAbout = new QAction(QApplication::translate("TrayMonitor",
                               "About",
                                0, QApplication::UnicodeUTF8),TrayMonitor);
        QAction* actRun = new QAction(QApplication::translate("TrayMonitor",
                               "Run...",
                                0, QApplication::UnicodeUTF8),TrayMonitor);
        QAction* actRes = new QAction(QApplication::translate("TrayMonitor",
                              "Restore...",
                               0, QApplication::UnicodeUTF8),TrayMonitor);

        QAction* actConf = new QAction(QApplication::translate("TrayMonitor",
                               "Configure...",
                                0, QApplication::UnicodeUTF8),TrayMonitor);
#endif
        stmenu->addAction(actShow);
        stmenu->addAction(actRun);
        stmenu->addAction(actRes);
        stmenu->addSeparator();
        stmenu->addAction(actConf);
        stmenu->addSeparator();
        stmenu->addAction(actAbout);
        stmenu->addSeparator();
        stmenu->addAction(actQuit);
        
        connect(actRun, SIGNAL(triggered()), this, SLOT(cb_run()));
        connect(actShow, SIGNAL(triggered()), this, SLOT(cb_show()));
        connect(actConf, SIGNAL(triggered()), this, SLOT(cb_conf()));
        connect(actRes, SIGNAL(triggered()), this, SLOT(cb_restore()));
        connect(actQuit, SIGNAL(triggered()), this, SLOT(cb_quit()));
        connect(actAbout, SIGNAL(triggered()), this, SLOT(cb_about()));
        connect(spinRefresh, SIGNAL(valueChanged(int)), this, SLOT(cb_refresh(int)));
        connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
                this, SLOT(cb_trayIconActivated(QSystemTrayIcon::ActivationReason)));
        tray->setContextMenu(stmenu);

        QIcon icon(":/images/cartridge1.png");
        tray->setIcon(icon);
        tray->setToolTip(QString("Bacula Tray Monitor"));
        tray->show();
        retranslateUi(TrayMonitor);
        QMetaObject::connectSlotsByName(TrayMonitor);
        startTimer();

        /* When we don't have the systemtray, we keep the menu, but disabled */
        if (!have_systray) {
           actShow->setEnabled(false);
           menubp->setMenu(stmenu);
           TrayMonitor->show();
        }
    } // setupUi

    void retranslateUi(QMainWindow *TrayMonitor)
    {
#if QT_VERSION >= 0x050000
        TrayMonitor->setWindowTitle(QApplication::translate("TrayMonitor", "Bacula Tray Monitor", 0));
#else
        TrayMonitor->setWindowTitle(QApplication::translate("TrayMonitor", "Bacula Tray Monitor", 0, QApplication::UnicodeUTF8));
#endif

    } // retranslateUi

private slots:
    void cb_quit() {
       QApplication::quit();
    }

    void cb_refresh(int val) {
       if (timer) {
          timer->setInterval(val*1000);
       }
    }

    void cb_about() {
       QMessageBox::about(this, "Bacula Tray Monitor", "Bacula Tray Monitor\n"
                          "For more information, see: www.bacula.org\n"
                          "Copyright (C) 1999-2017, Kern Sibbald.\n"
                          "All rights reserved.");
    }
    RESMON *get_director() {
       QStringList dirs;
       RESMON *d, *director=NULL;
       bool ok;
       foreach_res(d, R_DIRECTOR) {
          if (!director) {
             director = d;
          }
          dirs << QString(d->hdr.name);
       }
       foreach_res(d, R_CLIENT) {
          if (d->use_remote) {
             if (!director) {
                director = d;
             }
             dirs << QString(d->hdr.name);
          }
       }
       if (dirs.count() > 1) {
          /* TODO: Set Modal attribute */
          QString dir = QInputDialog::getItem(this, _("Select a Director"), "Director:", dirs, 0, false, &ok, 0);
          if (!ok) {
             return NULL;
          }
          if (ok && !dir.isEmpty()) {
             char *p = dir.toUtf8().data();
             foreach_res(d, R_DIRECTOR) {
                if (strcmp(p, d->hdr.name) == 0) {
                   director = d;
                   break;
                }
             }
             foreach_res(d, R_CLIENT) {
                if (strcmp(p, d->hdr.name) == 0) {
                   director = d;
                   break;
                }
             }
          }
       }
       if (dirs.count() == 0 || director == NULL) {
          /* We need the proxy feature */
          display_error("No Director defined");
          return NULL;
       }
       return director;
    }
    void cb_run() {
       RESMON *dir = get_director();
       if (!dir) {
          return;
       }
       task *t = new task();
       connect(t, SIGNAL(done(task *)), this, SLOT(run_job(task *)), Qt::QueuedConnection);
       t->init(dir, TASK_RESOURCES);
       dir->wrk->queue(t);
    }
    void refresh_item() {
       /* Probably do only the first one */
       int oldnbjobs = 0;

       for (int i=tabWidget->count() - 1; i >= 0; i--) {
          ResStatus *s = (ResStatus *) tabWidget->widget(i);
          if (s->res->use_monitor) {
             s->res->mutex->lock();
             if (s->res->running_jobs) {
                oldnbjobs += s->res->running_jobs->size();
             }
             s->res->mutex->unlock();
          }
          if (isVisible() || s->res->use_monitor) {
             s->doUpdate();
          }
       }
       /* We need to find an other way to compute running jobs */
       if (oldnbjobs) {
          QString q;
          tray->setIcon(QIcon(":/images/R.png"));
          tray->setToolTip(q.sprintf("Bacula Tray Monitor - %d job%s running", oldnbjobs, oldnbjobs>1?"s":""));
          //tray->showMessage();   Can use this function to display a popup

       } else {
          tray->setIcon(QIcon(":/images/cartridge1.png"));
          tray->setToolTip("Bacula Tray Monitor");
       }
    }
    void cb_conf() {
       new Conf();
    }
    void cb_restore() {
       RESMON *dir = get_director();
       if (!dir) {
          return;
       }
       task *t = new task();
       connect(t, SIGNAL(done(task *)), this, SLOT(start_restore_wizard(task *)), Qt::QueuedConnection);
       t->init(dir, TASK_RESOURCES);
       dir->wrk->queue(t);
    }

    void cb_trayIconActivated(QSystemTrayIcon::ActivationReason r) {
       if (r == QSystemTrayIcon::Trigger) {
          cb_show();
       }
    }

    void refresh_screen() {
       refresh_item();
    }

    void cb_show() {
       if (isVisible()) {
          hide();
       } else {
          refresh_item();
          show();
       }
    }
public slots:
    void task_done(task *t) {
       Dmsg0(0, "Task done!\n");
       t->deleteLater();
    }
    void run_job(task *t) {
       Dmsg0(0, "Task done!\n");
       RESMON *dir = t->res;
       t->deleteLater();
       new RunJob(dir);
    }

    void start_restore_wizard(task *t) {
        RESMON *dir = t->res;
        restorewiz = new RestoreWizard(dir);
        restorewiz->show();
        t->deleteLater();
    }

};


#endif  /* TRAYUI_H */
