#include "message.h"
#include <QPainter>
#include <QStyleOption>
#include <QGraphicsOpacityEffect>
#include <QSequentialAnimationGroup>
#include <algorithm>

static int nMessageItemMargin = 20;
static Message *s_instance_ = 0;
Message *Message::instance()
{
    return s_instance_;
}

Message::Message(QObject *parent) : QObject(parent)
{
    if (parent == nullptr)
        throw std::runtime_error("message structure error!");
    auto widget = qobject_cast<QWidget *>(parent);
    if (widget == nullptr) {
        throw std::runtime_error("message structure error!");
    }
    width_ = widget->width();
    duration_ = 3000;
    messages_.reserve(50);
    s_instance_ = this;
}

Message::Message(int duration, QObject *parent) : duration_(duration)
{
    if (parent == nullptr)
        throw std::runtime_error("message structure error!");
    auto widget = qobject_cast<QWidget *>(parent);
    if (widget == nullptr) {
        throw std::runtime_error("message structure error!");
    }
    width_ = widget->width();
    if (duration_ < 0)
        duration_ = 0;
    messages_.reserve(50);
}

Message::~Message()
{
}

void Message::Push(MessageType type, QString content)
{
    std::unique_lock<std::mutex> lck(mtx_);
    int height = 0;
    for_each(messages_.begin(), messages_.end(),
             [&height](MessageItem *pTp) mutable {
                 height += (nMessageItemMargin + pTp->height());
             });
    MessageItem *pItem =
        new MessageItem(qobject_cast<QWidget *>(parent()), type, content);
    connect(pItem, &MessageItem::itemReadyRemoved, this,
            &Message::adjustItemPos);
    connect(pItem, &MessageItem::itemRemoved, this, &Message::removeItem);
    pItem->SetDuration(duration_);
    height += nMessageItemMargin;
    pItem->move(QPoint((width_ - pItem->width()) / 2, height));
    messages_.emplace_back(pItem);
    pItem->Show();
}

void Message::setDuration(int nDuration)
{
    if (nDuration < 0)
        nDuration = 0;
    duration_ = nDuration;
}

void Message::adjustItemPos(MessageItem *pItem)
{
    pItem->Close();
}

void Message::removeItem(MessageItem *pItem)
{
    std::unique_lock<std::mutex> lck(mtx_);
    for (auto itr = messages_.begin(); itr != messages_.end();) {
        if (*itr == pItem) {
            messages_.erase(itr);
            break;
        } else
            ++itr;
    }
    int height = nMessageItemMargin;
    for_each(messages_.begin(), messages_.end(), [&](MessageItem *item) {
        QPropertyAnimation *pAnimation1 =
            new QPropertyAnimation(item, "geometry", this);
        pAnimation1->setDuration(300);
        pAnimation1->setStartValue(QRect(item->pos().x(), item->pos().y(),
                                         item->width(), item->height()));
        pAnimation1->setEndValue(
            QRect(item->pos().x(), height, item->width(), item->height()));

        pAnimation1->start(
            QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);
        height += (nMessageItemMargin + item->height());
    });
}

