#include "ElaMessageBarPrivate.h"

#include <QDateTime>
#include <QGraphicsOpacityEffect>
#include <QPainter>
#include <QPainterPath>
#include <QPropertyAnimation>
#include <QTimer>

#include "ElaIconButton.h"
#include "ElaMessageBar.h"
Q_SINGLETON_CREATE_CPP(ElaMessageBarManager)
QMap<ElaMessageBarType::PositionPolicy, QList<ElaMessageBar *> *> _messageBarActiveMap;
ElaMessageBarManager::ElaMessageBarManager(QObject *parent) {}

ElaMessageBarManager::~ElaMessageBarManager() {}

void ElaMessageBarManager::requestMessageBarEvent(ElaMessageBar *messageBar) {
    if (!messageBar) {
        return;
    }
    if (_messageBarEventMap.contains(messageBar)) {
        QList<QVariantMap> eventList = _messageBarEventMap.value(messageBar);
        QVariantMap        eventData = eventList.last();
        eventList.removeLast();
        if (eventList.isEmpty()) {
            _messageBarEventMap.remove(messageBar);
        } else {
            _messageBarEventMap[messageBar] = eventList;
        }
        // 触发事件
        QString     functionName = eventData.value("EventFunctionName").toString();
        QVariantMap functionData = eventData.value("EventFunctionData").toMap();
        QMetaObject::invokeMethod(messageBar->d_func(), functionName.toLocal8Bit().constData(), Qt::AutoConnection, Q_ARG(QVariantMap, functionData));
    }
}

void ElaMessageBarManager::postMessageBarCreateEvent(ElaMessageBar *messageBar) {
    if (!messageBar) {
        return;
    }
    updateActiveMap(messageBar, true); // 计算坐标前增加
    if (!_messageBarEventMap.contains(messageBar)) {
        QList<QVariantMap> eventList;
        QVariantMap        eventData;
        eventData.insert("EventFunctionName", "messageBarEnd");
        eventList.append(eventData);
        _messageBarEventMap.insert(messageBar, eventList);
    }
}

void ElaMessageBarManager::postMessageBarEndEvent(ElaMessageBar *messageBar) {
    if (!messageBar) {
        return;
    }
    updateActiveMap(messageBar, false);
    // Other MessageBar事件入栈 记录同一策略事件
    ElaMessageBarType::PositionPolicy policy = messageBar->d_ptr->_policy;
    foreach (auto otherMessageBar, *_messageBarActiveMap.value(policy)) {
        if (otherMessageBar->d_ptr->_judgeCreateOrder(messageBar)) {
            QList<QVariantMap> eventList = _messageBarEventMap[otherMessageBar];
            // 优先执行先触发的事件 End事件保持首位
            QVariantMap eventData;
            eventData.insert("EventFunctionName", "onOtherMessageBarEnd");
            QVariantMap functionData;
            functionData.insert("TargetPosY", otherMessageBar->d_ptr->_calculateTargetPosY());
            eventData.insert("EventFunctionData", functionData);
            // 若处于创建动画阶段  则合并事件动画
            if (otherMessageBar->d_ptr->getWorkMode() == WorkStatus::CreateAnimation) {
                while (eventList.count() > 1) {
                    eventList.removeLast();
                }
            }
            eventList.insert(1, eventData);
            _messageBarEventMap[otherMessageBar] = eventList;
            otherMessageBar->d_ptr->tryToRequestMessageBarEvent();
        }
    }
}

void ElaMessageBarManager::forcePostMessageBarEndEvent(ElaMessageBar *messageBar) {
    if (!messageBar) {
        return;
    }
    // 清除事件堆栈记录
    _messageBarEventMap.remove(messageBar);
    // 发布终止事件
    postMessageBarEndEvent(messageBar);
}

int ElaMessageBarManager::getMessageBarEventCount(ElaMessageBar *messageBar) {
    if (!messageBar) {
        return -1;
    }
    if (!_messageBarEventMap.contains(messageBar)) {
        return -1;
    }
    QList<QVariantMap> eventList = _messageBarEventMap[messageBar];
    return eventList.count();
}

