/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * 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/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#undef SC_DLLIMPLEMENTATION

#include <pvfundlg.hxx>

#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>

#include <osl/diagnose.h>

#include <scresid.hxx>
#include <dpobject.hxx>
#include <dpsave.hxx>
#include <pvfundlg.hrc>
#include <globstr.hrc>
#include <dputil.hxx>

#include <vector>

using namespace ::com::sun::star::sheet;

using ::com::sun::star::uno::Sequence;

namespace {

/** Appends all strings from the Sequence to the list box.

    Empty strings are replaced by a localized "(empty)" entry and inserted at
    the specified position.

    @return  true = The passed string list contains an empty string entry.
 */

bool lclFillListBox(weld::ComboBox& rLBox, const Sequence< OUString >& rStrings)
{
    bool bEmpty = false;
    for (const OUString& str : rStrings)
    {
        if (!str.isEmpty())
            rLBox.append_text(str);
        else
        {
            rLBox.append_text(ScResId(STR_EMPTYDATA));
            bEmpty = true;
        }
    }
    return bEmpty;
}

bool lclFillListBox(weld::ComboBox& rLBox, const std::vector<ScDPLabelData::Member>& rMembers, int nEmptyPos)
{
    bool bEmpty = false;
    std::vector<ScDPLabelData::Member>::const_iterator itr = rMembers.begin(), itrEnd = rMembers.end();
    for (; itr != itrEnd; ++itr)
    {
        OUString aName = itr->getDisplayName();
        if (!aName.isEmpty())
            rLBox.append_text(aName);
        else
        {
            rLBox.insert_text(nEmptyPos, ScResId(STR_EMPTYDATA));
            bEmpty = true;
        }
    }
    return bEmpty;
}

bool lclFillListBox(weld::TreeView& rLBox, const std::vector<ScDPLabelData::Member>& rMembers)
{
    bool bEmpty = false;
    for (const auto& rMember : rMembers)
    {
        rLBox.append();
        int pos = rLBox.n_children() - 1;
        rLBox.set_toggle(pos, TRISTATE_FALSE);
        const OUString& aName = rMember.getDisplayName();
        if (!aName.isEmpty())
            rLBox.set_text(pos, aName, 0);
        else
        {
            rLBox.set_text(pos, ScResId(STR_EMPTYDATA), 0);
            bEmpty = true;
        }
    }
    return bEmpty;
}

/** This table represents the order of the strings in the resource string array. */
const PivotFunc spnFunctions[] =
{
    PivotFunc::Sum,
    PivotFunc::Count,
    PivotFunc::Average,
    PivotFunc::Median,
    PivotFunc::Max,
    PivotFunc::Min,
    PivotFunc::Product,
    PivotFunc::CountNum,
    PivotFunc::StdDev,
    PivotFunc::StdDevP,
    PivotFunc::StdVar,
    PivotFunc::StdVarP
};

const sal_uInt16 SC_BASEITEM_PREV_POS = 0;
const sal_uInt16 SC_BASEITEM_NEXT_POS = 1;
const sal_uInt16 SC_BASEITEM_USER_POS = 2;

const sal_uInt16 SC_SORTNAME_POS = 0;
const sal_uInt16 SC_SORTDATA_POS = 1;

const tools::Long SC_SHOW_DEFAULT = 10;

} // namespace

ScDPFunctionListBox::ScDPFunctionListBox(std::unique_ptr<weld::TreeView> xControl)
    : m_xControl(std::move(xControl))
{
    FillFunctionNames();
}

void ScDPFunctionListBox::SetSelection( PivotFunc nFuncMask )
{
    if( (nFuncMask == PivotFunc::NONE) || (nFuncMask == PivotFunc::Auto) )
        m_xControl->unselect_all();
    else
    {
        for( sal_Int32 nEntry = 0, nCount = m_xControl->n_children(); nEntry < nCount; ++nEntry )
        {
            if (bool(nFuncMask & spnFunctions[ nEntry ]))
                m_xControl->select(nEntry);
            else
                m_xControl->unselect(nEntry);
        }
    }
}

PivotFunc ScDPFunctionListBox::GetSelection() const
{
    PivotFunc nFuncMask = PivotFunc::NONE;
    std::vector<int> aRows = m_xControl->get_selected_rows();
    for (int nSel : aRows)
        nFuncMask |= spnFunctions[nSel];
    return nFuncMask;
}

void ScDPFunctionListBox::FillFunctionNames()
{
    OSL_ENSURE( !m_xControl->n_children(), "ScDPMultiFuncListBox::FillFunctionNames - do not add texts to resource" );
    m_xControl->clear();
    m_xControl->freeze();
    for (size_t nIndex = 0; nIndex < SAL_N_ELEMENTS(SCSTR_DPFUNCLISTBOX); ++nIndex)
        m_xControl->append_text(ScResId(SCSTR_DPFUNCLISTBOX[nIndex]));
    m_xControl->thaw();
    assert(m_xControl->n_children() == SAL_N_ELEMENTS(spnFunctions));
}

