/* -*- 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/.
 */

#include <o3tl/safeint.hxx>
#include <searchresults.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/viewfrm.hxx>
#include <svx/srchdlg.hxx>
#include <dociter.hxx>
#include <document.hxx>
#include <tabvwsh.hxx>
#include <strings.hrc>
#include <sc.hrc>
#include <scresid.hxx>

namespace sc {

SearchResultsDlg::SearchResultsDlg(SfxBindings* _pBindings, weld::Window* pParent)
    : SfxDialogController(pParent, u"modules/scalc/ui/searchresults.ui"_ustr, u"SearchResultsDialog"_ustr)
    , aSkipped(ScResId(SCSTR_SKIPPED))
    , mpBindings(_pBindings)
    , mpDoc(nullptr)
    , mbSorted(false)
    , mxList(m_xBuilder->weld_tree_view(u"results"_ustr))
    , mxSearchResults(m_xBuilder->weld_label(u"lbSearchResults"_ustr))
    , mxShowDialog(m_xBuilder->weld_check_button(u"cbShow"_ustr))
{
    mxList->set_size_request(mxList->get_approximate_digit_width() * 50, mxList->get_height_rows(15));
    mxShowDialog->connect_toggled(LINK(this, SearchResultsDlg, OnShowToggled));
    std::vector<int> aWidths
    {
        o3tl::narrowing<int>(mxList->get_approximate_digit_width() * 10),
        o3tl::narrowing<int>(mxList->get_approximate_digit_width() * 10)
    };
    mxList->set_column_fixed_widths(aWidths);
    mxList->connect_selection_changed(LINK(this, SearchResultsDlg, ListSelectHdl));
    mxList->connect_column_clicked(LINK(this, SearchResultsDlg, HeaderBarClick));
}

SearchResultsDlg::~SearchResultsDlg()
{
    // tdf#133807 if the search dialog is shown then re-present that dialog
    // when this results dialog is dismissed
    SfxViewFrame* pViewFrame = mpBindings->GetDispatcher()->GetFrame();
    if (!pViewFrame)
        return;
    SfxChildWindow* pChildWindow = pViewFrame->GetChildWindow(
            SvxSearchDialogWrapper::GetChildWindowId());
    if (!pChildWindow)
        return;
    SvxSearchDialog* pSearchDlg = static_cast<SvxSearchDialog*>(pChildWindow->GetController().get());
    if (!pSearchDlg)
        return;
    pSearchDlg->Present();
}

namespace
{
    class ListWrapper {
        weld::TreeView& mrList;
        const ScDocument& mrDoc;
    public:
        size_t mnCount = 0;
        static const size_t mnMaximum = 1000;
        ListWrapper(weld::TreeView& rList, const ScDocument& rDoc)
            : mrList(rList)
            , mrDoc(rDoc)
        {
            mrList.clear();
            mrList.freeze();
        }
        ~ListWrapper()
        {
            mrList.thaw();
        }
        void Insert(const ScAddress &rPos, const OUString &rText)
        {
            if (mnCount++ < mnMaximum)
            {
                OUString aTabName;
                mrDoc.GetName(rPos.Tab(), aTabName);
                mrList.append_text(aTabName);
                int nPos = mrList.n_children() - 1;
                mrList.set_text(nPos, rPos.Format(ScRefFlags::ADDR_ABS,
                                      nullptr, mrDoc.GetAddressConvention()), 1);
                mrList.set_text(nPos, rText, 2);
            }
        }
    };
}

void SearchResultsDlg::FillResults( ScDocument& rDoc, const ScRangeList &rMatchedRanges, bool bCellNotes,
        bool bEmptyCells, bool bMatchedRangesWereClamped )
{
    ListWrapper aList(*mxList, rDoc);

    for (const auto& rRange : rMatchedRanges)
    {
        if (bCellNotes || bEmptyCells)
        {
            // Bear in mind that mostly the range is one address position
            // or a column or a row joined.
            ScAddress aPos( rRange.aStart );
            for ( ; aPos.Tab() <= rRange.aEnd.Tab(); aPos.IncTab())
            {
                for (aPos.SetCol( rRange.aStart.Col()); aPos.Col() <= rRange.aEnd.Col(); aPos.IncCol())
                {
                    for (aPos.SetRow( rRange.aStart.Row()); aPos.Row() <= rRange.aEnd.Row(); aPos.IncRow())
                    {
                        if (bCellNotes)
                        {
                            const ScPostIt* pNote = rDoc.GetNote( aPos);
                            if (pNote)
                                aList.Insert(aPos, pNote->GetText());
                        }
                        else  // bEmptyCells
                        {
                            aList.Insert(aPos, rDoc.GetString(aPos));
                        }
                    }
                }
            }
        }
        else
        {
            ScCellIterator aIter(rDoc, rRange);
            for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
            {
                const ScAddress& aPos = aIter.GetPos();
                aList.Insert(aPos, rDoc.GetString(aPos));
            }
        }
    }

    OUString aSearchResultsMsg;
    if (bMatchedRangesWereClamped)
    {
        aSearchResultsMsg = ScResId(SCSTR_RESULTS_CLAMPED);
        aSearchResultsMsg = aSearchResultsMsg.replaceFirst("%1", OUString::number(1000));
    }
    else
    {
        OUString aTotal(ScResId(SCSTR_TOTAL, aList.mnCount));
        aSearchResultsMsg = aTotal.replaceFirst("%1", OUString::number(aList.mnCount));
        if (aList.mnCount > ListWrapper::mnMaximum)
            aSearchResultsMsg += " " + ScGlobal::ReplaceOrAppend( aSkipped, u"%1", OUString::number( ListWrapper::mnMaximum ) );
    }
    mxSearchResults->set_label(aSearchResultsMsg);

    mpDoc = &rDoc;
}

void SearchResultsDlg::Close()
{
    if (mpBindings)
    {
        // Remove this dialog from the view frame after the dialog gets
        // dismissed, else it would keep popping up endlessly!
        SfxDispatcher* pDispacher = mpBindings ->GetDispatcher();
        SfxBoolItem aItem(SID_SEARCH_RESULTS_DIALOG, false);
        if (pDispacher)
        {
            pDispacher->ExecuteList(SID_SEARCH_RESULTS_DIALOG,
                SfxCallMode::SYNCHRON | SfxCallMode::RECORD, { &aItem });
        }
    }

    SfxDialogController::Close();
}

IMPL_LINK(SearchResultsDlg, HeaderBarClick, int, nColumn, void)
{
    if (!mbSorted)
    {
        mxList->make_sorted();
        mbSorted = true;
    }

    bool bSortAtoZ = mxList->get_sort_order();

    //set new arrow positions in headerbar
    if (nColumn == mxList->get_sort_column())
    {
        bSortAtoZ = !bSortAtoZ;
        mxList->set_sort_order(bSortAtoZ);
    }
    else
    {
        int nOldSortColumn = mxList->get_sort_column();
        if (nOldSortColumn != -1)
            mxList->set_sort_indicator(TRISTATE_INDET, nOldSortColumn);
        mxList->set_sort_column(nColumn);
    }

    if (nColumn != -1)
    {
        //sort lists
        mxList->set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn);
    }
}

