/*! @file

    @id $Id$
*/
//       1         2         3         4         5         6         7         8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890

#ifndef PROXY_HXX
#define PROXY_HXX

#include <proxyface/autoproxy.hxx>
#include <proxyface/ui_proxy.hxx>
#include <proxyface/proxyauth.hxx>

#include <QtGui/QMovie>
#if QT_VERSION >= 0x050000
#include <QtWidgets/QDialog>
#include <QtWidgets/QPushButton>
#else
#include <QtGui/QDialog>
#include <QtGui/QPushButton>
#endif
#include <QtNetwork/QNetworkProxy>
#include <QtNetwork/QAuthenticator>
#include <QtNetwork/QNetworkReply>
#include <QtCore/QUrl>
#include <QtCore/QSettings>

#include <QtCore/QDebug>
#include <cassert>

namespace gui {
  
  class Proxy: public QDialog, public Ui::Proxy {

      Q_OBJECT

    public:
    
      Proxy(QString testUrl, QWidget* p=0):
          QDialog(p), _lastDefaultProxy(QNetworkProxy()),
          _proxyAuth(this), _found(false) {
        setupUi(this);
        _testUrl->insertItem(0, testUrl);
        _internetWait->setMovie(new QMovie(":/icons/indicator.gif"));
        _internetWait->movie()->start();
        QSettings s("SwissSign", "ProxySettings");
        _save->setChecked(s.contains("default-type"));
        _type->setCurrentIndex(s.value("default-type", 0).toInt());
        _url->setText(s.value("default-server", QString()).toString());
        _port->setValue(s.value("default-port", 0).toInt());
        _lastDefaultProxy = QNetworkProxy();
        _lastType = _type->currentIndex();
        _lastUrl = _url->text();
        _lastPort = _port->value();
        if (!connect(&_auto, SIGNAL(proxyFound(const QUrl&,
                                               const QNetworkProxy&)),
                     SIGNAL(proxyFound(const QUrl&, const QNetworkProxy&))))
          qFatal("cannot if (!connect");
        if (!connect(&_auto, SIGNAL(proxyFound(const QUrl&,
                                              const QNetworkProxy&)),
                    SLOT(proxyFoundSlot(const QUrl&, const QNetworkProxy&))))
          qFatal("cannot connect");
        if (!connect(&_auto, SIGNAL(proxyError(QNetworkReply::NetworkError)),
                     SIGNAL(proxyError(QNetworkReply::NetworkError))))
          qFatal("cannot connect");
        if (!connect(&_auto, SIGNAL(proxyError(QNetworkReply::NetworkError)),
                     SLOT(proxyErrorSlot(QNetworkReply::NetworkError))))
          qFatal("cannot connect");
        if (!connect(&_auto, SIGNAL(temporaryError(QNetworkReply::NetworkError,
                                                   QString, QString)),
                     SIGNAL(temporaryError(QNetworkReply::NetworkError,
                                           QString, QString))))
          qFatal("cannot connect");
        if (!connect(&_auto, SIGNAL(proxyAuthenticationRequired
                                    (const QNetworkProxy&, QAuthenticator*)),
                     SLOT(proxyAuthenticationRequired(const QNetworkProxy&,
                                                      QAuthenticator*))))
          qFatal("cannot connect");
        if (!connect(&_auto, SIGNAL(authenticationRequired
                                    (QNetworkReply*, QAuthenticator*)),
                     SLOT(authenticationRequired
                          (QNetworkReply*, QAuthenticator*))))
          qFatal("cannot connect");
        acceptValues();
      }
      
      void checkUrl(const QString& url) {
        if (_testUrl->findText(url)==-1)
          _testUrl->addItem(url);
        _testUrl->setCurrentIndex(_testUrl->findText(url));
        acceptValues();
      }

      void proxy(const QString& url,
                 int timeout1=5000, int timeout2=30000) {
        _auto.proxy(url.toStdString(), timeout1, timeout2);
      }

      void ping() {
        _auto.ping(_testUrl->currentText());
      }

      void retry() {
        on__test_clicked();
      }

      void reset() {
        _type->setCurrentIndex(_lastType);
        _url->setText(_lastUrl);
        _port->setValue(_lastPort);
        QNetworkProxy::setApplicationProxy(_lastDefaultProxy);
        acceptValues();
      }
      