namespace
{
    int FromDataPilotFieldReferenceType(int eMode)
    {
        switch (eMode)
        {
            case DataPilotFieldReferenceType::NONE:
                return 0;
            case DataPilotFieldReferenceType::ITEM_DIFFERENCE:
                return 1;
            case DataPilotFieldReferenceType::ITEM_PERCENTAGE:
                return 2;
            case DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
                return 3;
            case DataPilotFieldReferenceType::RUNNING_TOTAL:
                return 4;
            case DataPilotFieldReferenceType::ROW_PERCENTAGE:
                return 5;
            case DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
                return 6;
            case DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
                return 7;
            case DataPilotFieldReferenceType::INDEX:
                return 8;
        }
        return -1;
    }

    int ToDataPilotFieldReferenceType(int nPos)
    {
        switch (nPos)
        {
            case 0:
                return DataPilotFieldReferenceType::NONE;
            case 1:
                return DataPilotFieldReferenceType::ITEM_DIFFERENCE;
            case 2:
                return DataPilotFieldReferenceType::ITEM_PERCENTAGE;
            case 3:
                return DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE;
            case 4:
                return DataPilotFieldReferenceType::RUNNING_TOTAL;
            case 5:
                return DataPilotFieldReferenceType::ROW_PERCENTAGE;
            case 6:
                return DataPilotFieldReferenceType::COLUMN_PERCENTAGE;
            case 7:
                return DataPilotFieldReferenceType::TOTAL_PERCENTAGE;
            case 8:
                return DataPilotFieldReferenceType::INDEX;
        }
        return DataPilotFieldReferenceType::NONE;

    }
}

ScDPFunctionDlg::ScDPFunctionDlg(
        weld::Widget* pParent, const ScDPLabelDataVector& rLabelVec,
        const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData)
    : GenericDialogController(pParent, u"modules/scalc/ui/datafielddialog.ui"_ustr, u"DataFieldDialog"_ustr)
    , mxLbFunc(new ScDPFunctionListBox(m_xBuilder->weld_tree_view(u"functions"_ustr)))
    , mxFtName(m_xBuilder->weld_label(u"name"_ustr))
    , mxLbType(m_xBuilder->weld_combo_box(u"type"_ustr))
    , mxFtBaseField(m_xBuilder->weld_label(u"basefieldft"_ustr))
    , mxLbBaseField(m_xBuilder->weld_combo_box(u"basefield"_ustr))
    , mxFtBaseItem(m_xBuilder->weld_label(u"baseitemft"_ustr))
    , mxLbBaseItem(m_xBuilder->weld_combo_box(u"baseitem"_ustr))
    , mxBtnOk(m_xBuilder->weld_button(u"ok"_ustr))
    , mxBtnCancel(m_xBuilder->weld_button(u"cancel"_ustr))
    , mxExpander(m_xBuilder->weld_expander(u"expander"_ustr))
    , mrLabelVec(rLabelVec)
    , mbEmptyItem(false)
{
    mxLbFunc->set_size_request(-1, mxLbFunc->get_height_rows(8));

    Init(rLabelData, rFuncData);
}

ScDPFunctionDlg::~ScDPFunctionDlg()
{
}

PivotFunc ScDPFunctionDlg::GetFuncMask() const
{
    return mxLbFunc->GetSelection();
}

DataPilotFieldReference ScDPFunctionDlg::GetFieldRef() const
{
    DataPilotFieldReference aRef;

    aRef.ReferenceType = ToDataPilotFieldReferenceType(mxLbType->get_active());
    aRef.ReferenceField = GetBaseFieldName(mxLbBaseField->get_active_text());

    sal_Int32 nBaseItemPos = mxLbBaseItem->get_active();
    switch( nBaseItemPos )
    {
        case SC_BASEITEM_PREV_POS:
            aRef.ReferenceItemType = DataPilotFieldReferenceItemType::PREVIOUS;
        break;
        case SC_BASEITEM_NEXT_POS:
            aRef.ReferenceItemType = DataPilotFieldReferenceItemType::NEXT;
        break;
        default:
        {
            aRef.ReferenceItemType = DataPilotFieldReferenceItemType::NAMED;
            if( !mbEmptyItem || (nBaseItemPos > SC_BASEITEM_USER_POS) )
                aRef.ReferenceItemName = GetBaseItemName(mxLbBaseItem->get_active_text());
        }
    }

    return aRef;
}