IMPL_LINK_NOARG( SearchResultsDlg, ListSelectHdl, weld::TreeView&, void )
{
    if (!mpDoc)
        return;

    int nEntry = mxList->get_selected_index();
    OUString aTabStr = mxList->get_text(nEntry, 0);
    OUString aPosStr = mxList->get_text(nEntry, 1);

    SCTAB nTab = -1;
    if (!mpDoc->GetTable(aTabStr, nTab))
        // No sheet with specified name.
        return;

    ScAddress aPos;
    ScRefFlags nRes = aPos.Parse(aPosStr, *mpDoc, mpDoc->GetAddressConvention());
    if (!(nRes & ScRefFlags::VALID))
        // Invalid address string.
        return;

    // Jump to the cell.
    if (ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell())
    {
        pScViewShell->SetTabNo(nTab);
        pScViewShell->SetCursor(aPos.Col(), aPos.Row());
        pScViewShell->AlignToCursor(aPos.Col(), aPos.Row(), SC_FOLLOW_JUMP);
    }
}

IMPL_STATIC_LINK( SearchResultsDlg, OnShowToggled, weld::Toggleable&, rButton, void )
{
    if (ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell())
    {
        ScViewOptions aViewOpt( pScViewShell->GetViewData().GetOptions() );
        aViewOpt.SetOption(sc::ViewOption::SUMMARY, rButton.get_active());
        pScViewShell->GetViewData().SetOptions( aViewOpt );
    }
}

SearchResultsDlgWrapper::SearchResultsDlgWrapper(
    vcl::Window* _pParent, sal_uInt16 nId, SfxBindings* pBindings, SfxChildWinInfo* /*pInfo*/)
    : SfxChildWindow(_pParent, nId)
    , m_xDialog(std::make_shared<SearchResultsDlg>(pBindings, _pParent->GetFrameWeld()))
{
    SetController(m_xDialog);
}

SearchResultsDlgWrapper::~SearchResultsDlgWrapper() {}

SfxChildWinInfo SearchResultsDlgWrapper::GetInfo() const
{
    SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
    aInfo.bVisible = false;
    return aInfo;
}

SFX_IMPL_CHILDWINDOW_WITHID(SearchResultsDlgWrapper, SID_SEARCH_RESULTS_DIALOG);

}

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