void ElaMessageBarManager::updateActiveMap(ElaMessageBar *messageBar, bool isActive) {
    if (!messageBar) {
        return;
    }
    ElaMessageBarType::PositionPolicy policy = messageBar->d_ptr->_policy;
    if (isActive) {
        if (_messageBarActiveMap.contains(policy)) {
            _messageBarActiveMap[policy]->append(messageBar);
        } else {
            QList<ElaMessageBar *> *messageBarList = new QList<ElaMessageBar *>();
            messageBarList->append(messageBar);
            _messageBarActiveMap.insert(policy, messageBarList);
        }
    } else {
        if (_messageBarActiveMap.contains(policy)) {
            if (_messageBarActiveMap[policy]->count() > 0) {
                _messageBarActiveMap[policy]->removeOne(messageBar);
            }
        }
    }
}

ElaMessageBarPrivate::ElaMessageBarPrivate(QObject *parent) : QObject{parent} {
    setProperty("MessageBarClosedY", 0);
    setProperty("MessageBarFinishY", 0);
    _createTime = QDateTime::currentMSecsSinceEpoch();
}

ElaMessageBarPrivate::~ElaMessageBarPrivate() {}

void ElaMessageBarPrivate::tryToRequestMessageBarEvent() {
    Q_Q(ElaMessageBar);
    if (!_isMessageBarCreateAnimationFinished || _isMessageBarEventAnimationStart) {
        return;
    }
    ElaMessageBarManager::getInstance()->requestMessageBarEvent(q);
}

WorkStatus ElaMessageBarPrivate::getWorkMode() const {
    if (!_isMessageBarCreateAnimationFinished) {
        return WorkStatus::CreateAnimation;
    }
    if (_isMessageBarEventAnimationStart) {
        return WorkStatus::OtherEventAnimation;
    }
    return WorkStatus::Idle;
}

void ElaMessageBarPrivate::onOtherMessageBarEnd(QVariantMap eventData) {
    Q_Q(ElaMessageBar);
    _isMessageBarEventAnimationStart      = true;
    qreal               targetPosY        = eventData.value("TargetPosY").toReal();
    QPropertyAnimation *closePosAnimation = new QPropertyAnimation(this, "MessageBarClosedY");
    connect(closePosAnimation, &QPropertyAnimation::valueChanged, this, [=](const QVariant &value) { q->move(q->pos().x(), value.toUInt()); });
    connect(closePosAnimation, &QPropertyAnimation::finished, this, [=]() {
        _isMessageBarEventAnimationStart = false;
        if (ElaMessageBarManager::getInstance()->getMessageBarEventCount(q) > 1) {
            ElaMessageBarManager::getInstance()->requestMessageBarEvent(q);
        }
        if (_isReadyToEnd) {
            ElaMessageBarManager::getInstance()->requestMessageBarEvent(q);
        }
    });
    closePosAnimation->setEasingCurve(QEasingCurve::InOutSine);
    closePosAnimation->setDuration(200);
    closePosAnimation->setStartValue(q->pos().y());
    closePosAnimation->setEndValue(targetPosY);
    closePosAnimation->start(QAbstractAnimation::DeleteWhenStopped);
}

void ElaMessageBarPrivate::messageBarEnd(QVariantMap eventData) {
    Q_Q(ElaMessageBar);
    ElaMessageBarManager::getInstance()->postMessageBarEndEvent(q);
    QPropertyAnimation *barFinishedOpacityAnimation = new QPropertyAnimation(this, "pOpacity");
    connect(barFinishedOpacityAnimation, &QPropertyAnimation::valueChanged, this, [=]() {
        _closeButton->setOpacity(_pOpacity);
        q->update();
    });
    connect(barFinishedOpacityAnimation, &QPropertyAnimation::finished, this, [=]() { q->deleteLater(); });
    barFinishedOpacityAnimation->setDuration(300);
    barFinishedOpacityAnimation->setEasingCurve(QEasingCurve::InOutSine);
    barFinishedOpacityAnimation->setStartValue(1);
    barFinishedOpacityAnimation->setEndValue(0);
    barFinishedOpacityAnimation->start(QAbstractAnimation::DeleteWhenStopped);
}