MessageItem::MessageItem(QWidget *parent, MessageType type,
                         const QString content, const int duration,
                         const int iconMargin, const int leftMargin,
                         const int topMargin, const int minWidth,
                         const int minHeight)
    : QWidget(parent), duration_(duration)
{
    setObjectName(QStringLiteral("message_item"));
    setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
    setAttribute(Qt::WA_TranslucentBackground);
    pLabelIcon_ = new QLabel(this);
    if (type == MessageType::MESSAGE_TYPE_SUCCESS) {
        setStyleSheet(QStringLiteral(
            "QWidget#message_item{border:1px solid "
            "#E1F3D8;background-color:rgb(240,249,235);border-radius:4px;}"
            "QLabel{border:none;background-color:rgb(240,249,235);font-family:"
            "Microsoft "
            "Yahei;font-size:16px;font-weight:400;color:rgb(103,194,58);}"));
        pLabelIcon_->setPixmap(
            QPixmap(":/image/resource/image/message/type_success.png"));
    } else if (type == MessageType::MESSAGE_TYPE_ERROR) {
        setStyleSheet(QStringLiteral(
            "QWidget#message_item{border:1px solid "
            "#FDE2E2;background-color:#FEF0F0;border-radius:4px;}"
            "QLabel{border:none;background-color:#FEF0F0;font-family:Microsoft "
            "Yahei;font-size:16px;font-weight:400;color:rgb(245,108,108);}"));
        pLabelIcon_->setPixmap(
            QPixmap(":/image/resource/image/message/type_error.png"));
    } else if (type == MessageType::MESSAGE_TYPE_WARNING) {
        setStyleSheet(QStringLiteral(
            "QWidget#message_item{border:1px solid "
            "#FAECD8;background-color:#FDF6EC;border-radius:4px;}"
            "QLabel{border:none;background-color:#FDF6EC;font-family:Microsoft "
            "Yahei;font-size:16px;font-weight:400;color:rgb(230,162,60);}"));
        pLabelIcon_->setPixmap(
            QPixmap(":/image/resource/image/message/type_warning.png"));
    } else {
        setStyleSheet(QStringLiteral(
            "QWidget#message_item{border:1px solid "
            "#EBEEF5;background-color:#EDF2FC;border-radius:4px;}"
            "QLabel{border:none;background-color:#EDF2FC;font-family:Microsoft "
            "Yahei;font-size:16px;font-weight:400;color:rgb(144,147,153);}"));
        pLabelIcon_->setPixmap(
            QPixmap(":/image/resource/image/message/type_infomation.png"));
    }

    pLabelContent_ = new QLabel(this);
    pLabelContent_->setText(content);
    pLabelContent_->adjustSize();
    width_ = pLabelContent_->width() + leftMargin * 2;
    height_ = pLabelContent_->height() + topMargin * 2;

    if (width_ < minWidth)
        width_ = minWidth;
    if (height_ < minHeight)
        height_ = minHeight;
    resize(width_, height_);
    pLabelContent_->move(leftMargin, (height_ - pLabelContent_->height()) / 2);
    pLabelIcon_->move(iconMargin, (height_ - pLabelIcon_->height()) / 2);

    connect(&lifeTimer_, &QTimer::timeout, this, [&]() {
        lifeTimer_.stop();
        emit itemReadyRemoved(this);
    });
    hide();
}

MessageItem::~MessageItem()
{
    delete pLabelIcon_;
    delete pLabelContent_;
}

void MessageItem::Show()
{
    show();
    if (duration_ > 0)
        lifeTimer_.start(duration_);
    AppearAnimation();
}

void MessageItem::Close()
{
    DisappearAnimation();
}

void MessageItem::SetDuration(int nDuration)
{
    duration_ = nDuration;
}

void MessageItem::paintEvent(QPaintEvent *event)
{
    QStyleOption opt;
    opt.init(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    QWidget::paintEvent(event);
}

void MessageItem::AppearAnimation()
{
    QPropertyAnimation *animation = new QPropertyAnimation(this, "geometry");
    animation->setDuration(20);
    animation->setStartValue(
        QRect(pos().x(), pos().y() - nMessageItemMargin, width_, height_));
    animation->setEndValue(QRect(pos().x(), pos().y(), width_, height_));
    animation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);
}

void MessageItem::DisappearAnimation()
{
    QGraphicsOpacityEffect *pOpacity = new QGraphicsOpacityEffect(this);
    pOpacity->setOpacity(1);
    setGraphicsEffect(pOpacity);

    QPropertyAnimation *pOpacityAnimation2 =
        new QPropertyAnimation(pOpacity, "opacity");
    pOpacityAnimation2->setDuration(500);
    pOpacityAnimation2->setStartValue(1);
    pOpacityAnimation2->setEndValue(0);

    pOpacityAnimation2->start(
        QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);

    connect(pOpacityAnimation2, &QPropertyAnimation::finished, this, [&]() {
        emit itemRemoved(this);
        deleteLater();
    });
}