void ScDPFunctionDlg::Init( const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData )
{
    mxBtnOk->connect_clicked( LINK( this, ScDPFunctionDlg, ButtonClicked ) );
    mxBtnCancel->connect_clicked( LINK( this, ScDPFunctionDlg, ButtonClicked ) );

    // list box
    PivotFunc nFuncMask = (rFuncData.mnFuncMask == PivotFunc::NONE) ? PivotFunc::Sum : rFuncData.mnFuncMask;
    mxLbFunc->SetSelection( nFuncMask );

    // field name
    mxFtName->set_label(rLabelData.getDisplayName());

    // handlers
    mxLbFunc->connect_row_activated( LINK( this, ScDPFunctionDlg, DblClickHdl ) );
    mxLbType->connect_changed( LINK( this, ScDPFunctionDlg, SelectHdl ) );
    mxLbBaseField->connect_changed( LINK( this, ScDPFunctionDlg, SelectHdl ) );

    // base field list box
    OUString aSelectedEntry;
    for( const auto& rxLabel : mrLabelVec )
    {
        mxLbBaseField->append_text(rxLabel->getDisplayName());
        maBaseFieldNameMap.emplace(rxLabel->getDisplayName(), rxLabel->maName);
        if (rxLabel->maName == rFuncData.maFieldRef.ReferenceField)
            aSelectedEntry = rxLabel->getDisplayName();
    }

    // select field reference type
    mxLbType->set_active(FromDataPilotFieldReferenceType(rFuncData.maFieldRef.ReferenceType));
    SelectHdl( *mxLbType );         // enables base field/item list boxes

    // select base field
    mxLbBaseField->set_active_text(aSelectedEntry);
    if (mxLbBaseField->get_active() == -1)
        mxLbBaseField->set_active(0);
    SelectHdl( *mxLbBaseField );    // fills base item list, selects base item

    // select base item
    switch( rFuncData.maFieldRef.ReferenceItemType )
    {
        case DataPilotFieldReferenceItemType::PREVIOUS:
            mxLbBaseItem->set_active( SC_BASEITEM_PREV_POS );
        break;
        case DataPilotFieldReferenceItemType::NEXT:
            mxLbBaseItem->set_active( SC_BASEITEM_NEXT_POS );
        break;
        default:
        {
            if( mbEmptyItem && rFuncData.maFieldRef.ReferenceItemName.isEmpty() )
            {
                // select special "(empty)" entry added before other items
                mxLbBaseItem->set_active( SC_BASEITEM_USER_POS );
            }
            else
            {
                sal_Int32 nStartPos = mbEmptyItem ? (SC_BASEITEM_USER_POS + 1) : SC_BASEITEM_USER_POS;
                sal_Int32 nPos = FindBaseItemPos( rFuncData.maFieldRef.ReferenceItemName, nStartPos );
                if( nPos == -1)
                    nPos = (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS) ? SC_BASEITEM_USER_POS : SC_BASEITEM_PREV_POS;
                mxLbBaseItem->set_active( nPos );
            }
        }
    }
}

const OUString& ScDPFunctionDlg::GetBaseFieldName(const OUString& rLayoutName) const
{
    NameMapType::const_iterator itr = maBaseFieldNameMap.find(rLayoutName);
    return itr == maBaseFieldNameMap.end() ? rLayoutName : itr->second;
}

const OUString& ScDPFunctionDlg::GetBaseItemName(const OUString& rLayoutName) const
{
    NameMapType::const_iterator itr = maBaseItemNameMap.find(rLayoutName);
    return itr == maBaseItemNameMap.end() ? rLayoutName : itr->second;
}

sal_Int32 ScDPFunctionDlg::FindBaseItemPos( std::u16string_view rEntry, sal_Int32 nStartPos ) const
{
    sal_Int32 nPos = nStartPos;
    bool bFound = false;
    while (nPos < mxLbBaseItem->get_count())
    {
        // translate the displayed field name back to its original field name.
        const OUString aInName = mxLbBaseItem->get_text(nPos);
        const OUString& rName = GetBaseItemName(aInName);
        if (rName == rEntry)
        {
            bFound = true;
            break;
        }
        ++nPos;
    }
    return bFound ? nPos : -1;
}

