/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <sal/config.h>

#include <QtInstanceButton.hxx>
#include <QtInstanceMessageDialog.hxx>
#include <QtInstanceMessageDialog.moc>

#include <vcl/qt/QtUtils.hxx>
#include <vcl/stdtext.hxx>

#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QVBoxLayout>

QtInstanceMessageDialog::QtInstanceMessageDialog(QMessageBox* pMessageDialog)
    : QtInstanceDialog(pMessageDialog)
    , m_pMessageDialog(pMessageDialog)
{
    assert(m_pMessageDialog);

    m_pExtraControlsContainer = new QWidget;
    m_pExtraControlsContainer->setLayout(new QVBoxLayout);
    positionExtraControlsContainer();
}

void QtInstanceMessageDialog::set_primary_text(const rtl::OUString& rText)
{
    SolarMutexGuard g;
    QtInstance& rQtInstance = GetQtInstance();
    if (!rQtInstance.IsMainThread())
    {
        rQtInstance.RunInMainThread([&] { set_primary_text(rText); });
        return;
    }

    // update text and ensure that extra controls are contained in the
    // dialog's layout (new layout gets set when setting text)
    m_pMessageDialog->setText(toQString(rText));
    positionExtraControlsContainer();
}

void QtInstanceMessageDialog::set_secondary_text(const rtl::OUString& rText)
{
    SolarMutexGuard g;
    QtInstance& rQtInstance = GetQtInstance();
    if (!rQtInstance.IsMainThread())
    {
        rQtInstance.RunInMainThread([&] { set_secondary_text(rText); });
        return;
    }

    // update text and ensure that extra controls are contained in the
    // dialog's layout (new layout gets set when setting text)
    m_pMessageDialog->setInformativeText(toQString(rText));
    positionExtraControlsContainer();
}

std::unique_ptr<weld::Container> QtInstanceMessageDialog::weld_message_area()
{
    return std::make_unique<QtInstanceContainer>(m_pExtraControlsContainer);
}

OUString QtInstanceMessageDialog::get_primary_text() const
{
    SolarMutexGuard g;
    QtInstance& rQtInstance = GetQtInstance();
    OUString sText;
    if (!rQtInstance.IsMainThread())
    {
        rQtInstance.RunInMainThread([&] { sText = get_primary_text(); });
        return sText;
    }

    assert(m_pMessageDialog);
    return toOUString(m_pMessageDialog->text());
}

OUString QtInstanceMessageDialog::get_secondary_text() const
{
    SolarMutexGuard g;
    QtInstance& rQtInstance = GetQtInstance();
    OUString sText;
    if (!rQtInstance.IsMainThread())
    {
        rQtInstance.RunInMainThread([&] { sText = get_secondary_text(); });
        return sText;
    }

    assert(m_pMessageDialog);
    return toOUString(m_pMessageDialog->informativeText());
}

void QtInstanceMessageDialog::add_button(const OUString& rText, int nResponse, const OUString&)
{
    addButton(*m_pMessageDialog, rText, nResponse);
}

void QtInstanceMessageDialog::change_default_button(weld::Button*, weld::Button* pNew)
{
    SolarMutexGuard g;

    GetQtInstance().RunInMainThread([&] {
        if (QtInstanceButton* pButton = dynamic_cast<QtInstanceButton*>(pNew))
        {
            if (QPushButton* pPushButton = qobject_cast<QPushButton*>(&pButton->getButton()))
                m_pMessageDialog->setDefaultButton(pPushButton);
        }
    });
}

std::unique_ptr<weld::Button> QtInstanceMessageDialog::weld_button_for_response(int nResponse)
{
    SolarMutexGuard g;
    QtInstance& rQtInstance = GetQtInstance();
    if (!rQtInstance.IsMainThread())
    {
        std::unique_ptr<weld::Button> xButton;
        rQtInstance.RunInMainThread([&] { xButton = weld_button_for_response(nResponse); });
        return xButton;
    }

    if (QPushButton* pButton = buttonForResponseCode(nResponse))
        return std::make_unique<QtInstanceButton>(pButton);

    return nullptr;
}

int QtInstanceMessageDialog::run()
{
    SolarMutexGuard g;
    QtInstance& rQtInstance = GetQtInstance();
    if (!rQtInstance.IsMainThread())
    {
        int nRet = 0;
        rQtInstance.RunInMainThread([&] { nRet = run(); });
        return nRet;
    }

    // if a button was clicked, return its response code
    int nRet = m_pMessageDialog->exec();
    if (QAbstractButton* pClickedButton = m_pMessageDialog->clickedButton())
        return pClickedButton->property(PROPERTY_VCL_RESPONSE_CODE).toInt();

    return nRet;
}

