//qcustomcalendarwidget.cpp
#include "calendarwidgetex.h"
#include <QPainter>
#include <QProxyStyle>
#include <QTableView>
#include <QHeaderView>
#include <QLayout>
#include <QPushButton>
#include <QLabel>
#include <QDebug>
#include <QLayoutItem>
#include <QKeyEvent>
#include <QPainterPath>

#include "scopeselectionmodel.h"
#include "calendarheader.h"
#include "calendarnav.h"
#include "PaintHelper/painthelper.h"
#include "StyleManager/lhstylemanager.h"
//#include "utility/utility.h"

CalendarWidgetEx::CalendarWidgetEx(QWidget *parent)
    : QCalendarWidget(parent)
    , m_modeSelection(Normal)
    , m_pDateScopeModel(nullptr)
    , m_nLineHeight(-1)
    , m_hasTopSplitLine(false)
{
    setWindowFlag(Qt::NoDropShadowWindowHint);
    setAttribute(Qt::WA_TranslucentBackground);
    setWindowFlag(Qt::FramelessWindowHint);

    //使用固定尺寸(无法通过resize控制日历大小, 日历整体大小由layout下的控件的fixSize决定)
    //layout()->setSizeConstraint(QLayout::SetFixedSize);
    //禁用原有的年月导航
    setNavigationBarVisible(false);
    //禁用横向纵向表头
    setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
    setHorizontalHeaderFormat(QCalendarWidget::NoHorizontalHeader);
    //取消聚焦虚线框
    setStyle(new NoFocusStyle(this));

    QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
    if(vBodyLayout == nullptr) return;

    CalendarNav *pNav = new CalendarNav(this);
    CalendarHeader *pHeader = new CalendarHeader(this);
    setFirstDayOfWeek(Qt::Sunday);
    pHeader->SetFirstDayOfWeek(Qt::Sunday);

    vBodyLayout->insertWidget(0, pNav);
    // 导航和星期标题间距
    vBodyLayout->insertSpacing(1, 10);
    vBodyLayout->insertWidget(2, pHeader);

    vBodyLayout->setSpacing(10);
    vBodyLayout->setContentsMargins(10,0,10,10);

    m_nLineHeight = vBodyLayout->itemAt(4)->widget()->mapTo(this, QPoint(0,0)).y();
    //qDebug()<<"m_nLineHeight"<<m_nLineHeight<<vBodyLayout->itemAt(3)->widget()->height();

    //开启鼠标监测
    QTableView *pCalendarView = dynamic_cast<QTableView*>(vBodyLayout->itemAt(4)->widget());
    if (Q_NULLPTR != pCalendarView) {
        pCalendarView->setMouseTracking(true);
    }

    //在构造函数里取消selectionChanged, clicked事件没用, 因为执行QDateTimeEdit的setCalendarWidget方法时, 会重新绑定
    //所以必须等setCalendarWidget执行完后再取消事件
    //calendarWidget->disconnect(calendarWidget, &QCalendarWidget::selectionChanged, 0, 0);
    //calendarWidget->disconnect(calendarWidget, &QCalendarWidget::clicked, 0, 0);

    setMouseTracking(true);

    // 设置默认字体后,修复单元格变得很宽的问题
    for (QWidget* f : findChildren<QWidget*>()) {
        if(f->objectName() != "qt_calendar_calendarview") continue;
        QTableView* pView = reinterpret_cast<QTableView*>(f);
        if (nullptr != pView && nullptr != pView->horizontalHeader()) {
            pView->horizontalHeader()->setMaximumSectionSize(WINDOW_WIDTH / 8);
        }
    }

    connect(this, &QCalendarWidget::clicked, this, &CalendarWidgetEx::OnClicked);
    initSkinColor();
}


void CalendarWidgetEx::SetSelectMode(SelectMode mode, ScopeSelectionModel *pDataModel)
{
    m_listMultiSelectDays.clear();
    m_modeSelection = mode;
    if(m_pDateScopeModel == nullptr)
    {
        m_pDateScopeModel = pDataModel;
        connect(m_pDateScopeModel, &ScopeSelectionModel::sig_Update, this, static_cast<void(QWidget::*)()>(&QWidget::update));
    }
    update();
}

void CalendarWidgetEx::hideNavigatioinButton(bool bPreYear, bool bPreMon, bool bNextYear, bool bNextMon)
{
    QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
    if(vBodyLayout == nullptr) return;
    CalendarNav *pNav = qobject_cast<CalendarNav*>(vBodyLayout->itemAt(0)->widget());
    if (nullptr != pNav) {
        pNav->hideNextMonth(bNextMon);
        pNav->hideNextYear(bNextYear);
        pNav->hidePreMonth(bPreMon);
        pNav->hidePreYear(bPreYear);
    }
}