IMPL_LINK( ScDPFunctionDlg, SelectHdl, weld::ComboBox&, rLBox, void )
{
    if (&rLBox == mxLbType.get())
    {
        bool bEnableField, bEnableItem;
        switch (ToDataPilotFieldReferenceType(mxLbType->get_active()))
        {
            case DataPilotFieldReferenceType::ITEM_DIFFERENCE:
            case DataPilotFieldReferenceType::ITEM_PERCENTAGE:
            case DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
                bEnableField = bEnableItem = true;
            break;

            case DataPilotFieldReferenceType::RUNNING_TOTAL:
                bEnableField = true;
                bEnableItem = false;
            break;

            default:
                bEnableField = bEnableItem = false;
        }

        bEnableField &= (mxLbBaseField->get_count() > 0);
        mxFtBaseField->set_sensitive( bEnableField );
        mxLbBaseField->set_sensitive( bEnableField );

        bEnableItem &= bEnableField;
        mxFtBaseItem->set_sensitive( bEnableItem );
        mxLbBaseItem->set_sensitive( bEnableItem );
    }
    else if (&rLBox == mxLbBaseField.get())
    {
        // keep "previous" and "next" entries
        while (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS)
             mxLbBaseItem->remove(SC_BASEITEM_USER_POS);

        // update item list for current base field
        mbEmptyItem = false;
        size_t nBasePos = mxLbBaseField->get_active();
        if (nBasePos < mrLabelVec.size())
        {
            const std::vector<ScDPLabelData::Member>& rMembers = mrLabelVec[nBasePos]->maMembers;
            mbEmptyItem = lclFillListBox(*mxLbBaseItem, rMembers, SC_BASEITEM_USER_POS);
            // build cache for base names.
            NameMapType aMap;
            for (const auto& rMember : rMembers)
                aMap.emplace(rMember.getDisplayName(), rMember.maName);
            maBaseItemNameMap.swap(aMap);
        }

        // select base item
        sal_uInt16 nItemPos = (mxLbBaseItem->get_count() > SC_BASEITEM_USER_POS) ? SC_BASEITEM_USER_POS : SC_BASEITEM_PREV_POS;
        mxLbBaseItem->set_active( nItemPos );
    }
}

IMPL_LINK(ScDPFunctionDlg, ButtonClicked, weld::Button&, rButton, void)
{
    if (&rButton == mxBtnOk.get())
        response(RET_OK);
    else
        response(RET_CANCEL);
}

IMPL_LINK_NOARG(ScDPFunctionDlg, DblClickHdl, weld::TreeView&, bool)
{
    m_xDialog->response(RET_OK);
    return true;
}

ScDPSubtotalDlg::ScDPSubtotalDlg(weld::Widget* pParent, ScDPObject& rDPObj,
        const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData,
        const ScDPNameVec& rDataFields, bool bEnableLayout)
    : GenericDialogController(pParent, u"modules/scalc/ui/pivotfielddialog.ui"_ustr, u"PivotFieldDialog"_ustr)
    , mrDPObj(rDPObj)
    , mrDataFields(rDataFields)
    , maLabelData(rLabelData)
    , mbEnableLayout(bEnableLayout)
    , mxRbNone(m_xBuilder->weld_radio_button(u"none"_ustr))
    , mxRbAuto(m_xBuilder->weld_radio_button(u"auto"_ustr))
    , mxRbUser(m_xBuilder->weld_radio_button(u"user"_ustr))
    , mxLbFunc(new ScDPFunctionListBox(m_xBuilder->weld_tree_view(u"functions"_ustr)))
    , mxFtName(m_xBuilder->weld_label(u"name"_ustr))
    , mxCbShowAll(m_xBuilder->weld_check_button(u"showall"_ustr))
    , mxBtnOk(m_xBuilder->weld_button(u"ok"_ustr))
    , mxBtnCancel(m_xBuilder->weld_button(u"cancel"_ustr))
    , mxBtnOptions(m_xBuilder->weld_button(u"options"_ustr))
{
    mxLbFunc->set_selection_mode(SelectionMode::Multiple);
    mxLbFunc->set_size_request(-1, mxLbFunc->get_height_rows(8));
    Init(rLabelData, rFuncData);
}

ScDPSubtotalDlg::~ScDPSubtotalDlg()
{
    CloseSubdialog();
}

void ScDPSubtotalDlg::CloseSubdialog()
{
    if (mxOptionsDlg && mxOptionsDlg->getDialog())
    {
        mxOptionsDlg->getDialog()->response(RET_CANCEL);
        mxOptionsDlg = nullptr;
    }
}

PivotFunc ScDPSubtotalDlg::GetFuncMask() const
{
    PivotFunc nFuncMask = PivotFunc::NONE;

    if (mxRbAuto->get_active())
        nFuncMask = PivotFunc::Auto;
    else if (mxRbUser->get_active())
        nFuncMask = mxLbFunc->GetSelection();

    return nFuncMask;
}

void ScDPSubtotalDlg::FillLabelData( ScDPLabelData& rLabelData ) const
{
    rLabelData.mnFuncMask = GetFuncMask();
    rLabelData.mnUsedHier = maLabelData.mnUsedHier;
    rLabelData.mbShowAll = mxCbShowAll->get_active();
    rLabelData.maMembers = maLabelData.maMembers;
    rLabelData.maSortInfo = maLabelData.maSortInfo;
    rLabelData.maLayoutInfo = maLabelData.maLayoutInfo;
    rLabelData.maShowInfo = maLabelData.maShowInfo;
    rLabelData.mbRepeatItemLabels = maLabelData.mbRepeatItemLabels;
}

