/************************************************************************
 **
 **  @file   dialogfinalmeasurements.cpp
 **  @author Roman Telezhynskyi <dismine(at)gmail.com>
 **  @date   26 9, 2017
 **
 **  @brief
 **  @copyright
 **  This source code is part of the Valentina project, a pattern making
 **  program, whose allow create and modeling patterns of clothing.
 **  Copyright (C) 2017 Valentina project
 **  <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
 **
 **  Valentina is free software: you can redistribute it and/or modify
 **  it under the terms of the GNU General Public License as published by
 **  the Free Software Foundation, either version 3 of the License, or
 **  (at your option) any later version.
 **
 **  Valentina is distributed in the hope that it will be useful,
 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 **  GNU General Public License for more details.
 **
 **  You should have received a copy of the GNU General Public License
 **  along with Valentina.  If not, see <http://www.gnu.org/licenses/>.
 **
 *************************************************************************/

#include "dialogfinalmeasurements.h"
#include "../qmuparser/qmudef.h"
#include "../vmisc/compatibility.h"
#include "../vmisc/theme/themeDef.h"
#include "../vmisc/theme/vtheme.h"
#include "../vmisc/vabstractvalapplication.h"
#include "../vmisc/vvalentinasettings.h"
#include "../vpatterndb/calculator.h"
#include "../vtools/dialogs/dialogtoolbox.h"
#include "../vtools/dialogs/support/dialogeditwrongformula.h"
#include "ui_dialogfinalmeasurements.h"

#include <QMenu>

using namespace Qt::Literals::StringLiterals;

constexpr int DIALOG_MAX_FORMULA_HEIGHT = 64;

//---------------------------------------------------------------------------------------------------------------------
DialogFinalMeasurements::DialogFinalMeasurements(VPattern *doc, QWidget *parent)
  : QDialog(parent),
    ui(new Ui::DialogFinalMeasurements),
    m_doc(doc),
    m_data(doc->GetCompleteData()),
    m_measurements(doc->GetFinalMeasurements()),
    m_searchHistory(new QMenu(this))
{
    ui->setupUi(this);

#if defined(Q_OS_MAC)
    setWindowFlags(Qt::Window);
#endif

    InitIcons();

    m_data.FillPiecesAreas(VAbstractValApplication::VApp()->patternUnits());

    ui->lineEditName->setClearButtonEnabled(true);

    ui->lineEditFind->installEventFilter(this);

    InitSearch();

    formulaBaseHeight = ui->plainTextEditFormula->height();
    ui->plainTextEditFormula->installEventFilter(this);

    VAbstractApplication::VApp()->Settings()->GetOsSeparator() ? setLocale(QLocale()) : setLocale(QLocale::c());

    ShowUnits();

    const bool freshCall = true;
    FillFinalMeasurements(freshCall);

    connect(m_doc, &VPattern::FullUpdateFromFile, this, &DialogFinalMeasurements::FullUpdateFromFile);

    ui->lineEditName->setValidator(
        new QRegularExpressionValidator(QRegularExpression(QStringLiteral("^$|") + NameRegExp()), this));

    connect(ui->tableWidget, &QTableWidget::itemSelectionChanged, this,
            &DialogFinalMeasurements::ShowFinalMeasurementDetails);

    connect(ui->toolButtonAdd, &QToolButton::clicked, this, &DialogFinalMeasurements::Add);
    connect(ui->toolButtonRemove, &QToolButton::clicked, this, &DialogFinalMeasurements::Remove);
    connect(ui->toolButtonUp, &QToolButton::clicked, this, &DialogFinalMeasurements::MoveUp);
    connect(ui->toolButtonDown, &QToolButton::clicked, this, &DialogFinalMeasurements::MoveDown);
    connect(ui->pushButtonGrow, &QPushButton::clicked, this, &DialogFinalMeasurements::DeployFormula);
    connect(ui->toolButtonExpr, &QToolButton::clicked, this, &DialogFinalMeasurements::Fx);
    connect(ui->lineEditName, &QLineEdit::textEdited, this, &DialogFinalMeasurements::SaveName);
    connect(ui->plainTextEditDescription, &QPlainTextEdit::textChanged, this,
            &DialogFinalMeasurements::SaveDescription);
    connect(ui->plainTextEditFormula, &QPlainTextEdit::textChanged, this, &DialogFinalMeasurements::SaveFormula);

    if (ui->tableWidget->rowCount() > 0)
    {
        ui->tableWidget->selectRow(0);
    }

    m_shortcuts.insert(VShortcutAction::CaseSensitiveMatch, ui->toolButtonCaseSensitive);
    m_shortcuts.insert(VShortcutAction::WholeWordMatch, ui->toolButtonWholeWord);
    m_shortcuts.insert(VShortcutAction::RegexMatch, ui->toolButtonRegexp);
    m_shortcuts.insert(VShortcutAction::SearchHistory, ui->pushButtonSearch);
    m_shortcuts.insert(VShortcutAction::RegexMatchUnicodeProperties, ui->toolButtonUseUnicodeProperties);
    m_shortcuts.insert(VShortcutAction::FindNext, ui->toolButtonFindNext);
    m_shortcuts.insert(VShortcutAction::FindPrevious, ui->toolButtonFindNext);

    if (VAbstractShortcutManager *manager = VAbstractApplication::VApp()->GetShortcutManager())
    {
        connect(manager, &VAbstractShortcutManager::ShortcutsUpdated, this, &DialogFinalMeasurements::UpdateShortcuts);
        UpdateShortcuts();
    }
}