void QtInstanceMessageDialog::dialogFinished(int nResult)
{
    SolarMutexGuard g;
    QtInstance& rQtInstance = GetQtInstance();
    if (!rQtInstance.IsMainThread())
    {
        rQtInstance.RunInMainThread([&] { dialogFinished(nResult); });
        return;
    }

    // if a button was clicked, use its response code, otherwise the passed one
    int nResponseCode = nResult;
    if (QAbstractButton* pClickedButton = m_pMessageDialog->clickedButton())
        nResponseCode = pClickedButton->property(PROPERTY_VCL_RESPONSE_CODE).toInt();

    QtInstanceDialog::dialogFinished(nResponseCode);
}

void QtInstanceMessageDialog::addStandardButtons(VclButtonsType eButtonType)
{
    addStandardButtons(*m_pMessageDialog, eButtonType);
}

void QtInstanceMessageDialog::addStandardButtons(QMessageBox& rMessageDialog,
                                                 VclButtonsType eButtonType)
{
    SolarMutexGuard g;
    GetQtInstance().RunInMainThread([&] {
        switch (eButtonType)
        {
            case VclButtonsType::NONE:
                break;
            case VclButtonsType::Ok:
                addButton(rMessageDialog, GetStandardText(StandardButtonType::OK), RET_OK);
                break;
            case VclButtonsType::Close:
                addButton(rMessageDialog, GetStandardText(StandardButtonType::Close), RET_CLOSE);
                break;
            case VclButtonsType::Cancel:
                addButton(rMessageDialog, GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
                break;
            case VclButtonsType::YesNo:
                addButton(rMessageDialog, GetStandardText(StandardButtonType::Yes), RET_YES);
                addButton(rMessageDialog, GetStandardText(StandardButtonType::No), RET_NO);
                break;
            case VclButtonsType::OkCancel:
                addButton(rMessageDialog, GetStandardText(StandardButtonType::OK), RET_OK);
                addButton(rMessageDialog, GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
                break;
            default:
                assert(false && "Unhandled VCLButtonsType");
        }
    });
}

void QtInstanceMessageDialog::addButton(QMessageBox& rMessageDialog, const OUString& rText,
                                        int nResponse)
{
    SolarMutexGuard g;
    GetQtInstance().RunInMainThread([&] {
        QPushButton* pButton = rMessageDialog.addButton(vclToQtStringWithAccelerator(rText),
                                                        QMessageBox::ButtonRole::ActionRole);
        pButton->setProperty(PROPERTY_VCL_RESPONSE_CODE, QVariant::fromValue(nResponse));
    });
}

void QtInstanceMessageDialog::positionExtraControlsContainer()
{
    assert(m_pExtraControlsContainer);

    // make use of implementation detail that QMessageBox uses QGridLayout for its layout
    // (logic here will need to be adjusted if that ever changes)
    QGridLayout* pDialogLayout = qobject_cast<QGridLayout*>(m_pMessageDialog->layout());
    assert(pDialogLayout && "QMessageBox has unexpected layout");

    // no need to reposition if layout didn't change
    if (pDialogLayout->indexOf(m_pExtraControlsContainer) >= 0)
        return;

    // find last label
    const int nItemCount = pDialogLayout->count();
    int nLastLabelIndex = -1;
    for (int i = nItemCount - 1; i >= 0; --i)
    {
        if (QLayoutItem* pItem = pDialogLayout->itemAt(i))
        {
            if (qobject_cast<QLabel*>(pItem->widget()))
            {
                nLastLabelIndex = i;
                break;
            }
        }
    }
    assert(nLastLabelIndex >= 0 && "didn't find label in message box");

    // shift everything after the last label down by one row
    for (int i = nLastLabelIndex + 1; i < nItemCount; ++i)
    {
        if (QLayoutItem* pItem = pDialogLayout->itemAt(i))
        {
            int nRow = 0;
            int nCol = 0;
            int nRowSpan = 0;
            int nColSpan = 0;
            pDialogLayout->getItemPosition(i, &nRow, &nCol, &nRowSpan, &nColSpan);
            pDialogLayout->removeItem(pItem);
            pDialogLayout->addItem(pItem, nRow + 1, nCol, nRowSpan, nColSpan);
        }
    }

    // insert an additional layout in the now empty row, underneath the last label
    int nLabelRow = 0;
    int nLabelCol = 0;
    int nLabelRowSpan = 0;
    int nLabelColSpan = 0;
    pDialogLayout->getItemPosition(nLastLabelIndex, &nLabelRow, &nLabelCol, &nLabelRowSpan,
                                   &nLabelColSpan);
    pDialogLayout->addWidget(m_pExtraControlsContainer, nLabelRow + 1, nLabelCol);
}

QPushButton* QtInstanceMessageDialog::buttonForResponseCode(int nResponse)
{
    const QList<QAbstractButton*> aButtons = m_pMessageDialog->buttons();
    return QtInstanceDialog::buttonForResponseCode(aButtons, nResponse);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