void ScDPSubtotalDlg::Init( const ScDPLabelData& rLabelData, const ScPivotFuncData& rFuncData )
{
    mxBtnOk->connect_clicked( LINK( this, ScDPSubtotalDlg, ButtonClicked ) );
    mxBtnCancel->connect_clicked( LINK( this, ScDPSubtotalDlg, ButtonClicked ) );

    // field name
    mxFtName->set_label(rLabelData.getDisplayName());

    // radio buttons
    mxRbNone->connect_toggled( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );
    mxRbAuto->connect_toggled( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );
    mxRbUser->connect_toggled( LINK( this, ScDPSubtotalDlg, RadioClickHdl ) );

    weld::RadioButton* pRBtn = nullptr;
    switch( rFuncData.mnFuncMask )
    {
        case PivotFunc::NONE:   pRBtn = mxRbNone.get();  break;
        case PivotFunc::Auto:   pRBtn = mxRbAuto.get();  break;
        default:                pRBtn = mxRbUser.get();
    }
    pRBtn->set_active(true);
    RadioClickHdl(*pRBtn);

    // list box
    mxLbFunc->SetSelection( rFuncData.mnFuncMask );
    mxLbFunc->connect_row_activated( LINK( this, ScDPSubtotalDlg, DblClickHdl ) );

    // show all
    mxCbShowAll->set_active( rLabelData.mbShowAll );

    // options
    mxBtnOptions->connect_clicked( LINK( this, ScDPSubtotalDlg, ClickHdl ) );
}

IMPL_LINK(ScDPSubtotalDlg, ButtonClicked, weld::Button&, rButton, void)
{
    CloseSubdialog();

    if (&rButton == mxBtnOk.get())
        response(RET_OK);
    else
        response(RET_CANCEL);
}

IMPL_LINK(ScDPSubtotalDlg, RadioClickHdl, weld::Toggleable&, rBtn, void)
{
    if (!rBtn.get_active())
        return;
    mxLbFunc->set_sensitive(mxRbUser->get_active());
}

IMPL_LINK_NOARG(ScDPSubtotalDlg, DblClickHdl, weld::TreeView&, bool)
{
    m_xDialog->response(RET_OK);
    return true;
}

IMPL_LINK(ScDPSubtotalDlg, ClickHdl, weld::Button&, rBtn, void)
{
    if (&rBtn == mxBtnOptions.get())
    {
        mxOptionsDlg = std::make_shared<ScDPSubtotalOptDlg>(m_xDialog.get(), mrDPObj, maLabelData, mrDataFields, mbEnableLayout);

        weld::DialogController::runAsync(mxOptionsDlg, [this](int nResult) {
            if (nResult == RET_OK)
                mxOptionsDlg->FillLabelData(maLabelData);
            mxOptionsDlg = nullptr;
        });
    }
}

namespace
{
    int FromDataPilotFieldLayoutMode(int eMode)
    {
        switch (eMode)
        {
            case DataPilotFieldLayoutMode::TABULAR_LAYOUT:
                return 0;
            case DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP:
                return 1;
            case DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM:
                return 2;
            case DataPilotFieldLayoutMode::COMPACT_LAYOUT:
                return 3;
        }
        return -1;
    }

    int ToDataPilotFieldLayoutMode(int nPos)
    {
        switch (nPos)
        {
            case 0:
                return DataPilotFieldLayoutMode::TABULAR_LAYOUT;
            case 1:
                return DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
            case 2:
                return DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
            case 3:
                return DataPilotFieldLayoutMode::COMPACT_LAYOUT;
        }
        return DataPilotFieldLayoutMode::TABULAR_LAYOUT;
    }

    int FromDataPilotFieldShowItemsMode(int eMode)
    {
        switch (eMode)
        {
            case DataPilotFieldShowItemsMode::FROM_TOP:
                return 0;
            case DataPilotFieldShowItemsMode::FROM_BOTTOM:
                return 1;
        }
        return -1;
    }

    int ToDataPilotFieldShowItemsMode(int nPos)
    {
        switch (nPos)
        {
            case 0:
                return DataPilotFieldShowItemsMode::FROM_TOP;
            case 1:
                return DataPilotFieldShowItemsMode::FROM_BOTTOM;
        }
        return DataPilotFieldShowItemsMode::FROM_TOP;
    }
}