      void acceptValues() {
        on__test_clicked();
        switch (_type->currentIndex()) {
          case 0: try {
            _url->setEnabled(false);
            _port->setEnabled(false);
            _url->setText("");
            _port->setValue(0);
          } catch (...) {} break;
        }
        if (_save->isChecked()) {
          QSettings s("SwissSign", "ProxySettings");
          s.setValue("default-type", _type->currentIndex());
          s.setValue("default-server", _url->text());
          s.setValue("default-port", _port->value());
        } else {
          QSettings s("SwissSign", "ProxySettings");
          s.clear();
        }
        _lastDefaultProxy = QNetworkProxy();
        _lastType = _type->currentIndex();
        _lastUrl = _url->text();
        _lastPort = _port->value();
      }
      
    Q_SIGNALS:

      void detecting();
      void proxyFound(const QUrl&, const QNetworkProxy&);
      void proxyError(QNetworkReply::NetworkError);
      void temporaryError(QNetworkReply::NetworkError, QString, QString);      

    public Q_SLOTS:

      void proxyAuthenticationRequired(const QNetworkProxy&,
                                       QAuthenticator* auth) {
        qDebug()<<"proxyAuthenticationRequired";
        _proxyAuth._realm->setText(auth->realm());
        _auto.pause();
        if (_found || _proxyAuth.exec()==QDialog::Accepted) {
          auth->setUser(_proxyAuth._username->text());
          auth->setPassword(_proxyAuth._password->text());
          _found = false;
        }
        _auto.restart();
      }
      
      void authenticationRequired(QNetworkReply*, QAuthenticator*) {
      }
      
      void proxyFoundSlot(const QUrl&, const QNetworkProxy& p) {
        _found = true;
        _stackInternet->setCurrentIndex(SERVER_FOUND);
        QNetworkProxy prx(p.type()==QNetworkProxy::DefaultProxy
                          ?QNetworkProxy::applicationProxy():p);
        if (prx.type()==QNetworkProxy::DefaultProxy) return;
        if (!_url->isEnabled()) _url->setText(prx.hostName());
        if (!_port->isEnabled()) _port->setValue(prx.port());
        if (p.type()==QNetworkProxy::DefaultProxy) return;
        prx.setUser(_proxyAuth._username->text());
        prx.setPassword(_proxyAuth._password->text());
        QNetworkProxy::setApplicationProxy(prx);
      }
      
      void proxyErrorSlot(QNetworkReply::NetworkError){
        _stackInternet->setCurrentIndex(SERVER_NOT_FOUND);
      }
      
      void on__type_currentIndexChanged(int index) {
        _url->setEnabled(index!=0);
        _port->setEnabled(index!=0);
      }

      void on__test_clicked() {
        _auto.reset();
        _stackInternet->setCurrentIndex(WAITING_FOR_SERVER);
        detecting();
        switch (_type->currentIndex()) {
          case 0: try {
            QNetworkProxy::setApplicationProxy
              (QNetworkProxy(QNetworkProxy::NoProxy));
            _auto.proxy(_testUrl->currentText());
          } catch (...) {} break;
          case 1: {
            QNetworkProxy::setApplicationProxy
              (QNetworkProxy
               (QNetworkProxy::HttpProxy, _url->text(), _port->value()));
            _auto.ping(_testUrl->currentText());
          } break;
          case 2: {
            QNetworkProxy::setApplicationProxy
              (QNetworkProxy
               (QNetworkProxy::Socks5Proxy, _url->text(), _port->value(),
                "marwae", "marwae"));
            _auto.ping(_testUrl->currentText());
          } break;
        }
      }
      
      virtual void accept() {
        acceptValues();
        QDialog::accept();
      }

      virtual void reject() {
        reset();
        QDialog::reject();
      }

    private:

      enum Network {SERVER_FOUND = 0, SERVER_NOT_FOUND, WAITING_FOR_SERVER};

    private:

      proxy::Face _auto;
      int _lastType;
      QString _lastUrl;
      int _lastPort;
      QNetworkProxy _lastDefaultProxy;
      ProxyAuth _proxyAuth;
      bool _found;
      
  };
  
}

#endif