//---------------------------------------------------------------------------------------------------------------------
DialogFinalMeasurements::~DialogFinalMeasurements()
{
    ui->lineEditFind->blockSignals(true); // prevents crash
    delete ui;
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::closeEvent(QCloseEvent *event)
{
    ui->plainTextEditFormula->blockSignals(true);
    ui->lineEditName->blockSignals(true);
    ui->plainTextEditDescription->blockSignals(true);

    VValentinaSettings *settings = VAbstractValApplication::VApp()->ValentinaSettings();
    settings->SetFinalMeasurementsSearchOptionMatchCase(m_search->IsMatchCase());
    settings->SetFinalMeasurementsSearchOptionWholeWord(m_search->IsMatchWord());
    settings->SetFinalMeasurementsSearchOptionRegexp(m_search->IsMatchRegexp());
    settings->SetFinalMeasurementsSearchOptionUseUnicodeProperties(m_search->IsUseUnicodePreperties());

    QDialog::closeEvent(event);
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::changeEvent(QEvent *event)
{
    if (event->type() == QEvent::LanguageChange)
    {
        // retranslate designer form (single inheritance approach)
        ui->retranslateUi(this);
        ui->lineEditFind->setPlaceholderText(m_search->SearchPlaceholder());
        UpdateSearchControlsTooltips();
        FullUpdateFromFile();
    }

    if (event->type() == QEvent::PaletteChange)
    {
        InitIcons();
        InitDialogButtonBoxIcons(ui->buttonBox);
    }

    // remember to call base class implementation
    QDialog::changeEvent(event);
}

//---------------------------------------------------------------------------------------------------------------------
auto DialogFinalMeasurements::eventFilter(QObject *object, QEvent *event) -> bool
{
    if (auto *textEdit = qobject_cast<QLineEdit *>(object))
    {
        if (event->type() == QEvent::KeyPress)
        {
            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
            if (const auto *keyEvent = static_cast<QKeyEvent *>(event);
                (keyEvent->key() == Qt::Key_Period) && (keyEvent->modifiers() & Qt::KeypadModifier))
            {
                if (VAbstractApplication::VApp()->Settings()->GetOsSeparator())
                {
                    textEdit->insert(LocaleDecimalPoint(QLocale()));
                }
                else
                {
                    textEdit->insert(LocaleDecimalPoint(QLocale::c()));
                }
                return true;
            }
        }
    }
    else
    {
        // pass the event on to the parent class
        return QDialog::eventFilter(object, event);
    }
    return false; // pass the event to the widget // clazy:exclude=base-class-event
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::showEvent(QShowEvent *event)
{
    QDialog::showEvent(event);
    if (event->spontaneous())
    {
        return;
    }

    if (m_isInitialized)
    {
        return;
    }
    // do your init stuff here

    if (const QSize sz = VAbstractApplication::VApp()->Settings()->GetFinalMeasurementsDialogSize(); not sz.isEmpty())
    {
        resize(sz);
    }

    m_isInitialized = true; // first show windows are held
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::resizeEvent(QResizeEvent *event)
{
    // remember the size for the next time this dialog is opened, but only
    // if widget was already initialized, which rules out the resize at
    // dialog creating, which would
    if (m_isInitialized)
    {
        VAbstractApplication::VApp()->Settings()->SetFinalMeasurementsDialogSize(size());
    }
    QDialog::resizeEvent(event);
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::ShowFinalMeasurementDetails()
{
    if (ui->tableWidget->rowCount() > 0 && m_measurements.size() == ui->tableWidget->rowCount())
    {
        EnableDetails(true);

        const VFinalMeasurement &m = m_measurements.at(ui->tableWidget->currentRow());

        ui->lineEditName->blockSignals(true);
        ui->lineEditName->setText(m.name);
        ui->lineEditName->blockSignals(false);

        ui->plainTextEditDescription->blockSignals(true);
        ui->plainTextEditDescription->setPlainText(m.description);
        ui->plainTextEditDescription->blockSignals(false);

        EvalUserFormula(m.formula, false);
        ui->plainTextEditFormula->blockSignals(true);

        const QString formula =
            VTranslateVars::TryFormulaToUser(m.formula, VAbstractApplication::VApp()->Settings()->GetOsSeparator());

        ui->plainTextEditFormula->setPlainText(formula);
        ui->plainTextEditFormula->blockSignals(false);
    }
    else
    {
        EnableDetails(false);
    }
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::Add()
{
    const int currentRow = ui->tableWidget->currentRow() + 1;

    VFinalMeasurement m;
    m.name = tr("measurement");
    m.formula = '0'_L1;

    m_measurements.append(m);

    UpdateTree();
    ui->tableWidget->selectRow(currentRow);
    ui->tableWidget->repaint(); // Force repain to fix paint artifacts on Mac OS X
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::Remove()
{
    const int row = ui->tableWidget->currentRow();

    if (row == -1 || row >= m_measurements.size())
    {
        return;
    }

    m_measurements.remove(row);

    UpdateTree();

    if (ui->tableWidget->rowCount() > 0)
    {
        ui->tableWidget->selectRow(0);
    }
    else
    {
        EnableDetails(false);
    }
    ui->tableWidget->repaint(); // Force repain to fix paint artifacts on Mac OS X
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::MoveUp()
{
    const int row = ui->tableWidget->currentRow();

    if (row == -1 || row == 0 || row >= m_measurements.size())
    {
        return;
    }

    m_measurements.move(row, row - 1);
    UpdateTree();

    ui->tableWidget->selectRow(row - 1);
    ui->tableWidget->repaint(); // Force repain to fix paint artifacts on Mac OS X
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::MoveDown()
{
    const int row = ui->tableWidget->currentRow();

    if (row == -1 || row == ui->tableWidget->rowCount() - 1 || row >= m_measurements.size())
    {
        return;
    }

    m_measurements.move(row, row + 1);
    UpdateTree();

    ui->tableWidget->selectRow(row + 1);
    ui->tableWidget->repaint(); // Force repain to fix paint artifacts on Mac OS X
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::SaveName(const QString &text)
{
    const int row = ui->tableWidget->currentRow();

    if (row == -1 || row >= m_measurements.size())
    {
        return;
    }

    m_measurements[row].name = text.isEmpty() ? tr("measurement") : text;

    UpdateTree();

    ui->tableWidget->blockSignals(true);
    ui->tableWidget->selectRow(row);
    ui->tableWidget->blockSignals(false);
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::SaveDescription()
{
    const int row = ui->tableWidget->currentRow();

    if (row == -1 || row >= m_measurements.size())
    {
        return;
    }

    const QTextCursor cursor = ui->plainTextEditDescription->textCursor();

    m_measurements[row].description = ui->plainTextEditDescription->toPlainText();

    UpdateTree();

    ui->tableWidget->blockSignals(true);
    ui->tableWidget->selectRow(row);
    ui->tableWidget->blockSignals(false);
    ui->plainTextEditDescription->setTextCursor(cursor);
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::SaveFormula()
{
    const int row = ui->tableWidget->currentRow();

    if (row == -1 || row >= m_measurements.size())
    {
        return;
    }

    // Replace line return character with spaces for calc if exist
    QString const text = ui->plainTextEditFormula->toPlainText();

    if (const QTableWidgetItem *formulaField = ui->tableWidget->item(row, 2); formulaField->text() == text)
    {
        QTableWidgetItem *result = ui->tableWidget->item(row, 1);
        // Show unit in dialog lable (cm, mm or inch)
        const QString postfix = UnitsToStr(VAbstractValApplication::VApp()->patternUnits());
        ui->labelCalculatedValue->setText(result->text() + QChar(QChar::Space) + postfix);
        return;
    }

    if (text.isEmpty())
    {
        // Show unit in dialog lable (cm, mm or inch)
        const QString postfix = UnitsToStr(VAbstractValApplication::VApp()->patternUnits());
        ui->labelCalculatedValue->setText(tr("Error") + " (" + postfix + "). " + tr("Empty field."));
        return;
    }

    if (not EvalUserFormula(text, true))
    {
        return;
    }

    try
    {
        m_measurements[row].formula = VAbstractApplication::VApp()->TrVars()->FormulaFromUser(
            text, VAbstractApplication::VApp()->Settings()->GetOsSeparator());
    }
    catch (qmu::QmuParserError &e) // Just in case something bad will happen
    {
        Q_UNUSED(e)
        return;
    }

    const QTextCursor cursor = ui->plainTextEditFormula->textCursor();

    UpdateTree();

    ui->tableWidget->blockSignals(true);
    ui->tableWidget->selectRow(row);
    ui->tableWidget->blockSignals(false);
    ui->plainTextEditFormula->setTextCursor(cursor);
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::DeployFormula()
{
    const QTextCursor cursor = ui->plainTextEditFormula->textCursor();

    if (ui->plainTextEditFormula->height() < DIALOG_MAX_FORMULA_HEIGHT)
    {
        ui->plainTextEditFormula->setFixedHeight(DIALOG_MAX_FORMULA_HEIGHT);
        // Set icon from theme (internal for Windows system)
        ui->pushButtonGrow->setIcon(FromTheme(VThemeIcon::GoNext));
    }
    else
    {
        ui->plainTextEditFormula->setFixedHeight(formulaBaseHeight);
        // Set icon from theme (internal for Windows system)
        ui->pushButtonGrow->setIcon(FromTheme(VThemeIcon::GoDown));
    }

    // I found that after change size of formula field, it was filed for angle formula, field for formula became black.
    // This code prevent this.
    setUpdatesEnabled(false);
    repaint();
    setUpdatesEnabled(true);

    ui->plainTextEditFormula->setFocus();
    ui->plainTextEditFormula->setTextCursor(cursor);
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::Fx()
{
    const int row = ui->tableWidget->currentRow();

    if (row == -1 || row >= m_measurements.size())
    {
        return;
    }

    QScopedPointer<DialogEditWrongFormula> const dialog(new DialogEditWrongFormula(&m_data, NULL_ID, this));
    dialog->setWindowTitle(tr("Edit measurement"));
    dialog->SetFormula(VTranslateVars::TryFormulaFromUser(ui->plainTextEditFormula->toPlainText(),
                                                          VAbstractApplication::VApp()->Settings()->GetOsSeparator()));
    dialog->ShowPieceArea(true);
    const QString postfix = UnitsToStr(VAbstractValApplication::VApp()->patternUnits(), true);
    dialog->setPostfix(postfix); // Show unit in dialog lable (cm, mm or inch)

    if (dialog->exec() == QDialog::Accepted)
    {
        m_measurements[row].formula = dialog->GetFormula();
        UpdateTree();

        ui->tableWidget->selectRow(row);
        ShowFinalMeasurementDetails();
    }
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::FullUpdateFromFile()
{
    m_data = m_doc->GetCompleteData();
    m_data.FillPiecesAreas(VAbstractValApplication::VApp()->patternUnits());
    m_measurements = m_doc->GetFinalMeasurements();

    FillFinalMeasurements();

    m_search->RefreshList(ui->lineEditFind->text());
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::UpdateShortcuts()
{
    if (VAbstractShortcutManager *manager = VAbstractApplication::VApp()->GetShortcutManager())
    {
        manager->UpdateButtonShortcut(m_shortcuts);
        UpdateSearchControlsTooltips();
    }
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::FillFinalMeasurements(bool freshCall)
{
    ui->tableWidget->blockSignals(true);
    ui->tableWidget->clearContents();

    ui->tableWidget->setRowCount(static_cast<int>(m_measurements.size()));
    for (int i = 0; i < m_measurements.size(); ++i)
    {
        const VFinalMeasurement &m = m_measurements.at(i);

        AddCell(m.name, i, 0, Qt::AlignVCenter); // name

        bool ok = true;
        const qreal result = EvalFormula(m.formula, ok);
        AddCell(VAbstractApplication::VApp()->LocaleToString(result), i, 1, Qt::AlignHCenter | Qt::AlignVCenter,
                ok); // calculated value

        const QString formula =
            VTranslateVars::TryFormulaFromUser(m.formula, VAbstractApplication::VApp()->Settings()->GetOsSeparator());
        AddCell(formula, i, 2, Qt::AlignVCenter); // formula
    }

    if (freshCall)
    {
        ui->tableWidget->resizeColumnsToContents();
        ui->tableWidget->resizeRowsToContents();
    }
    ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
    ui->tableWidget->blockSignals(false);
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::ShowUnits()
{
    const QString unit = UnitsToStr(VAbstractValApplication::VApp()->patternUnits());

    {
        // calculated value
        const QString header = ui->tableWidget->horizontalHeaderItem(1)->text();
        const auto unitHeader = QStringLiteral("%1 (%2)").arg(header, unit);
        ui->tableWidget->horizontalHeaderItem(1)->setText(unitHeader);
    }

    {
        // formula
        const QString header = ui->tableWidget->horizontalHeaderItem(2)->text();
        const auto unitHeader = QStringLiteral("%1 (%2)").arg(header, unit);
        ui->tableWidget->horizontalHeaderItem(2)->setText(unitHeader);
    }
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::AddCell(const QString &text, int row, int column, int aligment, bool ok)
{
    auto *item = new QTableWidgetItem(text);
    SetTextAlignment(item, static_cast<Qt::Alignment>(aligment));

    // set the item non-editable (view only), and non-selectable
    Qt::ItemFlags flags = item->flags();
    flags &= ~(Qt::ItemIsEditable); // reset/clear the flag
    item->setFlags(flags);

    if (not ok)
    {
        QBrush brush = item->foreground();
        brush.setColor(Qt::red);
        item->setForeground(brush);
    }

    ui->tableWidget->setItem(row, column, item);
}

//---------------------------------------------------------------------------------------------------------------------
auto DialogFinalMeasurements::EvalUserFormula(const QString &formula, bool fromUser) -> bool
{
    const QString postfix =
        UnitsToStr(VAbstractValApplication::VApp()->patternUnits()); // Show unit in dialog lable (cm, mm or inch)
    if (formula.isEmpty())
    {
        ui->labelCalculatedValue->setText(tr("Error") + " (" + postfix + "). " + tr("Empty field."));
        ui->labelCalculatedValue->setToolTip(tr("Empty field"));
        return false;
    }

    try
    {
        QString f;
        // Replace line return character with spaces for calc if exist
        if (fromUser)
        {
            f = VAbstractApplication::VApp()->TrVars()->FormulaFromUser(
                formula, VAbstractApplication::VApp()->Settings()->GetOsSeparator());
        }
        else
        {
            f = formula;
        }
        QScopedPointer<Calculator> const cal(new Calculator());
        const qreal result = cal->EvalFormula(m_data.DataVariables(), f);

        if (qIsInf(result) || qIsNaN(result))
        {
            ui->labelCalculatedValue->setText(tr("Error") + " (" + postfix + ").");
            ui->labelCalculatedValue->setToolTip(tr("Invalid result. Value is infinite or NaN. Please, check your "
                                                    "calculations."));
            return false;
        }

        ui->labelCalculatedValue->setText(VAbstractApplication::VApp()->LocaleToString(result) + QChar(QChar::Space) +
                                          postfix);
        ui->labelCalculatedValue->setToolTip(tr("Value"));
        return true;
    }
    catch (qmu::QmuParserError &e)
    {
        ui->labelCalculatedValue->setText(tr("Error") + " (" + postfix + "). " +
                                          tr("Parser error: %1").arg(e.GetMsg()));
        ui->labelCalculatedValue->setToolTip(tr("Parser error: %1").arg(e.GetMsg()));
        return false;
    }
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::Controls()
{
    ui->toolButtonRemove->setEnabled(ui->tableWidget->rowCount() > 0);

    if (ui->tableWidget->rowCount() >= 2)
    {
        if (ui->tableWidget->currentRow() == 0)
        {
            ui->toolButtonUp->setEnabled(false);
            ui->toolButtonDown->setEnabled(true);
        }
        else if (ui->tableWidget->currentRow() == ui->tableWidget->rowCount() - 1)
        {
            ui->toolButtonUp->setEnabled(true);
            ui->toolButtonDown->setEnabled(false);
        }
        else
        {
            ui->toolButtonUp->setEnabled(true);
            ui->toolButtonDown->setEnabled(true);
        }
    }
    else
    {
        ui->toolButtonUp->setEnabled(false);
        ui->toolButtonDown->setEnabled(false);
    }
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::EnableDetails(bool enabled)
{
    if (enabled)
    {
        Controls();
    }
    else
    {
        ui->toolButtonRemove->setEnabled(enabled);
        ui->toolButtonUp->setEnabled(enabled);
        ui->toolButtonDown->setEnabled(enabled);
    }

    if (not enabled)
    { // Clear
        ui->lineEditName->blockSignals(true);
        ui->lineEditName->clear();
        ui->lineEditName->blockSignals(false);

        ui->plainTextEditDescription->blockSignals(true);
        ui->plainTextEditDescription->clear();
        ui->plainTextEditDescription->blockSignals(false);

        ui->labelCalculatedValue->blockSignals(true);
        ui->labelCalculatedValue->clear();
        ui->labelCalculatedValue->blockSignals(false);

        ui->plainTextEditFormula->blockSignals(true);
        ui->plainTextEditFormula->clear();
        ui->plainTextEditFormula->blockSignals(false);
    }

    ui->pushButtonGrow->setEnabled(enabled);
    ui->toolButtonExpr->setEnabled(enabled);
    ui->lineEditName->setEnabled(enabled);
    ui->plainTextEditDescription->setEnabled(enabled);
    ui->plainTextEditFormula->setEnabled(enabled);
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::UpdateTree()
{
    int const row = ui->tableWidget->currentRow();
    FillFinalMeasurements();
    ui->tableWidget->blockSignals(true);
    ui->tableWidget->selectRow(row);
    ui->tableWidget->blockSignals(false);

    m_search->RefreshList(ui->lineEditFind->text());
}

//---------------------------------------------------------------------------------------------------------------------
auto DialogFinalMeasurements::EvalFormula(const QString &formula, bool &ok) -> qreal
{
    qreal result = 0;
    if (formula.isEmpty())
    {
        ok = false;
        return result;
    }

    try
    {
        // Replace line return character with spaces for calc if exist
        QScopedPointer<Calculator> const cal(new Calculator());
        result = cal->EvalFormula(m_data.DataVariables(), formula);

        if (qIsInf(result) || qIsNaN(result))
        {
            ok = false;
            return 0;
        }
    }
    catch (qmu::QmuParserError &)
    {
        ok = false;
        return 0;
    }

    ok = true;
    return result;
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::InitSearch()
{
    m_search = QSharedPointer<VTableSearch>(new VTableSearch(ui->tableWidget));

    VValentinaSettings *settings = VAbstractValApplication::VApp()->ValentinaSettings();
    m_search->SetUseUnicodePreperties(settings->GetFinalMeasurementsSearchOptionUseUnicodeProperties());
    m_search->SetMatchWord(settings->GetFinalMeasurementsSearchOptionWholeWord());
    m_search->SetMatchRegexp(settings->GetFinalMeasurementsSearchOptionRegexp());
    m_search->SetMatchCase(settings->GetFinalMeasurementsSearchOptionMatchCase());

    ui->lineEditFind->setPlaceholderText(m_search->SearchPlaceholder());

    UpdateSearchControlsTooltips();

    connect(ui->lineEditFind, &QLineEdit::textEdited, this, [this](const QString &term) { m_search->Find(term); });
    connect(ui->lineEditFind, &QLineEdit::editingFinished, this,
            [this]()
            {
                SaveSearchRequest();
                InitSearchHistory();
                m_search->Find(ui->lineEditFind->text());
            });
    connect(ui->toolButtonFindPrevious, &QToolButton::clicked, this,
            [this]()
            {
                SaveSearchRequest();
                InitSearchHistory();
                m_search->FindPrevious();
                ui->labelResults->setText(
                    QStringLiteral("%1/%2").arg(m_search->MatchIndex() + 1).arg(m_search->MatchCount()));
            });
    connect(ui->toolButtonFindNext, &QToolButton::clicked, this,
            [this]()
            {
                SaveSearchRequest();
                InitSearchHistory();
                m_search->FindNext();
                ui->labelResults->setText(
                    QStringLiteral("%1/%2").arg(m_search->MatchIndex() + 1).arg(m_search->MatchCount()));
            });

    connect(m_search.data(), &VTableSearch::HasResult, this,
            [this](bool state)
            {
                ui->toolButtonFindPrevious->setEnabled(state);
                ui->toolButtonFindNext->setEnabled(state);

                if (state)
                {
                    ui->labelResults->setText(
                        QStringLiteral("%1/%2").arg(m_search->MatchIndex() + 1).arg(m_search->MatchCount()));
                }
                else
                {
                    ui->labelResults->setText(tr("0 results"));
                }

                QPalette palette;

                if (not state && not ui->lineEditFind->text().isEmpty())
                {
                    palette.setColor(QPalette::Text, Qt::red);
                    ui->lineEditFind->setPalette(palette);

                    palette.setColor(QPalette::Active, ui->labelResults->foregroundRole(), Qt::red);
                    palette.setColor(QPalette::Inactive, ui->labelResults->foregroundRole(), Qt::red);
                    ui->labelResults->setPalette(palette);
                }
                else
                {
                    ui->lineEditFind->setPalette(palette);
                    ui->labelResults->setPalette(palette);
                }
            });

    connect(ui->toolButtonCaseSensitive, &QToolButton::toggled, this,
            [this](bool checked)
            {
                m_search->SetMatchCase(checked);
                m_search->Find(ui->lineEditFind->text());
                ui->lineEditFind->setPlaceholderText(m_search->SearchPlaceholder());
            });

    connect(ui->toolButtonWholeWord, &QToolButton::toggled, this,
            [this](bool checked)
            {
                m_search->SetMatchWord(checked);
                m_search->Find(ui->lineEditFind->text());
                ui->lineEditFind->setPlaceholderText(m_search->SearchPlaceholder());
            });

    connect(ui->toolButtonRegexp, &QToolButton::toggled, this,
            [this](bool checked)
            {
                m_search->SetMatchRegexp(checked);

                if (checked)
                {
                    ui->toolButtonWholeWord->blockSignals(true);
                    ui->toolButtonWholeWord->setChecked(false);
                    ui->toolButtonWholeWord->blockSignals(false);
                    ui->toolButtonWholeWord->setEnabled(false);

                    ui->toolButtonUseUnicodeProperties->setEnabled(true);
                }
                else
                {
                    ui->toolButtonWholeWord->setEnabled(true);
                    ui->toolButtonUseUnicodeProperties->blockSignals(true);
                    ui->toolButtonUseUnicodeProperties->setChecked(false);
                    ui->toolButtonUseUnicodeProperties->blockSignals(false);
                    ui->toolButtonUseUnicodeProperties->setEnabled(false);
                }
                m_search->Find(ui->lineEditFind->text());
                ui->lineEditFind->setPlaceholderText(m_search->SearchPlaceholder());
            });

    connect(ui->toolButtonUseUnicodeProperties, &QToolButton::toggled, this,
            [this](bool checked)
            {
                m_search->SetUseUnicodePreperties(checked);
                m_search->Find(ui->lineEditFind->text());
            });

    m_searchHistory->setStyleSheet(QStringLiteral("QMenu { menu-scrollable: 1; }"));
    InitSearchHistory();
    ui->pushButtonSearch->setMenu(m_searchHistory);
    ui->pushButtonSearch->setProperty("hasMenu", true);
    ui->pushButtonSearch->style()->unpolish(ui->pushButtonSearch);
    ui->pushButtonSearch->style()->polish(ui->pushButtonSearch);
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::InitSearchHistory()
{
    QStringList const searchHistory =
        VAbstractValApplication::VApp()->ValentinaSettings()->GetFinalMeasurementsSearchHistory();
    m_searchHistory->clear();

    if (searchHistory.isEmpty())
    {
        QAction *action = m_searchHistory->addAction('<'_L1 + tr("Empty", "list") + '>'_L1);
        action->setDisabled(true);
        return;
    }

    for (const auto &term : searchHistory)
    {
        QAction *action = m_searchHistory->addAction(term);
        action->setData(term);
        connect(action, &QAction::triggered, this,
                [this]()
                {
                    auto *action = qobject_cast<QAction *>(sender());
                    if (action != nullptr)
                    {
                        QString const term = action->data().toString();
                        ui->lineEditFind->setText(term);
                        m_search->Find(term);
                        ui->lineEditFind->setFocus();
                    }
                });
    }
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::SaveSearchRequest()
{
    QStringList searchHistory =
        VAbstractValApplication::VApp()->ValentinaSettings()->GetFinalMeasurementsSearchHistory();
    QString const term = ui->lineEditFind->text();
    if (term.isEmpty())
    {
        return;
    }

    searchHistory.removeAll(term);
    searchHistory.prepend(term);
    while (searchHistory.size() > VTableSearch::MaxHistoryRecords)
    {
        searchHistory.removeLast();
    }
    VAbstractValApplication::VApp()->ValentinaSettings()->SetFinalMeasurementsSearchHistory(searchHistory);
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::UpdateSearchControlsTooltips()
{
    auto UpdateToolTip = [this](QAbstractButton *button)
    {
        if (button->toolTip().contains("%1"_L1))
        {
            m_serachButtonTooltips.insert(button, button->toolTip());
            button->setToolTip(button->toolTip().arg(button->shortcut().toString(QKeySequence::NativeText)));
        }
        else if (m_serachButtonTooltips.contains(button))
        {
            QString const tooltip = m_serachButtonTooltips.value(button);
            button->setToolTip(tooltip.arg(button->shortcut().toString(QKeySequence::NativeText)));
        }
    };

    UpdateToolTip(ui->toolButtonCaseSensitive);
    UpdateToolTip(ui->toolButtonWholeWord);
    UpdateToolTip(ui->toolButtonRegexp);
    UpdateToolTip(ui->toolButtonUseUnicodeProperties);
    UpdateToolTip(ui->pushButtonSearch);
    UpdateToolTip(ui->toolButtonFindPrevious);
    UpdateToolTip(ui->toolButtonFindNext);
}

//---------------------------------------------------------------------------------------------------------------------
void DialogFinalMeasurements::InitIcons()
{
    auto const resource = QStringLiteral("icon");

    ui->toolButtonExpr->setIcon(VTheme::GetIconResource(resource, QStringLiteral("24x24/fx.png")));
}