ScDPSubtotalOptDlg::ScDPSubtotalOptDlg(weld::Window* pParent, ScDPObject& rDPObj,
        const ScDPLabelData& rLabelData, const ScDPNameVec& rDataFields,
        bool bEnableLayout )
    : GenericDialogController(pParent, u"modules/scalc/ui/datafieldoptionsdialog.ui"_ustr,
                              u"DataFieldOptionsDialog"_ustr)
    , m_xLbSortBy(m_xBuilder->weld_combo_box(u"sortby"_ustr))
    , m_xRbSortAsc(m_xBuilder->weld_radio_button(u"ascending"_ustr))
    , m_xRbSortDesc(m_xBuilder->weld_radio_button(u"descending"_ustr))
    , m_xRbSortMan(m_xBuilder->weld_radio_button(u"manual"_ustr))
    , m_xLayoutFrame(m_xBuilder->weld_widget(u"layoutframe"_ustr))
    , m_xLbLayout(m_xBuilder->weld_combo_box(u"layout"_ustr))
    , m_xCbLayoutEmpty(m_xBuilder->weld_check_button(u"emptyline"_ustr))
    , m_xCbRepeatItemLabels(m_xBuilder->weld_check_button(u"repeatitemlabels"_ustr))
    , m_xCbShow(m_xBuilder->weld_check_button(u"show"_ustr))
    , m_xNfShow(m_xBuilder->weld_spin_button(u"items"_ustr))
    , m_xFtShow(m_xBuilder->weld_label(u"showft"_ustr))
    , m_xFtShowFrom(m_xBuilder->weld_label(u"showfromft"_ustr))
    , m_xLbShowFrom(m_xBuilder->weld_combo_box(u"from"_ustr))
    , m_xFtShowUsing(m_xBuilder->weld_label(u"usingft"_ustr))
    , m_xLbShowUsing(m_xBuilder->weld_combo_box(u"using"_ustr))
    , m_xHideFrame(m_xBuilder->weld_widget(u"hideframe"_ustr))
    , m_xLbHide(m_xBuilder->weld_tree_view(u"hideitems"_ustr))
    , m_xFtHierarchy(m_xBuilder->weld_label(u"hierarchyft"_ustr))
    , m_xLbHierarchy(m_xBuilder->weld_combo_box(u"hierarchy"_ustr))
    , m_xBtnOk(m_xBuilder->weld_button(u"ok"_ustr))
    , m_xBtnCancel(m_xBuilder->weld_button(u"cancel"_ustr))
    , mrDPObj(rDPObj)
    , maLabelData(rLabelData)
{
    m_xLbHide->enable_toggle_buttons(weld::ColumnToggleType::Check);

    m_xLbSortBy->set_size_request(m_xLbSortBy->get_approximate_digit_width() * 18, -1);
    m_xLbHide->set_size_request(-1, m_xLbHide->get_height_rows(5));
    Init(rDataFields, bEnableLayout);
}

ScDPSubtotalOptDlg::~ScDPSubtotalOptDlg()
{
}

void ScDPSubtotalOptDlg::FillLabelData( ScDPLabelData& rLabelData ) const
{
    // *** SORTING ***

    if (m_xRbSortMan->get_active())
        rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::MANUAL;
    else if (m_xLbSortBy->get_active() == SC_SORTNAME_POS)
        rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::NAME;
    else
        rLabelData.maSortInfo.Mode = DataPilotFieldSortMode::DATA;

    ScDPName aFieldName = GetFieldName(m_xLbSortBy->get_active_text());
    if (!aFieldName.maName.isEmpty())
    {
        rLabelData.maSortInfo.Field =
            ScDPUtil::createDuplicateDimensionName(aFieldName.maName, aFieldName.mnDupCount);
    }

    if (rLabelData.maSortInfo.Mode != DataPilotFieldSortMode::MANUAL)
        rLabelData.maSortInfo.IsAscending = m_xRbSortAsc->get_active();

    // *** LAYOUT MODE ***

    rLabelData.maLayoutInfo.LayoutMode = ToDataPilotFieldLayoutMode(m_xLbLayout->get_active());
    rLabelData.maLayoutInfo.AddEmptyLines = m_xCbLayoutEmpty->get_active();
    rLabelData.mbRepeatItemLabels = m_xCbRepeatItemLabels->get_active();

    // *** AUTO SHOW ***

    aFieldName = GetFieldName(m_xLbShowUsing->get_active_text());
    if (!aFieldName.maName.isEmpty())
    {
        rLabelData.maShowInfo.IsEnabled = m_xCbShow->get_active();
        rLabelData.maShowInfo.ShowItemsMode = ToDataPilotFieldShowItemsMode(m_xLbShowFrom->get_active());
        rLabelData.maShowInfo.ItemCount = sal::static_int_cast<sal_Int32>( m_xNfShow->get_value() );
        rLabelData.maShowInfo.DataField =
            ScDPUtil::createDuplicateDimensionName(aFieldName.maName, aFieldName.mnDupCount);
    }

    // *** HIDDEN ITEMS ***

    rLabelData.maMembers = maLabelData.maMembers;
    int nVisCount = m_xLbHide->n_children();
    for (int nPos = 0; nPos < nVisCount; ++nPos)
        rLabelData.maMembers[nPos].mbVisible = m_xLbHide->get_toggle(nPos) == TRISTATE_FALSE;

    // *** HIERARCHY ***

    rLabelData.mnUsedHier = m_xLbHierarchy->get_active() != -1 ? m_xLbHierarchy->get_active() : 0;
}