void CalendarWidgetEx::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
{
    PainterEx *painterEx = static_cast<PainterEx*>(painter);
    painterEx->setRenderHint(QPainter::Antialiasing);

    QColor textColor;
    {//正常
        textColor = m_normalTextColor;
    }
#if 0
    // 周六,周日特殊颜色
    int nWeek = date.dayOfWeek();
    if (6 == nWeek || 7 == nWeek) {
        textColor = QColor(255,149,0);
    }
#endif
    //鼠标移入
    if(date == m_dateMouseOver)
    {
        QPoint center = rect.center();
        QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
        painterEx->DrawRoundedRect(rc, 2.0, m_hoverBlockColor);
        textColor = m_normalTextColor;
    }
    //当天
    if(date == QDate::currentDate())
    {
        painter->save();
        QPoint center = rect.center();
        QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
        painter->setPen(m_todayTextColor);
        painter->setBrush(Qt::transparent);
        painter->drawRoundedRect(rc, 2.0, 2.0);
        textColor = m_todayTextColor;
        painter->restore();
    }
    //选中
    if(m_modeSelection == Multi && m_listMultiSelectDays.contains(date))
    {
        //painterEx->DrawCircle(QRectF(rect).center(), 9, QColor(9,109,217));
        QPoint center = rect.center();
        QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
        painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
        textColor = m_selectTextColor;
    }
    if(m_modeSelection == Normal && date == selectedDate())
    {
        //painterEx->DrawCircle(QRectF(rect).center(), 9, QColor(9, 109, 217));
        QPoint center = rect.center();
        QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
        painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
        textColor = m_selectTextColor;
    }
    if(m_modeSelection == Scope && m_pDateScopeModel != nullptr && m_pDateScopeModel->dtFirst.isValid() && m_pDateScopeModel->dtSecond.isValid())
    {
        QDate scopeStart = qMin(m_pDateScopeModel->dtFirst, m_pDateScopeModel->dtSecond);
        QDate scopeEnd = qMax(m_pDateScopeModel->dtFirst, m_pDateScopeModel->dtSecond);
        if(date == qBound(scopeStart, date, scopeEnd) && date.month() == monthShown())
        {
            painterEx->SetBrushOnly(m_hoverBlockColor);
            if(date == scopeStart || date == scopeEnd)
            {
                textColor = m_selectTextColor;
                QPoint center = rect.center();
                QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
                painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
            }
            else
            {
                QRect r(0, 0, rect.width(), TEXT_WIDTH);
                r.moveCenter(rect.center());
                painterEx->drawRect(r);
            }
        }
    }
    //不可选的日期或非当月日期
    if(date < minimumDate() || date > maximumDate() || date.month() != monthShown())
    {
        textColor = m_disableTextColor;
    }

    //当天且未选中, 加粗
    bool isBold = (date == QDate::currentDate() && date != selectedDate());
    QString strDay(QString("%1").arg(date.day(), 2, 10, QLatin1Char('0')));
    painterEx->setFont(FontEx(font().family(), DEFAULT_FONT_SIZE, isBold));
    QRect rc = rect.adjusted(-1, 0, 0, -3); // 矫正文字位置
    painterEx->DrawText(rc, strDay, textColor, Qt::AlignCenter);
}

void CalendarWidgetEx::paintEvent(QPaintEvent *)
{
    PainterEx painter(this);

    //边框和背景
    painter.setPen(Qt::transparent);
    painter.setBrush(QColor(255, 255, 255));
    QRect rc(rect());
    painter.DrawRoundedRect(rc, WINDOW_RADIUS);

    QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
    if(vBodyLayout != nullptr) {
        // 画导航栏,绘制上圆角
        QPainterPath path;
        path.setFillRule(Qt::WindingFill);
        QRectF tmpRc(1, 0, WINDOW_WIDTH - WINDOW_RADIUS, 40);
        path.addRoundedRect(tmpRc, WINDOW_RADIUS, WINDOW_RADIUS);
        path.addRect(QRectF(tmpRc.x(), tmpRc.y() + WINDOW_RADIUS, tmpRc.width(), tmpRc.height()));
        painter.fillPath(path, QColor(255, 255, 255));
        //分割线
        QWidget* pNav = vBodyLayout->itemAt(0)->widget();
        if (nullptr != pNav) {
            int h = pNav->mapTo(this, pNav->rect().bottomRight()).y();
            painter.SetPenOnly(QColor(0, 0, 0, 23));
            painter.drawLine(QPoint(0, h), QPoint(width(), h));
        }
    }
}

