#include <QtNetwork>
#include <QtGui>

#include "qsslcipher.h"
#include "qsslsocket.h"

class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog()
    {
        QHBoxLayout *connectLayout = new QHBoxLayout;
        connectLayout->addWidget((hostEdit = new QLineEdit));
        connectLayout->addWidget((portEdit = new QSpinBox));
        connectLayout->addWidget((connectButton = new QPushButton(tr("Connect"))));

        hostEdit->setMinimumWidth(hostEdit->fontMetrics().width(QLatin1String("X")) * 30);
        portEdit->setRange(1, 65535);

        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addLayout(connectLayout);
        layout->addWidget((textEdit = new QTextEdit));
        layout->addWidget((lineEdit = new QLineEdit));
        layout->addWidget((statusLabel = new QLabel(tr("Status: Unconnected"))));

        textEdit->setEnabled(false);
        lineEdit->setEnabled(false);

        // Create the socket and connect the signals.
        socket = new QSslSocket(this);
        connect(socket, SIGNAL(encrypted()),
                this, SLOT(encryptedSlot()));
        connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
                this, SLOT(stateChangedSlot(QAbstractSocket::SocketState)));
        connect(socket, SIGNAL(readyRead()),
                this, SLOT(readFromSocket()));
        connect(socket, SIGNAL(sslErrors(const QList<QSslError>&)),
                this, SLOT(sslErrors(const QList<QSslError>&)));

        // Connect the form
        connect(connectButton, SIGNAL(clicked()),
                this, SLOT(connectToHostSlot()));
        connect(lineEdit, SIGNAL(returnPressed()),
                this, SLOT(writeDataSlot()));

        setWindowTitle(tr("QSslSocket Example"));
    }

public slots:
    void connectToHostSlot()
    {
        socket->connectToHostEncrypted(hostEdit->text(), portEdit->value());
        hostEdit->setEnabled(false);
        portEdit->setEnabled(false);
        textEdit->clear();
        connectButton->setEnabled(false);
    }

    void writeDataSlot()
    {
        socket->write(lineEdit->text().trimmed().toUtf8() + "\n");
        textEdit->append(lineEdit->text().trimmed());
        lineEdit->clear();
    }

    void stateChangedSlot(QAbstractSocket::SocketState state)
    {
        switch (state) {
        case QAbstractSocket::UnconnectedState:
            statusLabel->setText(tr("Status: Unconnected"));
            hostEdit->setEnabled(true);
            portEdit->setEnabled(true);
            connectButton->setEnabled(true);
            textEdit->setEnabled(false);
            lineEdit->setEnabled(false);
            break;
        case QAbstractSocket::HostLookupState:
            statusLabel->setText(tr("Status: Searching for host"));
            break;
        case QAbstractSocket::ConnectingState:
            statusLabel->setText(tr("Status: Connecting to host"));
            break;
        case QAbstractSocket::ConnectedState:
            statusLabel->setText(tr("Status: Negotiating handshake"));
            break;
        case QAbstractSocket::ClosingState:
            statusLabel->setText(tr("Status: Closing"));
            break;
        default:
            break;
        }
    }

    void encryptedSlot()
    {
        statusLabel->setText(tr("Status: Encrypted (Cipher: %1)").arg(socket->currentCipher().name()));
        textEdit->setEnabled(true);
        textEdit->setFocusPolicy(Qt::NoFocus);
        lineEdit->setEnabled(true);
        lineEdit->setFocusPolicy(Qt::StrongFocus);
        lineEdit->setFocus();
    }

    void sslErrors(const QList<QSslError> &errors)
    {
        QDialog *dialog = new QDialog(this);
        dialog->deleteLater();

        QGridLayout *layout = new QGridLayout(dialog);
        for (int i = 0; i < errors.size(); ++i) {
            QLabel *description = new QLabel;
            description->setText(tr("%1\nHostname: %2\nPeer certificate issuer: %3\nExpiry date: %4")
                                 .arg(errors.at(i).errorString())
                                 .arg(socket->peerName())
                                 .arg(socket->peerCertificate().issuerInfo(QSslCertificate::Organization))
                                 .arg(socket->peerCertificate().notValidAfter().toString()));
            layout->addWidget(new QLabel(tr("Error:")), i, 0, Qt::AlignLeft | Qt::AlignTop);
            layout->addWidget(description, i, 1, Qt::AlignLeft | Qt::AlignTop);
        }

        QPushButton *button = new QPushButton(tr("Ignore errors"));
        connect(button, SIGNAL(clicked()), dialog, SLOT(accept()));
        QPushButton *button2 = new QPushButton(tr("Cancel connection"));
        button2->setDefault(true);
        connect(button2, SIGNAL(clicked()), dialog, SLOT(reject()));

        QHBoxLayout *buttonLayout = new QHBoxLayout;
        buttonLayout->addStretch();
        buttonLayout->addWidget(button);
        buttonLayout->addWidget(button2);
        buttonLayout->addStretch();
        layout->addLayout(buttonLayout, errors.size(), 0, 1, 2);

        if (dialog->exec())
            socket->ignoreSslErrors();
    }

    void readFromSocket()
    {
        textEdit->append(QString::fromUtf8(socket->readAll()).trimmed());
    }

private:
    QLineEdit *hostEdit;
    QSpinBox *portEdit;
    QPushButton *connectButton;

    QTextEdit *textEdit;
    QLineEdit *lineEdit;
    QLabel *statusLabel;

    QSslSocket *socket;
};

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    Dialog dialog;
    dialog.show();

    return app.exec();
}

#include "main.moc"