void ScDPSubtotalOptDlg::Init( const ScDPNameVec& rDataFields, bool bEnableLayout )
{
    m_xBtnOk->connect_clicked(LINK(this, ScDPSubtotalOptDlg, ButtonClicked));
    m_xBtnCancel->connect_clicked(LINK(this, ScDPSubtotalOptDlg, ButtonClicked));

    // *** SORTING ***

    sal_Int32 nSortMode = maLabelData.maSortInfo.Mode;

    // sort fields list box
    m_xLbSortBy->append_text(maLabelData.getDisplayName());

    for( const auto& rDataField : rDataFields )
    {
        // Cache names for later lookup.
        maDataFieldNameMap.emplace(rDataField.maLayoutName, rDataField);

        m_xLbSortBy->append_text(rDataField.maLayoutName);
        m_xLbShowUsing->append_text(rDataField.maLayoutName);  // for AutoShow
    }

    sal_Int32 nSortPos = SC_SORTNAME_POS;
    if( nSortMode == DataPilotFieldSortMode::DATA )
    {
        nSortPos = FindListBoxEntry( *m_xLbSortBy, maLabelData.maSortInfo.Field, SC_SORTDATA_POS );
        if( nSortPos == -1 )
        {
            nSortPos = SC_SORTNAME_POS;
            nSortMode = DataPilotFieldSortMode::MANUAL;
        }
    }
    m_xLbSortBy->set_active(nSortPos);

    weld::RadioButton* pRBtn = nullptr;
    switch( nSortMode )
    {
        case DataPilotFieldSortMode::NONE:
        case DataPilotFieldSortMode::MANUAL:
            pRBtn = m_xRbSortMan.get();
        break;
        default:
            pRBtn = maLabelData.maSortInfo.IsAscending ? m_xRbSortAsc.get() : m_xRbSortDesc.get();
    }
    pRBtn->set_active(true);

    // *** LAYOUT MODE ***

    m_xLayoutFrame->set_sensitive(bEnableLayout);

    m_xLbLayout->set_active(FromDataPilotFieldLayoutMode(maLabelData.maLayoutInfo.LayoutMode));
    m_xCbLayoutEmpty->set_active( maLabelData.maLayoutInfo.AddEmptyLines );
    m_xCbRepeatItemLabels->set_active( maLabelData.mbRepeatItemLabels );

    // *** AUTO SHOW ***

    m_xCbShow->set_active( maLabelData.maShowInfo.IsEnabled );
    m_xCbShow->connect_toggled( LINK( this, ScDPSubtotalOptDlg, CheckHdl ) );

    m_xLbShowFrom->set_active(FromDataPilotFieldShowItemsMode(maLabelData.maShowInfo.ShowItemsMode));
    tools::Long nCount = static_cast< tools::Long >( maLabelData.maShowInfo.ItemCount );
    if( nCount < 1 )
        nCount = SC_SHOW_DEFAULT;
    m_xNfShow->set_value( nCount );

    // m_xLbShowUsing already filled above
    m_xLbShowUsing->set_active_text(maLabelData.maShowInfo.DataField);
    if (m_xLbShowUsing->get_active() == -1)
        m_xLbShowUsing->set_active(0);

    CheckHdl(*m_xCbShow);      // enable/disable dependent controls

    // *** HIDDEN ITEMS ***

    InitHideListBox();

    // *** HIERARCHY ***

    if( maLabelData.maHiers.getLength() > 1 )
    {
        lclFillListBox(*m_xLbHierarchy, maLabelData.maHiers);
        sal_Int32 nHier = maLabelData.mnUsedHier;
        if( (nHier < 0) || (nHier >= maLabelData.maHiers.getLength()) ) nHier = 0;
        m_xLbHierarchy->set_active( nHier );
        m_xLbHierarchy->connect_changed( LINK( this, ScDPSubtotalOptDlg, SelectHdl ) );
    }
    else
    {
        m_xFtHierarchy->set_sensitive(false);
        m_xLbHierarchy->set_sensitive(false);
    }
}

void ScDPSubtotalOptDlg::InitHideListBox()
{
    m_xLbHide->clear();
    lclFillListBox(*m_xLbHide, maLabelData.maMembers);
    size_t n = maLabelData.maMembers.size();
    for (size_t i = 0; i < n; ++i)
        m_xLbHide->set_toggle(i, maLabelData.maMembers[i].mbVisible ? TRISTATE_FALSE : TRISTATE_TRUE);
    bool bEnable = m_xLbHide->n_children() > 0;
    m_xHideFrame->set_sensitive(bEnable);
}

ScDPName ScDPSubtotalOptDlg::GetFieldName(const OUString& rLayoutName) const
{
    NameMapType::const_iterator itr = maDataFieldNameMap.find(rLayoutName);
    return itr == maDataFieldNameMap.end() ? ScDPName() : itr->second;
}