void ElaMessageBarPrivate::onCloseButtonClicked() {
    Q_Q(ElaMessageBar);
    if (_isReadyToEnd) {
        return;
    }
    _isReadyToEnd    = true;
    _isNormalDisplay = false;
    ElaMessageBarManager::getInstance()->forcePostMessageBarEndEvent(q);
    QPropertyAnimation *opacityAnimation = new QPropertyAnimation(this, "pOpacity");
    connect(opacityAnimation, &QPropertyAnimation::valueChanged, this, [=]() {
        _closeButton->setOpacity(_pOpacity);
        q->update();
    });
    connect(opacityAnimation, &QPropertyAnimation::finished, q, [=]() { q->deleteLater(); });
    opacityAnimation->setStartValue(_pOpacity);
    opacityAnimation->setEndValue(0);
    opacityAnimation->setDuration(220);
    opacityAnimation->setEasingCurve(QEasingCurve::InOutSine);
    opacityAnimation->start(QAbstractAnimation::DeleteWhenStopped);
}

void ElaMessageBarPrivate::_messageBarCreate(int displayMsec) {
    Q_Q(ElaMessageBar);
    q->show();
    QFont font = q->font();
    font.setPixelSize(16);
    font.setWeight(QFont::Bold);
    q->setFont(font);
    int titleWidth = q->fontMetrics().horizontalAdvance(_title);
    font.setPixelSize(14);
    font.setWeight(QFont::Medium);
    q->setFont(font);
    int textWidth  = q->fontMetrics().horizontalAdvance(_text);
    int fixedWidth = _closeButtonLeftRightMargin + _leftPadding + _titleLeftSpacing + _textLeftSpacing + _closeButtonWidth + titleWidth + textWidth +
                     2 * _shadowBorderWidth;
    q->setFixedWidth(fixedWidth > 500 ? 500 : fixedWidth);
    ElaMessageBarManager::getInstance()->postMessageBarCreateEvent(q);
    int startX = 0;
    int startY = 0;
    int endX   = 0;
    int endY   = 0;
    _calculateInitialPos(startX, startY, endX, endY);
    // 滑入动画
    QPropertyAnimation *barPosAnimation = new QPropertyAnimation(q, "pos");
    connect(barPosAnimation, &QPropertyAnimation::finished, q, [=]() {
        _isNormalDisplay                     = true;
        _isMessageBarCreateAnimationFinished = true;
        if (ElaMessageBarManager::getInstance()->getMessageBarEventCount(q) > 1) {
            ElaMessageBarManager::getInstance()->requestMessageBarEvent(q);
        }
        QTimer::singleShot(displayMsec, q, [=]() {
            _isReadyToEnd = true;
            ElaMessageBarManager::getInstance()->requestMessageBarEvent(q);
        });
    });
    switch (_policy) {
    case ElaMessageBarType::Top:
    case ElaMessageBarType::Bottom: {
        barPosAnimation->setDuration(250);
        break;
    }
    default: {
        barPosAnimation->setDuration(450);
        break;
    }
    }
    barPosAnimation->setStartValue(QPoint(startX, startY));
    barPosAnimation->setEndValue(QPoint(endX, endY));
    barPosAnimation->setEasingCurve(QEasingCurve::InOutSine);
    barPosAnimation->start(QAbstractAnimation::DeleteWhenStopped);
}

