/* -*- 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 <dataproviderdlg.hxx>
#include <scresid.hxx>
#include <globstr.hrc>

#include <document.hxx>
#include <dataprovider.hxx>
#include <datatransformation.hxx>
#include <datamapper.hxx>
#include <dbdata.hxx>

#include <comphelper/string.hxx>
#include <sal/log.hxx>
#include <sfx2/filedlghelper.hxx>
#include <unotools/charclass.hxx>
#include <utility>
#include <vcl/svapp.hxx>

class ScDataTransformationBaseControl
{
protected:
    std::unique_ptr<weld::Builder> mxBuilder;
    std::unique_ptr<weld::Container> mxGrid;
    weld::Container* mpContainer;

    sal_uInt32 mnIndex;

public:
    ScDataTransformationBaseControl(weld::Container* pParent, const OUString& rUIFile, sal_uInt32 nIndex);
    virtual ~ScDataTransformationBaseControl();

    void updateIndex(sal_uInt32 nIndex) { mnIndex = nIndex; }

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() = 0;
};

ScDataTransformationBaseControl::ScDataTransformationBaseControl(weld::Container* pParent, const OUString& rUIFile, sal_uInt32 nIndex)
    : mxBuilder(Application::CreateBuilder(pParent, rUIFile))
    , mxGrid(mxBuilder->weld_container(u"grid"_ustr))
    , mpContainer(pParent)
    , mnIndex(nIndex)
{
}

ScDataTransformationBaseControl::~ScDataTransformationBaseControl()
{
    mpContainer->move(mxGrid.get(), nullptr);
}

namespace {

class ScDeleteColumnTransformationControl : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::Entry> mxColumnNums;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument* mpDoc;

public:
    ScDeleteColumnTransformationControl(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 aIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScDeleteColumnTransformationControl::ScDeleteColumnTransformationControl(
    const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent, u"modules/scalc/ui/deletecolumnentry.ui"_ustr, nIndex)
    , mxColumnNums(mxBuilder->weld_entry(u"ed_columns"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    mxDelete->connect_clicked(LINK(this,ScDeleteColumnTransformationControl, DeleteHdl));
}

std::shared_ptr<sc::DataTransformation> ScDeleteColumnTransformationControl::getTransformation()
{
    OUString aColumnString = mxColumnNums->get_text();
    std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
    std::set<SCCOL> ColNums;
    for (const auto& rColStr : aSplitColumns)
    {
        sal_Int32 nCol = rColStr.toInt32();
        if (nCol <= 0)
            continue;

        if (nCol > mpDoc->MaxCol())
            continue;

        // translate from 1-based column notations to internal Calc one
        ColNums.insert(nCol - 1);
    }

    return std::make_shared<sc::ColumnRemoveTransformation>(std::move(ColNums));
}

class ScSplitColumnTransformationControl : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::Entry> mxSeparator;
    std::unique_ptr<weld::Entry> mxNumColumns;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument* mpDoc;

public:
    ScSplitColumnTransformationControl(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScSplitColumnTransformationControl::ScSplitColumnTransformationControl(
    const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex,
    std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent, u"modules/scalc/ui/splitcolumnentry.ui"_ustr, nIndex)
    , mxSeparator(mxBuilder->weld_entry(u"ed_separator"_ustr))
    , mxNumColumns(mxBuilder->weld_entry(u"num_cols"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    // for uitests
    mxSeparator->set_buildable_name(mxSeparator->get_buildable_name() + OUString::number(nIndex));
    mxNumColumns->set_buildable_name(mxNumColumns->get_buildable_name() + OUString::number(nIndex));
    mxDelete->set_buildable_name(mxDelete->get_buildable_name() + OUString::number(nIndex));

    mxDelete->connect_clicked(LINK(this,ScSplitColumnTransformationControl, DeleteHdl));
}

std::shared_ptr<sc::DataTransformation> ScSplitColumnTransformationControl::getTransformation()
{
    OUString aSeparator = mxSeparator->get_text();
    sal_Unicode cSeparator = aSeparator.isEmpty() ? ',' : aSeparator[0];
    OUString aColStr = mxNumColumns->get_text();
    SCCOL mnCol = -1;
    sal_Int32 nCol = aColStr.toInt32();
    if (nCol > 0 && nCol <= mpDoc->MaxCol())
        mnCol = nCol - 1;
    return std::make_shared<sc::SplitColumnTransformation>(mnCol, cSeparator);
}

class ScMergeColumnTransformationControl : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::Entry> mxSeparator;
    std::unique_ptr<weld::Entry> mxEdColumns;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument* mpDoc;

public:
    ScMergeColumnTransformationControl(const ScDocument *pDoc, weld::Container* pParent, SCCOL nStartCol, SCCOL nEndCol, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScMergeColumnTransformationControl::ScMergeColumnTransformationControl(
    const ScDocument* pDoc, weld::Container* pParent, SCCOL nStartCol, SCCOL nEndCol, sal_uInt32 nIndex,
    std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent, u"modules/scalc/ui/mergecolumnentry.ui"_ustr, nIndex)
    , mxSeparator(mxBuilder->weld_entry(u"ed_separator"_ustr))
    , mxEdColumns(mxBuilder->weld_entry(u"ed_columns"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    // for uitests
    mxSeparator->set_buildable_name(mxSeparator->get_buildable_name() + OUString::number(nIndex));
    mxEdColumns->set_buildable_name(mxEdColumns->get_buildable_name() + OUString::number(nIndex));
    mxDelete->set_buildable_name(mxDelete->get_buildable_name() + OUString::number(nIndex));

    mxDelete->connect_clicked(LINK(this,ScMergeColumnTransformationControl, DeleteHdl));

    OUStringBuffer aBuffer;

    // map from zero based to one based column numbers
    aBuffer.append(static_cast<sal_Int32>(nStartCol + 1));
    for ( SCCOL nCol = nStartCol + 1; nCol <= nEndCol; ++nCol)
    {
        aBuffer.append(";" + OUString::number(nCol + 1));
    }

    mxEdColumns->set_text(aBuffer.makeStringAndClear());
}

std::shared_ptr<sc::DataTransformation> ScMergeColumnTransformationControl::getTransformation()
{
    OUString aColumnString = mxEdColumns->get_text();
    std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
    std::set<SCCOL> aMergedColumns;
    for (const auto& rColStr : aSplitColumns)
    {
        sal_Int32 nCol = rColStr.toInt32();
        if (nCol <= 0)
            continue;

        if (nCol > mpDoc->MaxCol())
            continue;

        // translate from 1-based column notations to internal Calc one
        aMergedColumns.insert(nCol - 1);
    }
    return std::make_shared<sc::MergeColumnTransformation>(std::move(aMergedColumns), mxSeparator->get_text());
}

class ScSortTransformationControl : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::ComboBox> mxType;
    std::unique_ptr<weld::Entry> mxEdColumns;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument* mpDoc;

public:
    ScSortTransformationControl(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScSortTransformationControl::ScSortTransformationControl(
    const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent, u"modules/scalc/ui/sorttransformationentry.ui"_ustr, nIndex)
    , mxType(mxBuilder->weld_combo_box(u"ed_ascending"_ustr))
    , mxEdColumns(mxBuilder->weld_entry(u"ed_columns"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    mxDelete->connect_clicked(LINK(this,ScSortTransformationControl, DeleteHdl));
}

std::shared_ptr<sc::DataTransformation> ScSortTransformationControl::getTransformation()
{
    OUString aColStr = mxEdColumns->get_text();
    bool aIsAscending = mxType->get_active();
    SCCOL aColumn = 0;
    sal_Int32 nCol = aColStr.toInt32();
    if (nCol > 0 && nCol <= mpDoc->MaxCol())
        aColumn = nCol - 1;     // translate from 1-based column notations to internal Calc one

    ScSortParam aSortParam;
    SCCOL nEndCol = mpDoc->MaxCol();
    SCROW nEndRow = mpDoc->MaxRow();
    SCCOL nStartCol = 0;
    SCROW nStartRow = 0;
    SCTAB nTab = 0; // for internal doc in case of Apply and clipboard in case of OK
    if (!mpDoc->ShrinkToDataArea(nTab, nStartCol, nStartRow, nEndCol, nEndRow))
    {
        // tdf#169515 The preview document does not show the sorted data. Reason: ShrinkToDataArea
        // fails. ToDo: Why?
        // Workaround: We use the size of the "internalhelper" database range instead. In case the
        // user has defined a huge range, LibreOffice seems to hang. Thus the size is restricted here
        // although the preview does not show the final result in that case.
        ScDBCollection::NamedDBs& rLocalDBs = mpDoc->GetDBCollection()->getNamedDBs();
        ScDBData* pDB = rLocalDBs.findByName(u"internalhelper"_ustr);
        if (pDB)
        {
            pDB->GetArea(nTab, nStartCol, nStartRow, nEndCol, nEndRow);
            nEndCol = std::min<SCCOL>(nEndCol, nStartCol + 30);
            nEndRow = std::min<SCROW>(nEndRow, nStartRow + 10000);
        }
        else
        { // should not happen
            nEndCol = 30;
            nEndRow = 10000;
        }
    }
    aSortParam.nCol1 = nStartCol;
    aSortParam.nRow1 = nStartRow;
    aSortParam.nCol2 = nEndCol;
    aSortParam.nRow2 = nEndRow;
    {
        ScDBCollection::NamedDBs& rLocalDBs = mpDoc->GetDBCollection()->getNamedDBs();
        ScDBData* pDB = rLocalDBs.findByName(u"internalhelper"_ustr);
        if (pDB)
            aSortParam.bHasHeader = pDB->HasHeader();
        else
            aSortParam.bHasHeader = true;
    }
    aSortParam.maKeyState[0].bDoSort = true;
    aSortParam.maKeyState[0].nField = aColumn;
    aSortParam.maKeyState[0].bAscending = aIsAscending;
    return std::make_shared<sc::SortTransformation>(aSortParam);
}

class ScColumnTextTransformation : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::Entry> mxColumnNums;
    std::unique_ptr<weld::ComboBox> mxType;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument* mpDoc;

public:
    ScColumnTextTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScColumnTextTransformation::ScColumnTextTransformation(
    const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent, u"modules/scalc/ui/texttransformationentry.ui"_ustr, nIndex)
    , mxColumnNums(mxBuilder->weld_entry(u"ed_columns"_ustr))
    , mxType(mxBuilder->weld_combo_box(u"ed_lst"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    mxDelete->connect_clicked(LINK(this,ScColumnTextTransformation, DeleteHdl));
}

std::shared_ptr<sc::DataTransformation> ScColumnTextTransformation::getTransformation()
{
    OUString aColumnString = mxColumnNums->get_text();
    std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
    std::set<SCCOL> aColumns;
    for (const auto& rColStr : aSplitColumns)
    {
        sal_Int32 nCol = rColStr.toInt32();
        if (nCol <= 0)
            continue;

        if (nCol > mpDoc->MaxCol())
            continue;

        // translate from 1-based column notations to internal Calc one
        aColumns.insert(nCol - 1);
    }

    sal_Int32 nPos = mxType->get_active();
    switch (nPos)
    {
        case 0:
            return std::make_shared<sc::TextTransformation>(std::move(aColumns),sc::TEXT_TRANSFORM_TYPE::TO_LOWER);
        case 1:
            return std::make_shared<sc::TextTransformation>(std::move(aColumns),sc::TEXT_TRANSFORM_TYPE::TO_UPPER);
        case 2:
            return std::make_shared<sc::TextTransformation>(std::move(aColumns),sc::TEXT_TRANSFORM_TYPE::CAPITALIZE);
        case 3:
            return std::make_shared<sc::TextTransformation>(std::move(aColumns),sc::TEXT_TRANSFORM_TYPE::TRIM);
        default:
            assert(false);
    }

    return nullptr;
}

class ScAggregateFunction : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::Entry> mxColumnNums;
    std::unique_ptr<weld::ComboBox> mxType;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument* mpDoc;

public:
    ScAggregateFunction(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScAggregateFunction::ScAggregateFunction(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex,
                                         std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent, u"modules/scalc/ui/aggregatefunctionentry.ui"_ustr, nIndex)
    , mxColumnNums(mxBuilder->weld_entry(u"ed_columns"_ustr))
    , mxType(mxBuilder->weld_combo_box(u"ed_lst"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    mxDelete->connect_clicked(LINK(this,ScAggregateFunction, DeleteHdl));
}

std::shared_ptr<sc::DataTransformation> ScAggregateFunction::getTransformation()
{
    OUString aColumnString = mxColumnNums->get_text();
    sal_Int32 nPos = mxType->get_active();
    std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
    std::set<SCCOL> aColumns;
    for (const auto& rColStr : aSplitColumns)
    {
        sal_Int32 nCol = rColStr.toInt32();
        if (nCol <= 0)
            continue;

        if (nCol > mpDoc->MaxCol())
            continue;

        // translate from 1-based column notations to internal Calc one
        aColumns.insert(nCol - 1);
    }
    switch (nPos)
    {
        case 0:
            return std::make_shared<sc::AggregateFunction>(std::move(aColumns),sc::AGGREGATE_FUNCTION::SUM);
        case 1:
            return std::make_shared<sc::AggregateFunction>(std::move(aColumns),sc::AGGREGATE_FUNCTION::AVERAGE);
        case 2:
            return std::make_shared<sc::AggregateFunction>(std::move(aColumns),sc::AGGREGATE_FUNCTION::MIN);
        case 3:
            return std::make_shared<sc::AggregateFunction>(std::move(aColumns),sc::AGGREGATE_FUNCTION::MAX);
        default:
            assert(false);
    }

    return nullptr;
}

class ScNumberTransformation : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::Entry> mxColumnNums;
    std::unique_ptr<weld::ComboBox> mxType;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument* mpDoc;

public:
    ScNumberTransformation(const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScNumberTransformation::ScNumberTransformation(
    const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent, u"modules/scalc/ui/numbertransformationentry.ui"_ustr, nIndex)
    , mxColumnNums(mxBuilder->weld_entry(u"ed_columns"_ustr))
    , mxType(mxBuilder->weld_combo_box(u"ed_lst"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    mxDelete->connect_clicked(LINK(this,ScNumberTransformation, DeleteHdl));
}

std::shared_ptr<sc::DataTransformation> ScNumberTransformation::getTransformation()
{
    OUString aColumnString = mxColumnNums->get_text();
    sal_Int32 nPos = mxType->get_active();
    std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
    std::set<SCCOL> aColumns;
    for (const auto& rColStr : aSplitColumns)
    {
        sal_Int32 nCol = rColStr.toInt32();
        if (nCol <= 0)
            continue;

        if (nCol > mpDoc->MaxCol())
            continue;

        // translate from 1-based column notations to internal Calc one
        aColumns.insert(nCol - 1);
    }
    switch (nPos)
    {
        case 0:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::SIGN);
        case 1:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::ROUND);
        case 2:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::ROUND_UP);
        case 3:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::ROUND_DOWN);
        case 4:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::ABSOLUTE);
        case 5:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::LOG_E);
        case 6:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::LOG_10);
        case 7:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::CUBE);
        case 8:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::SQUARE);
        case 9:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::SQUARE_ROOT);
        case 10:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::EXPONENT);
        case 11:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::IS_EVEN);
        case 12:
            return std::make_shared<sc::NumberTransformation>(std::move(aColumns),sc::NUMBER_TRANSFORM_TYPE::IS_ODD);
        default:
            assert(false);
    }

    return nullptr;
}

class ScReplaceNullTransformation : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::Entry> mxColumnNums;
    std::unique_ptr<weld::Entry> mxReplaceString;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument *mpDoc;

public:

    ScReplaceNullTransformation(const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScReplaceNullTransformation::ScReplaceNullTransformation(const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent,u"modules/scalc/ui/replacenulltransformationentry.ui"_ustr, nIndex)
    , mxColumnNums(mxBuilder->weld_entry(u"ed_columns"_ustr))
    , mxReplaceString(mxBuilder->weld_entry(u"ed_str"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    mxDelete->connect_clicked(LINK(this,ScReplaceNullTransformation, DeleteHdl));
}


std::shared_ptr<sc::DataTransformation> ScReplaceNullTransformation::getTransformation()
{
    OUString aColumnString = mxColumnNums->get_text();
    OUString aReplaceWithString = mxReplaceString->get_text();
    std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
    std::set<SCCOL> aColumns;
    for (const auto& rColStr : aSplitColumns)
    {
        sal_Int32 nCol = rColStr.toInt32();
        if (nCol <= 0)
            continue;

        if (nCol > mpDoc->MaxCol())
            continue;

        // translate from 1-based column notations to internal Calc one
        aColumns.insert(nCol - 1);
    }

    return std::make_shared<sc::ReplaceNullTransformation>(std::move(aColumns),aReplaceWithString);
}

class ScDateTimeTransformation : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::Entry> mxColumnNums;
    std::unique_ptr<weld::ComboBox> mxType;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument* mpDoc;

public:

    ScDateTimeTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScDateTimeTransformation::ScDateTimeTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent,u"modules/scalc/ui/datetimetransformationentry.ui"_ustr, nIndex)
    , mxColumnNums(mxBuilder->weld_entry(u"ed_columns"_ustr))
    , mxType(mxBuilder->weld_combo_box(u"ed_lst"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    mxDelete->connect_clicked(LINK(this,ScDateTimeTransformation, DeleteHdl));
}

std::shared_ptr<sc::DataTransformation> ScDateTimeTransformation::getTransformation()
{
    OUString aColumnString = mxColumnNums->get_text();
    sal_Int32 nPos = mxType->get_active();
    std::vector<OUString> aSplitColumns = comphelper::string::split(aColumnString, ';');
    std::set<SCCOL> aColumns;
    for (const auto& rColStr : aSplitColumns)
    {
        sal_Int32 nCol = rColStr.toInt32();
        if (nCol <= 0)
            continue;

        if (nCol > mpDoc->MaxCol())
            continue;

        // translate from 1-based column notations to internal Calc one
        aColumns.insert(nCol - 1);
    }
    switch (nPos)
    {
        case 0:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::DATE_STRING);
        case 1:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::YEAR);
        case 2:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::START_OF_YEAR);
        case 3:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::END_OF_YEAR);
        case 4:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::MONTH);
        case 5:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::MONTH_NAME);
        case 6:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::START_OF_MONTH);
        case 7:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::END_OF_MONTH);
        case 8:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::DAY);
        case 9:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_WEEK);
        case 10:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_YEAR);
        case 11:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::QUARTER);
        case 12:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::START_OF_QUARTER);
        case 13:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::END_OF_QUARTER);
        case 14:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::HOUR);
        case 15:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::MINUTE);
        case 16:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::SECOND);
        case 17:
            return std::make_shared<sc::DateTimeTransformation>(std::move(aColumns),sc::DATETIME_TRANSFORMATION_TYPE::TIME);
        default:
            assert(false);
    }

    return nullptr;
}

class ScFindReplaceTransformation : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::Entry> mxFindString;
    std::unique_ptr<weld::Entry> mxReplaceString;
    std::unique_ptr<weld::Entry> mxEdColumns;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument* mpDoc;

public:
    ScFindReplaceTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScFindReplaceTransformation::ScFindReplaceTransformation(
    const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex,
    std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent, u"modules/scalc/ui/findreplaceentry.ui"_ustr, nIndex)
    , mxFindString(mxBuilder->weld_entry(u"ed_find"_ustr))
    , mxReplaceString(mxBuilder->weld_entry(u"ed_replace"_ustr))
    , mxEdColumns(mxBuilder->weld_entry(u"ed_columns"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    mxDelete->connect_clicked(LINK(this, ScFindReplaceTransformation, DeleteHdl));
}

std::shared_ptr<sc::DataTransformation> ScFindReplaceTransformation::getTransformation()
{
    OUString aColStr = mxEdColumns->get_text();
    SCCOL aColumn = -1;
    sal_Int32 nCol = aColStr.toInt32();
    if (nCol > 0 && nCol <= mpDoc->MaxCol())
        aColumn = nCol - 1;
    return std::make_shared<sc::FindReplaceTransformation>(aColumn, mxFindString->get_text(), mxReplaceString->get_text());
}

class ScDeleteRowTransformation : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::Entry> mxFindString;
    std::unique_ptr<weld::Entry> mxEdColumns;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument* mpDoc;

public:
    ScDeleteRowTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScDeleteRowTransformation::ScDeleteRowTransformation(
    const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex,
    std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent, u"modules/scalc/ui/deleterowentry.ui"_ustr, nIndex)
    , mxFindString(mxBuilder->weld_entry(u"ed_find"_ustr))
    , mxEdColumns(mxBuilder->weld_entry(u"ed_columns"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    mxDelete->connect_clicked(LINK(this, ScDeleteRowTransformation, DeleteHdl));
}

std::shared_ptr<sc::DataTransformation> ScDeleteRowTransformation::getTransformation()
{
    OUString aColStr = mxEdColumns->get_text();
    SCCOL aColumn = -1;
    sal_Int32 nCol = aColStr.toInt32();
    if (nCol > 0 && nCol <= mpDoc->MaxCol())
        aColumn = nCol - 1;
    return std::make_shared<sc::DeleteRowTransformation>(aColumn, mxFindString->get_text());
}

class ScSwapRowsTransformation : public ScDataTransformationBaseControl
{
private:
    std::unique_ptr<weld::Entry> mxRow;
    std::unique_ptr<weld::Entry> nxRow;
    std::unique_ptr<weld::Button> mxDelete;
    std::function<void(sal_uInt32&)> maDeleteTransformation;
    const ScDocument* mpDoc;

public:
    ScSwapRowsTransformation(const ScDocument* pDoc, weld::Container* pParent, sal_uInt32 nIndex, std::function<void(sal_uInt32&)> aDeleteTransformation);

    virtual std::shared_ptr<sc::DataTransformation> getTransformation() override;
    DECL_LINK(DeleteHdl, weld::Button&, void);
};

ScSwapRowsTransformation::ScSwapRowsTransformation(
    const ScDocument *pDoc, weld::Container* pParent, sal_uInt32 nIndex,
    std::function<void(sal_uInt32&)> aDeleteTransformation)
    : ScDataTransformationBaseControl(pParent, u"modules/scalc/ui/swaprowsentry.ui"_ustr, nIndex)
    , mxRow(mxBuilder->weld_entry(u"ed_row1"_ustr))
    , nxRow(mxBuilder->weld_entry(u"ed_row2"_ustr))
    , mxDelete(mxBuilder->weld_button(u"ed_delete"_ustr))
    , maDeleteTransformation(std::move(aDeleteTransformation))
    , mpDoc(pDoc)
{
    mxDelete->connect_clicked(LINK(this, ScSwapRowsTransformation, DeleteHdl));
}

std::shared_ptr<sc::DataTransformation> ScSwapRowsTransformation::getTransformation()
{
    OUString aRowStr = mxRow->get_text();
    OUString bRowStr = nxRow->get_text();
    SCROW aRow = -1;
    SCROW bRow = -1;
    sal_Int32 mRow = aRowStr.toInt32();
    sal_Int32 nRow = bRowStr.toInt32();
    if (mRow > 0 && mRow <= mpDoc->MaxRow())
        aRow = mRow - 1;
    if (nRow > 0 && nRow <= mpDoc->MaxRow())
        bRow = nRow - 1;
    return std::make_shared<sc::SwapRowsTransformation>(aRow, bRow);
}

}

ScDataProviderDlg::ScDataProviderDlg(weld::Window* pParent, std::shared_ptr<ScDocument> pDoc,
                                     const ScDocument* pDocument)
    : GenericDialogController(pParent, u"modules/scalc/ui/dataproviderdlg.ui"_ustr, u"dataproviderdlg"_ustr)
    , mxDoc(std::move(pDoc))
    , mxBox(m_xBuilder->weld_container(u"data_table"_ustr))
    , m_xTableParent(mxBox->CreateChildFrame())
    , mxTable(VclPtr<ScDataTableView>::Create(m_xTableParent, mxDoc))
    , mxDBRanges(m_xBuilder->weld_combo_box(u"select_db_range"_ustr))
    , mxOKBtn(m_xBuilder->weld_button(u"ok"_ustr))
    , mxCancelBtn(m_xBuilder->weld_button(u"cancel"_ustr))
    , mxAddTransformationBtn(m_xBuilder->weld_button(u"add_transformation"_ustr))
    , mxScroll(m_xBuilder->weld_scrolled_window(u"scroll"_ustr))
    , mxTransformationList(m_xBuilder->weld_container(u"transformation_ctrl"_ustr))
    , mxTransformationBox(m_xBuilder->weld_combo_box(u"transformation_box"_ustr))
    , mxProviderList(m_xBuilder->weld_combo_box(u"provider_lst"_ustr))
    , mxEditURL(m_xBuilder->weld_entry(u"ed_url"_ustr))
    , mxEditID(m_xBuilder->weld_entry(u"ed_id"_ustr))
    , mxApplyBtn(m_xBuilder->weld_button(u"apply"_ustr))
    , mxBrowseBtn(m_xBuilder->weld_button(u"browse"_ustr))
    , maIdle("ScDataProviderDlg maIdle")
    , mnIndex(0)
{
    Size aPrefSize = mxTable->GetOptimalSize();
    mxBox->set_size_request(aPrefSize.Width(), aPrefSize.Height());
    mxTable->Show();

    mpDestDBCollection = pDocument->GetDBCollection();
    auto& rNamedDBs = mpDestDBCollection->getNamedDBs();
    for (auto& rNamedDB : rNamedDBs)
    {
        mxDBRanges->append_text(rNamedDB->GetName());
    }

    pDBData = new ScDBData(u"data"_ustr, 0, 0, 0, mxDoc->MaxCol(), mxDoc->MaxRow());
    bool bSuccess = mxDoc->GetDBCollection()->getNamedDBs().insert(std::unique_ptr<ScDBData>(pDBData));
    SAL_WARN_IF(!bSuccess, "sc", "temporary warning");

    mxOKBtn->connect_clicked(LINK(this, ScDataProviderDlg, ApplyQuitHdl));
    mxCancelBtn->connect_clicked(LINK(this, ScDataProviderDlg, CancelQuitHdl));
    mxAddTransformationBtn->connect_clicked(LINK(this, ScDataProviderDlg, TransformationListHdl));
    mxApplyBtn->connect_clicked(LINK(this, ScDataProviderDlg, ApplyBtnHdl));
    mxBrowseBtn->connect_clicked(LINK(this, ScDataProviderDlg, BrowseBtnHdl));
    mxTransformationBox->connect_changed(LINK(this, ScDataProviderDlg, TransformationSelectHdl));
    mxProviderList->connect_changed(LINK(this, ScDataProviderDlg, ProviderSelectHdl));
    mxEditID->connect_changed(LINK(this, ScDataProviderDlg, IDEditHdl));
    mxEditURL->connect_changed(LINK(this, ScDataProviderDlg, URLEditHdl));

    msApplyTooltip = mxApplyBtn->get_tooltip_text();
    msAddTransformationToolTip = mxAddTransformationBtn->get_tooltip_text();
    mxAddTransformationBtn->set_sensitive(false);
    mxAddTransformationBtn->set_tooltip_text(OUString());
    isValid();

    maIdle.SetPriority( TaskPriority::LOWEST );
    maIdle.SetInvokeHandler( LINK( this, ScDataProviderDlg, ScrollToEnd) );
}

ScDataProviderDlg::~ScDataProviderDlg()
{
    mxTable.disposeAndClear();
    m_xTableParent->dispose();
    m_xTableParent.clear();
}

IMPL_LINK_NOARG(ScDataProviderDlg, ScrollToEnd, Timer*, void)
{
    mxScroll->vadjustment_set_value(mxScroll->vadjustment_get_upper());
}

IMPL_LINK_NOARG(ScDataProviderDlg, ApplyQuitHdl, weld::Button&, void)
{
    m_xDialog->response(RET_OK);
}

IMPL_LINK_NOARG(ScDataProviderDlg, CancelQuitHdl, weld::Button&, void)
{
    m_xDialog->response(RET_CANCEL);
}

IMPL_LINK_NOARG(ScDataProviderDlg, TransformationListHdl, weld::Button&, void)
{
    static std::function<void(ScDataProviderDlg*)> aTransformationOp[12]{
        &ScDataProviderDlg::deleteColumn,
        &ScDataProviderDlg::deleteRowTransformation,
        &ScDataProviderDlg::swapRowsTransformation,
        &ScDataProviderDlg::splitColumn,
        &ScDataProviderDlg::mergeColumns,
        &ScDataProviderDlg::textTransformation,
        &ScDataProviderDlg::sortTransformation,
        &ScDataProviderDlg::aggregateFunction,
        &ScDataProviderDlg::numberTransformation,
        &ScDataProviderDlg::replaceNullTransformation,
        &ScDataProviderDlg::dateTimeTransformation,
        &ScDataProviderDlg::findReplaceTransformation
    };
    OUString rId = mxTransformationBox->get_active_id();
    int nPos = mxTransformationBox->find_id(rId);

    aTransformationOp[nPos](this);
    maIdle.Start();
    return;
}

IMPL_LINK_NOARG(ScDataProviderDlg, ProviderSelectHdl, weld::ComboBox&, void)
{
    isValid();
}

IMPL_LINK_NOARG(ScDataProviderDlg, IDEditHdl, weld::Entry&, void)
{
    isValid();
}

IMPL_LINK_NOARG(ScDataProviderDlg, URLEditHdl, weld::Entry&, void)
{
    isValid();
}

IMPL_LINK_NOARG(ScDataProviderDlg, ApplyBtnHdl, weld::Button&, void)
{
    updateApplyBtn(true);
    clearTablePreview();
    import(*mxDoc, true);
}

IMPL_LINK_NOARG(ScDataProviderDlg, BrowseBtnHdl, weld::Button&, void)
{
    sfx2::FileDialogHelper aFileDialog(0, FileDialogFlags::NONE, m_xDialog.get());
    aFileDialog.SetContext(sfx2::FileDialogHelper::CalcDataProvider);
    if (aFileDialog.Execute() != ERRCODE_NONE)
        return;

    mxEditURL->set_text(aFileDialog.GetPath());
    isValid();
}

IMPL_LINK_NOARG(ScDataProviderDlg, TransformationSelectHdl, weld::ComboBox&, void)
{
    mxAddTransformationBtn->set_sensitive(true);
    mxAddTransformationBtn->set_tooltip_text(msAddTransformationToolTip);
}

sc::ExternalDataSource ScDataProviderDlg::getDataSource(ScDocument* pDoc)
{
    sc::ExternalDataSource aSource(mxEditURL->get_text(), mxProviderList->get_active_id(), pDoc);

    aSource.setID(mxEditID->get_text());
    return aSource;
}

void ScDataProviderDlg::isValid()
{
    bool bValid = !mxProviderList->get_active_id().isEmpty();
    bValid &= !mxEditURL->get_text().isEmpty();
    updateApplyBtn(bValid);
}

void ScDataProviderDlg::updateApplyBtn(bool bValidConfig)
{
    if (!bValidConfig)
    {
        mxApplyBtn->set_sensitive(false);
        mxApplyBtn->set_tooltip_text(OUString());
        return;
    }

    mxApplyBtn->set_sensitive(true);
    mxApplyBtn->set_tooltip_text(msApplyTooltip);
}

void ScDataProviderDlg::deleteColumn()
{
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScDeleteColumnTransformationControl>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
}

void ScDataProviderDlg::splitColumn()
{
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScSplitColumnTransformationControl>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
}

void ScDataProviderDlg::mergeColumns()
{
    SCCOL nStartCol = -1;
    SCCOL nEndCol = -1;
    mxTable->getColRange(nStartCol, nEndCol);
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScMergeColumnTransformationControl>(mxDoc.get(), mxTransformationList.get(), nStartCol, nEndCol, mnIndex++, adeleteTransformation));
}

void ScDataProviderDlg::textTransformation()
{
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScColumnTextTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
}

void ScDataProviderDlg::sortTransformation()
{
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScSortTransformationControl>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
}

void ScDataProviderDlg::aggregateFunction()
{
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScAggregateFunction>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
}

void ScDataProviderDlg::numberTransformation()
{
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScNumberTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
}

void ScDataProviderDlg::replaceNullTransformation()
{
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScReplaceNullTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
}

void ScDataProviderDlg::dateTimeTransformation()
{
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScDateTimeTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
}

void ScDataProviderDlg::findReplaceTransformation()
{
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScFindReplaceTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
}

void ScDataProviderDlg::deleteRowTransformation()
{
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScDeleteRowTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
}

void ScDataProviderDlg::swapRowsTransformation()
{
    auto adeleteTransformation = [this](sal_uInt32 nIndex) {deletefromList(nIndex);};
    maControls.emplace_back(std::make_unique<ScSwapRowsTransformation>(mxDoc.get(), mxTransformationList.get(), mnIndex++, adeleteTransformation));
}

namespace {

bool lcl_hasDBName(const OUString& rName, ScDBCollection* pDBCollection)
{
    if (pDBCollection->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rName)))
        return true;

    return false;
}

}

// tdf#165502 Clear the contents of the temporary ScDocument in mxDoc and adds a sheet to
// store the data to be previewed. This is needed if the newly imported content is smaller
// than the previously shown content
void ScDataProviderDlg::clearTablePreview()
{
    mxDoc->Clear();
    // The name "test" below is just a dummy name for the tab that will hold the data
    mxDoc->InsertTab(0, u"test"_ustr);
}

void ScDataProviderDlg::import(ScDocument& rDoc, bool bInternal)
{
    OUString sDestDBUpperName = ScGlobal::getCharClass().uppercase(mxDBRanges->get_active_text());
    ScDBData* pDestDBRange = mpDestDBCollection->getNamedDBs().findByUpperName(sDestDBUpperName);
    if (!pDestDBRange)
    {
        if (bInternal)
        {
            std::unique_ptr<weld::MessageDialog> xMsgBox(
                Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Error,
                VclButtonsType::Close, ScResId(STR_DATAPROVIDER_NODATABASERANGE)));
            xMsgBox->run();
        }
        return;
    }

    if (bInternal)
    {
        // Use an additional database range in the temporary document to provide infos for transformations.
        // Currently used for ersatz area in sorting and for HasHeader flag.
        ScRange aDestRange;
        pDestDBRange->GetArea(aDestRange);
        SCCOL nHelperCol = aDestRange.aEnd.Col() - aDestRange.aStart.Col();
        SCROW nHelperRow = aDestRange.aEnd.Row() - aDestRange.aStart.Row();
        ScDBData* pDBHelper = new ScDBData(u"internalhelper"_ustr, 0, 0, 0, nHelperCol, nHelperRow);
        pDBHelper->SetHeader(pDestDBRange->HasHeader());
        ScDBCollection::NamedDBs& rLocalDBs = rDoc.GetDBCollection()->getNamedDBs();
        auto iter = rLocalDBs.findByUpperName2(u"INTERNALHELPER"_ustr);
        if (iter != rLocalDBs.end())
            rLocalDBs.erase(iter);
        bool bSuccess = rLocalDBs.insert(std::unique_ptr<ScDBData>(pDBHelper));
        SAL_WARN_IF(!bSuccess, "sc", "Could not generate DBRange.");
    }

    sc::ExternalDataSource aSource = getDataSource(&rDoc);

    for (size_t i = 0; i < maControls.size(); ++i)
    {
        ScDataTransformationBaseControl* pTransformationCtrl = maControls[i].get();
        aSource.AddDataTransformation(pTransformationCtrl->getTransformation());
    }
    if (bInternal)
        aSource.setDBData(pDBData->GetName());
    else
    {
        aSource.setDBData(mxDBRanges->get_active_text());
        if (!lcl_hasDBName(aSource.getDBName(), rDoc.GetDBCollection()))
            return;
        rDoc.GetExternalDataMapper().insertDataSource(aSource);
    }

    try
    {
        aSource.refresh(&rDoc, true);
    }
    catch(const orcus::parse_error&)
    {
        std::unique_ptr<weld::MessageDialog> xMsgBox(Application::CreateMessageDialog(m_xDialog.get(),
                                                  VclMessageType::Error, VclButtonsType::Close,
                                                  ScResId(STD_ERR_CSV_PARSE)));
        xMsgBox->run();
    }

    mxTable->Invalidate();
}

void ScDataProviderDlg::deletefromList(sal_uInt32 nIndex)
{
    auto itr = maControls.erase(maControls.begin() + nIndex);
    while (itr != maControls.end())
    {
        (*itr)->updateIndex(nIndex++);
        ++itr;
    }
    --mnIndex;
}

IMPL_LINK_NOARG(ScDeleteColumnTransformationControl, DeleteHdl, weld::Button&, void)
{
   maDeleteTransformation(mnIndex);
}

IMPL_LINK_NOARG(ScSplitColumnTransformationControl, DeleteHdl, weld::Button&, void)
{
   maDeleteTransformation(mnIndex);
}

IMPL_LINK_NOARG(ScMergeColumnTransformationControl, DeleteHdl, weld::Button&, void)
{
   maDeleteTransformation(mnIndex);
}

IMPL_LINK_NOARG(ScNumberTransformation, DeleteHdl, weld::Button&, void)
{
   maDeleteTransformation(mnIndex);
}

IMPL_LINK_NOARG(ScAggregateFunction, DeleteHdl, weld::Button&, void)
{
   maDeleteTransformation(mnIndex);
}

IMPL_LINK_NOARG(ScSortTransformationControl, DeleteHdl, weld::Button&, void)
{
   maDeleteTransformation(mnIndex);
}

IMPL_LINK_NOARG(ScColumnTextTransformation, DeleteHdl, weld::Button&, void)
{
   maDeleteTransformation(mnIndex);
}

IMPL_LINK_NOARG(ScReplaceNullTransformation, DeleteHdl, weld::Button&, void)
{
   maDeleteTransformation(mnIndex);
}

IMPL_LINK_NOARG(ScDateTimeTransformation, DeleteHdl, weld::Button&, void)
{
   maDeleteTransformation(mnIndex);
}

IMPL_LINK_NOARG(ScFindReplaceTransformation, DeleteHdl, weld::Button&, void)
{
   maDeleteTransformation(mnIndex);
}

IMPL_LINK_NOARG(ScDeleteRowTransformation, DeleteHdl, weld::Button&, void)
{
    maDeleteTransformation(mnIndex);
}

IMPL_LINK_NOARG(ScSwapRowsTransformation, DeleteHdl, weld::Button&, void)
{
   maDeleteTransformation(mnIndex);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