sal_Int32 ScDPSubtotalOptDlg::FindListBoxEntry(
    const weld::ComboBox& rLBox, std::u16string_view rEntry, sal_Int32 nStartPos ) const
{
    sal_Int32 nPos = nStartPos;
    bool bFound = false;
    while (nPos < rLBox.get_count())
    {
        // translate the displayed field name back to its original field name.
        ScDPName aName = GetFieldName(rLBox.get_text(nPos));
        OUString aUnoName = ScDPUtil::createDuplicateDimensionName(aName.maName, aName.mnDupCount);
        if (aUnoName == rEntry)
        {
            bFound = true;
            break;
        }
        ++nPos;
    }
    return bFound ? nPos : -1;
}

IMPL_LINK(ScDPSubtotalOptDlg, ButtonClicked, weld::Button&, rButton, void)
{
    if (&rButton == m_xBtnOk.get())
        response(RET_OK);
    else
        response(RET_CANCEL);
}

IMPL_LINK(ScDPSubtotalOptDlg, CheckHdl, weld::Toggleable&, rCBox, void)
{
    if (&rCBox == m_xCbShow.get())
    {
        bool bEnable = m_xCbShow->get_active();
        m_xNfShow->set_sensitive( bEnable );
        m_xFtShow->set_sensitive( bEnable );
        m_xFtShowFrom->set_sensitive( bEnable );
        m_xLbShowFrom->set_sensitive( bEnable );

        bool bEnableUsing = bEnable && (m_xLbShowUsing->get_count() > 0);
        m_xFtShowUsing->set_sensitive(bEnableUsing);
        m_xLbShowUsing->set_sensitive(bEnableUsing);
    }
}

IMPL_LINK_NOARG(ScDPSubtotalOptDlg, SelectHdl, weld::ComboBox&, void)
{
    mrDPObj.GetMembers(maLabelData.mnCol, m_xLbHierarchy->get_active(), maLabelData.maMembers);
    InitHideListBox();
}

ScDPShowDetailDlg::ScDPShowDetailDlg(weld::Window* pParent, ScDPObject& rDPObj, css::sheet::DataPilotFieldOrientation nOrient)
    : GenericDialogController(pParent, u"modules/scalc/ui/showdetaildialog.ui"_ustr, u"ShowDetail"_ustr)
    , mrDPObj(rDPObj)
    , mxLbDims(m_xBuilder->weld_tree_view(u"dimsTreeview"_ustr))
{
    ScDPSaveData* pSaveData = rDPObj.GetSaveData();
    tools::Long nDimCount = rDPObj.GetDimCount();
    for (tools::Long nDim=0; nDim<nDimCount; nDim++)
    {
        bool bIsDataLayout;
        sal_Int32 nDimFlags = 0;
        OUString aName = rDPObj.GetDimName( nDim, bIsDataLayout, &nDimFlags );
        if ( !bIsDataLayout && !rDPObj.IsDuplicated( nDim ) && ScDPObject::IsOrientationAllowed( nOrient, nDimFlags ) )
        {
            const ScDPSaveDimension* pDimension = pSaveData ? pSaveData->GetExistingDimensionByName(aName) : nullptr;
            if ( !pDimension || (pDimension->GetOrientation() != nOrient) )
            {
                if (pDimension)
                {
                    const std::optional<OUString> & pLayoutName = pDimension->GetLayoutName();
                    if (pLayoutName)
                        aName = *pLayoutName;
                }
                mxLbDims->append_text(aName);
                maNameIndexMap.emplace(aName, nDim);
            }
        }
    }
    if (mxLbDims->n_children())
       mxLbDims->select(0);

    mxLbDims->connect_row_activated(LINK(this, ScDPShowDetailDlg, DblClickHdl));
}

ScDPShowDetailDlg::~ScDPShowDetailDlg()
{
}

short ScDPShowDetailDlg::run()
{
    return mxLbDims->n_children() ? GenericDialogController::run() : static_cast<short>(RET_CANCEL);
}

OUString ScDPShowDetailDlg::GetDimensionName() const
{
    // Look up the internal dimension name which may be different from the
    // displayed field name.
    OUString aSelectedName = mxLbDims->get_selected_text();
    DimNameIndexMap::const_iterator itr = maNameIndexMap.find(aSelectedName);
    if (itr == maNameIndexMap.end())
        // This should never happen!
        return aSelectedName;

    tools::Long nDim = itr->second;
    bool bIsDataLayout = false;
    return mrDPObj.GetDimName(nDim, bIsDataLayout);
}

IMPL_LINK_NOARG(ScDPShowDetailDlg, DblClickHdl, weld::TreeView&, bool)
{
    m_xDialog->response(RET_OK);
    return true;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
