// Copyright (C) 2023 The Qt Company Ltd.
// Copyright (C) 2019 Alexey Edelev <semlanik@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include "simplechatengine.h"

#include <QGrpcHttp2Channel>
#include <QGrpcUserPasswordCredentials>
#include <QGrpcInsecureChannelCredentials>

#include <QDebug>
#include <QFile>
#include <QCryptographicHash>
#include <QDateTime>
#include <QClipboard>
#include <QGuiApplication>
#include <QMimeData>
#include <QImage>
#include <QByteArray>
#include <QBuffer>

SimpleChatEngine::SimpleChatEngine(QObject *parent)
    : QObject(parent),
      m_state(Disconnected),
      m_client(new qtgrpc::examples::chat::SimpleChat::Client),
      m_clipBoard(QGuiApplication::clipboard())
{
    qRegisterProtobufTypes();
    if (m_clipBoard)
        QObject::connect(m_clipBoard, &QClipboard::dataChanged, this,
                         &SimpleChatEngine::clipBoardContentTypeChanged);
}

SimpleChatEngine::~SimpleChatEngine()
{
    delete m_client;
}

void SimpleChatEngine::login(const QString &name, const QString &password)
{
    if (m_state != Disconnected)
        return;

    setState(Connecting);
    QUrl url("http://localhost:65002");

    // ![0]
    std::shared_ptr<QAbstractGrpcChannel> channel(
            new QGrpcHttp2Channel(url,
                                  QGrpcUserPasswordCredentials(name, password.toUtf8())
                                          | QGrpcInsecureChannelCredentials()));
    // ![0]

    m_client->attachChannel(channel);

    // ![1]
    auto stream = m_client->streamMessageList(qtgrpc::examples::chat::None());
    QObject::connect(stream.get(), &QGrpcStream::errorOccurred, this,
                     [this, stream](const QGrpcStatus &status) {
                         qCritical()
                                 << "Stream error(" << status.code() << "):" << status.message();
                         if (status.code() == QGrpcStatus::Unauthenticated)
                             emit authFailed();
                     });

    QObject::connect(stream.get(), &QGrpcStream::finished, this,
                     [this, stream]() { setState(Disconnected); });

    QObject::connect(stream.get(), &QGrpcStream::messageReceived, this, [this, name, stream]() {
        if (m_userName != name) {
            m_userName = name;
            emit userNameChanged();
        }
        setState(Connected);
        m_messages.append(stream->read<qtgrpc::examples::chat::ChatMessages>().messages());
    });
    // ![1]
}

void SimpleChatEngine::sendMessage(const QString &content)
{
    // ![2]
    qtgrpc::examples::chat::ChatMessage msg;
    msg.setContent(content.toUtf8());
    msg.setType(qtgrpc::examples::chat::ChatMessage::Text);
    msg.setTimestamp(QDateTime::currentMSecsSinceEpoch());
    msg.setFrom(m_userName);
    m_client->sendMessage(msg);
    // ![2]
}

SimpleChatEngine::ContentType SimpleChatEngine::clipBoardContentType() const
{
    if (m_clipBoard != nullptr) {
        const QMimeData *mime = m_clipBoard->mimeData();
        if (mime != nullptr) {
            if (mime->hasImage() || mime->hasUrls())
                return SimpleChatEngine::ContentType::Image;
            else if (mime->hasText())
                return SimpleChatEngine::ContentType::Text;
        }
    }
    return SimpleChatEngine::ContentType::Unknown;
}

void SimpleChatEngine::sendImageFromClipboard()
{
    if (m_clipBoard == nullptr)
        return;

    QByteArray imgData;
    const QMimeData *mime = m_clipBoard->mimeData();
    if (mime != nullptr) {
        if (mime->hasImage()) {
            QImage img = mime->imageData().value<QImage>();
            img = img.scaled(300, 300, Qt::KeepAspectRatio);
            QBuffer buffer(&imgData);
            buffer.open(QIODevice::WriteOnly);
            img.save(&buffer, "PNG");
            buffer.close();
        } else if (mime->hasUrls()) {
            QUrl imgUrl = mime->urls().at(0);
            if (!imgUrl.isLocalFile()) {
                qWarning() << "Only supports transfer of local images";
                return;
            }
            QImage img(imgUrl.toLocalFile());
            if (img.isNull()) {
                qWarning() << "Invalid image format";
                return;
            }

            QBuffer buffer(&imgData);
            buffer.open(QIODevice::WriteOnly);
            img.save(&buffer, "PNG");
            buffer.close();
        }
    }

    if (imgData.isEmpty())
        return;

    qtgrpc::examples::chat::ChatMessage msg;
    msg.setContent(imgData);
    msg.setType(qtgrpc::examples::chat::ChatMessage::Image);
    msg.setTimestamp(QDateTime::currentMSecsSinceEpoch());
    msg.setFrom(m_userName);
    m_client->sendMessage(msg);
}

ChatMessageModel *SimpleChatEngine::messages()
{
    return &m_messages;
}

QString SimpleChatEngine::userName() const
{
    return m_userName;
}

SimpleChatEngine::State SimpleChatEngine::state() const
{
    return m_state;
}

void SimpleChatEngine::setState(SimpleChatEngine::State state)
{
    if (m_state != state) {
        m_state = state;
        emit stateChanged();
    }
}