void ElaMessageBarPrivate::_calculateInitialPos(int &startX, int &startY, int &endX, int &endY) {
    Q_Q(ElaMessageBar);
    QList<int> resultList         = _getOtherMessageBarTotalData();
    int        minimumHeightTotal = resultList[0];
    int        indexLessCount     = resultList[1];
    switch (_policy) {
    case ElaMessageBarType::Top: {
        // 25动画距离
        startX = q->parentWidget()->width() / 2 - q->minimumWidth() / 2;
        startY = minimumHeightTotal + _messageBarSpacing * indexLessCount + _messageBarVerticalTopMargin - 25;
        endX   = startX;
        endY   = minimumHeightTotal + _messageBarSpacing * indexLessCount + _messageBarVerticalTopMargin;
        break;
    }
    case ElaMessageBarType::Left: {
        startX = -q->minimumWidth();
        startY = minimumHeightTotal + _messageBarSpacing * indexLessCount + q->parentWidget()->height() / 2;
        endX   = _messageBarHorizontalMargin;
        endY   = startY;
        break;
    }
    case ElaMessageBarType::Bottom: {
        startX = q->parentWidget()->width() / 2 - q->minimumWidth() / 2;
        startY = q->parentWidget()->height() - q->minimumHeight() - minimumHeightTotal - _messageBarSpacing * indexLessCount -
                 _messageBarVerticalBottomMargin - 25;
        endX = startX;
        endY = q->parentWidget()->height() - q->minimumHeight() - minimumHeightTotal - _messageBarSpacing * indexLessCount -
               _messageBarVerticalBottomMargin;
        break;
    }
    case ElaMessageBarType::Right: {
        startX = q->parentWidget()->width();
        startY = minimumHeightTotal + _messageBarSpacing * indexLessCount + q->parentWidget()->height() / 2;
        endX   = q->parentWidget()->width() - q->minimumWidth() - _messageBarHorizontalMargin;
        endY   = startY;
        break;
    }
    case ElaMessageBarType::TopRight: {
        startX = q->parentWidget()->width();
        startY = minimumHeightTotal + _messageBarSpacing * indexLessCount + _messageBarVerticalTopMargin;
        endX   = q->parentWidget()->width() - q->minimumWidth() - _messageBarHorizontalMargin;
        endY   = startY;
        break;
    }
    case ElaMessageBarType::TopLeft: {
        startX = -q->minimumWidth();
        startY = minimumHeightTotal + _messageBarSpacing * indexLessCount + _messageBarVerticalTopMargin;
        endX   = _messageBarHorizontalMargin;
        endY   = startY;
        break;
    }
    case ElaMessageBarType::BottomRight: {
        startX = q->parentWidget()->width();
        startY = q->parentWidget()->height() - q->minimumHeight() - minimumHeightTotal - _messageBarSpacing * indexLessCount -
                 _messageBarVerticalBottomMargin;
        endX = q->parentWidget()->width() - q->minimumWidth() - _messageBarHorizontalMargin;
        endY = startY;
        break;
    }
    case ElaMessageBarType::BottomLeft: {
        startX = -q->minimumWidth();
        startY = q->parentWidget()->height() - q->minimumHeight() - minimumHeightTotal - _messageBarSpacing * indexLessCount -
                 _messageBarVerticalBottomMargin;
        endX = _messageBarHorizontalMargin;
        endY = startY;
        break;
    }
    }
    if (endY < _messageBarVerticalTopMargin || endY > q->parentWidget()->height() - _messageBarVerticalBottomMargin - q->minimumHeight()) {
        (*_messageBarActiveMap[_policy])[0]->d_ptr->onCloseButtonClicked();
        _calculateInitialPos(startX, startY, endX, endY);
    }
}

QList<int> ElaMessageBarPrivate::_getOtherMessageBarTotalData(bool isJudgeCreateOrder) {
    Q_Q(ElaMessageBar);
    QList<int>              resultList;
    int                     minimumHeightTotal = 0;
    int                     indexLessCount     = 0;
    QList<ElaMessageBar *> *messageBarList     = _messageBarActiveMap[_policy];
    for (auto messageBar : *messageBarList) {
        if (messageBar == q) {
            continue;
        }
        if (!isJudgeCreateOrder || (isJudgeCreateOrder && _judgeCreateOrder(messageBar))) {
            indexLessCount++;
            minimumHeightTotal += messageBar->minimumHeight();
        }
    }
    resultList.append(minimumHeightTotal);
    resultList.append(indexLessCount);
    return resultList;
}

qreal ElaMessageBarPrivate::_calculateTargetPosY() {
    Q_Q(ElaMessageBar);
    QList<int> resultList         = _getOtherMessageBarTotalData(true);
    int        minimumHeightTotal = resultList[0];
    int        indexLessCount     = resultList[1];
    switch (_policy) {
    case ElaMessageBarType::Top:
    case ElaMessageBarType::TopRight:
    case ElaMessageBarType::TopLeft: {
        return minimumHeightTotal + _messageBarSpacing * indexLessCount + _messageBarVerticalTopMargin;
    }
    case ElaMessageBarType::Left:
    case ElaMessageBarType::Right: {
        return minimumHeightTotal + _messageBarSpacing * indexLessCount + q->parentWidget()->height() / 2;
    }
    case ElaMessageBarType::Bottom:
    case ElaMessageBarType::BottomRight:
    case ElaMessageBarType::BottomLeft: {
        return q->parentWidget()->height() - q->minimumHeight() - minimumHeightTotal - _messageBarSpacing * indexLessCount -
               _messageBarVerticalBottomMargin;
    }
    }
    return 0;
}

bool ElaMessageBarPrivate::_judgeCreateOrder(ElaMessageBar *otherMessageBar) {
    if (otherMessageBar->d_ptr->_createTime < _createTime) {
        // otherMessageBar先创建
        return true;
    } else {
        return false;
    }
}