QSize CalendarWidgetEx::minimumSizeHint() const
{
    return QSize(WINDOW_WIDTH, WINDOW_HEIGHT);
}

void CalendarWidgetEx::mouseMoveEvent(QMouseEvent *event)
{
    QCalendarWidget::mouseMoveEvent(event);

    QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
    if(vBodyLayout == nullptr) return;
    QTableView *pCalendarView = dynamic_cast<QTableView*>(vBodyLayout->itemAt(4)->widget());
    if(pCalendarView == nullptr) return;
    QModelIndex index = pCalendarView->indexAt(pCalendarView->mapFromGlobal(event->globalPos()));
    QDate dateMouseOver = dateForCell(index.row(), index.column());
    if(m_dateMouseOver != dateMouseOver)
    {
        m_dateMouseOver = dateMouseOver;
        if(m_pDateScopeModel != nullptr && !m_pDateScopeModel->bLocked && m_pDateScopeModel->dtFirst.isValid())
        {
            m_pDateScopeModel->dtSecond = dateMouseOver;
            m_pDateScopeModel->Update();
        }
        update();
    }
}

//仅适用于: 不显示纵向表头(第几周), 且不显示横向表头(周几)
QDate CalendarWidgetEx::dateForCell(int row, int column) const
{
    if (row < 0 || row > 5 || column < 0 || column > 6)
        return QDate();

    const QDate refDate = referenceDate();
    if (!refDate.isValid())
        return QDate();

    const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate);
    if (columnForFirstOfShownMonth - 0/*m_firstColumn*/ < 1)
        row -= 1;

    const int requestedDay = 7 * (row - 0/*m_firstRow*/) + column - columnForFirstOfShownMonth - refDate.day() + 1;
    return refDate.addDays(requestedDay);
}

QDate CalendarWidgetEx::referenceDate() const
{
    int refDay = 1;
    while (refDay <= 31) {
        QDate refDate(yearShown(), monthShown(), refDay);
        if (refDate.isValid())
            return refDate;
        refDay += 1;
    }
    return QDate();
}
int CalendarWidgetEx::columnForFirstOfMonth(const QDate &date) const
{
    return (columnForDayOfWeek(date.dayOfWeek()) - (date.day() % 7) + 8) % 7;
}
int CalendarWidgetEx::columnForDayOfWeek(int day) const
{
    if (day < 1 || day > 7)
        return -1;
    int column = day - firstDayOfWeek();
    if (column < 0)
        column += 7;
    return column;
}

void CalendarWidgetEx::initSkinColor()
{
    switch (LHStyleManager::Instance()->GetCurSkinStyle()) {
    case eBrightStyle:
        m_normalTextColor = NORMAL_TEXT_BRIGHT;
        m_todayTextColor = TODAY_TEXT_BRIGHT;
        m_selectTextColor = SELECT_TEXT_BRIGHT;
        m_disableTextColor = DISABLE_TEXT_BRIGHT;
        m_splitLineColor = SPLIT_LINE_BRIGHT;
        m_selectBlockColor = SELECT_BRIGHT;
        m_hoverBlockColor = HOVER_BRIGHT;
        break;
    case eDarkStyle:
        break;
    default:
        break;
    }
}

void CalendarWidgetEx::OnClicked(const QDate &date)
{
    if(m_modeSelection == Multi)
    {
        if(m_listMultiSelectDays.contains(date))
        {
            m_listMultiSelectDays.removeOne(date);
        }
        else
        {
            m_listMultiSelectDays.append(date);
        }
    }
    if(m_modeSelection == Scope && m_pDateScopeModel != nullptr)
    {
        if(!m_pDateScopeModel->bLocked && m_pDateScopeModel->dtFirst.isValid())
        {
            m_pDateScopeModel->dtSecond = date;
            m_pDateScopeModel->bLocked = true;
            m_pDateScopeModel->Locked();
        }
        else
        {
            m_pDateScopeModel->dtFirst = date;
            m_pDateScopeModel->dtSecond = date;
            m_pDateScopeModel->bLocked = false;
        }

        m_pDateScopeModel->Update();
    }
    update();
}

void CalendarWidgetEx::leaveEvent(QEvent *)
{
    if(m_modeSelection != Scope)
    {
        //离开日历时, 清空鼠标移入状态
        m_dateMouseOver = QDate();
        update();
    }
}

void NoFocusStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
{
    QStyleOption *viewOption = new QStyleOption(*option);
    viewOption->state &= (~QStyle::State_HasFocus);
    //if (element == PE_FrameFocusRect) return;
    QProxyStyle::drawPrimitive(element, viewOption, painter, widget);
    delete viewOption;
}