void ElaMessageBarPrivate::_drawSuccess(QPainter *painter) {
    Q_Q(ElaMessageBar);
    painter->setBrush(QColor(0xE0, 0xF6, 0xDD));
    QRect foregroundRect(_shadowBorderWidth, _shadowBorderWidth, q->width() - 2 * _shadowBorderWidth, q->height() - 2 * _shadowBorderWidth);
    painter->drawRoundedRect(foregroundRect, _borderRadius, _borderRadius);
    // 图标绘制
    painter->save();
    painter->setPen(Qt::white);
    QPainterPath textPath;
    textPath.addEllipse(QPoint(_leftPadding + 6, q->height() / 2), 9, 9);
    painter->setClipPath(textPath);
    painter->fillPath(textPath, QColor(0x11, 0x77, 0x10));
    QFont iconFont = QFont("ElaAwesome");
    iconFont.setPixelSize(12);
    painter->setFont(iconFont);
    painter->drawText(_leftPadding, 0, q->width(), q->height(), Qt::AlignVCenter, QChar((unsigned short)ElaIconType::Check));
    painter->restore();
    // 文字颜色
    painter->setPen(QPen(Qt::black));
}

void ElaMessageBarPrivate::_drawWarning(QPainter *painter) {
    Q_Q(ElaMessageBar);
    painter->setBrush(QColor(0x6B, 0x56, 0x27));
    QRect foregroundRect(_shadowBorderWidth, _shadowBorderWidth, q->width() - 2 * _shadowBorderWidth, q->height() - 2 * _shadowBorderWidth);
    painter->drawRoundedRect(foregroundRect, _borderRadius, _borderRadius);
    // 图标绘制
    // exclamation
    painter->save();
    painter->setPen(Qt::black);
    QPainterPath textPath;
    textPath.addEllipse(QPoint(_leftPadding + 6, q->height() / 2), 9, 9);
    painter->setClipPath(textPath);
    painter->fillPath(textPath, QColor(0xF8, 0xE2, 0x23));
    painter->drawText(_leftPadding + 4, 0, q->width(), q->height(), Qt::AlignVCenter, "!");
    painter->restore();
    // 文字颜色
    painter->setPen(QColor(0xFA, 0xFA, 0xFA));
}

void ElaMessageBarPrivate::_drawInformation(QPainter *painter) {
    Q_Q(ElaMessageBar);
    painter->setBrush(QColor(0xF4, 0xF4, 0xF4));
    QRect foregroundRect(_shadowBorderWidth, _shadowBorderWidth, q->width() - 2 * _shadowBorderWidth, q->height() - 2 * _shadowBorderWidth);
    painter->drawRoundedRect(foregroundRect, _borderRadius, _borderRadius);
    // 图标绘制
    painter->save();
    painter->setPen(Qt::white);
    QPainterPath textPath;
    textPath.addEllipse(QPoint(_leftPadding + 6, q->height() / 2), 9, 9);
    painter->setClipPath(textPath);
    painter->fillPath(textPath, QColor(0x00, 0x66, 0xB4));
    painter->drawText(_leftPadding + 4, 0, q->width(), q->height(), Qt::AlignVCenter, "i");
    painter->restore();
    // 文字颜色
    painter->setPen(Qt::black);
}

void ElaMessageBarPrivate::_drawError(QPainter *painter) {
    Q_Q(ElaMessageBar);
    painter->setBrush(QColor(0xFE, 0xE7, 0xEA));
    QRect foregroundRect(_shadowBorderWidth, _shadowBorderWidth, q->width() - 2 * _shadowBorderWidth, q->height() - 2 * _shadowBorderWidth);
    painter->drawRoundedRect(foregroundRect, _borderRadius, _borderRadius);
    // 图标绘制
    painter->save();
    painter->setPen(Qt::white);
    QPainterPath textPath;
    textPath.addEllipse(QPoint(_leftPadding + 6, q->height() / 2), 9, 9);
    painter->setClipPath(textPath);
    painter->fillPath(textPath, QColor(0xBA, 0x2D, 0x20));
    QFont iconFont = QFont("ElaAwesome");
    iconFont.setPixelSize(13);
    painter->setFont(iconFont);
    painter->drawText(_leftPadding + 1, 0, q->width(), q->height(), Qt::AlignVCenter, QChar((unsigned short)ElaIconType::Xmark));
    painter->restore();
    // 文字颜色
    painter->setPen(Qt::black);
}
