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

#include <config_features.h>

#include <excimp8.hxx>

#include <scitems.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/sequence.hxx>
#include <officecfg/Office/Calc.hxx>

#include <sfx2/docfile.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/docinf.hxx>
#include <sot/storage.hxx>
#include <svl/sharedstringpool.hxx>

#include <rtl/math.hxx>
#include <rtl/ustring.hxx>
#include <unotools/localedatawrapper.hxx>

#include <document.hxx>
#include <docsh.hxx>
#include <attrib.hxx>
#include <dbdata.hxx>
#include <globalnames.hxx>
#include <docoptio.hxx>
#include <xihelper.hxx>
#include <xicontent.hxx>
#include <xilink.hxx>
#include <xiescher.hxx>
#include <xistyle.hxx>
#include <excdefs.hxx>

#include <excform.hxx>
#include <queryentry.hxx>

#include <com/sun/star/document/XDocumentProperties.hpp>
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <com/sun/star/container/XIndexContainer.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <cppuhelper/implbase.hxx>
#include "xltoolbar.hxx"
#include <oox/ole/vbaproject.hxx>
#include <oox/ole/olestorage.hxx>

using namespace com::sun::star;
using namespace ::comphelper;

//OleNameOverrideContainer

namespace {

class OleNameOverrideContainer : public ::cppu::WeakImplHelper< container::XNameContainer >
{
private:
    typedef std::unordered_map< OUString, uno::Reference< container::XIndexContainer > > NamedIndexToOleName;
    NamedIndexToOleName  IdToOleNameHash;
    std::mutex m_aMutex;
public:
    // XElementAccess
    virtual uno::Type SAL_CALL getElementType(  ) override { return  cppu::UnoType<container::XIndexContainer>::get(); }
    virtual sal_Bool SAL_CALL hasElements(  ) override
    {
        std::unique_lock aGuard( m_aMutex );
        return ( !IdToOleNameHash.empty() );
    }
    // XNameAccess
    virtual uno::Any SAL_CALL getByName( const OUString& aName ) override
    {
        std::unique_lock aGuard( m_aMutex );
        auto it = IdToOleNameHash.find( aName );
        if ( it == IdToOleNameHash.end() )
            throw container::NoSuchElementException();
        return uno::Any( it->second );
    }
    virtual uno::Sequence< OUString > SAL_CALL getElementNames(  ) override
    {
        std::unique_lock aGuard( m_aMutex );
        return comphelper::mapKeysToSequence( IdToOleNameHash);
    }
    virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
    {
        std::unique_lock aGuard( m_aMutex );
        return ( IdToOleNameHash.find( aName ) != IdToOleNameHash.end() );
    }

    // XNameContainer
    virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override
    {
        std::unique_lock aGuard( m_aMutex );
        auto it = IdToOleNameHash.find( aName );
        if ( it != IdToOleNameHash.end() )
            throw container::ElementExistException();
        uno::Reference< container::XIndexContainer > xElement;
        if ( ! ( aElement >>= xElement ) )
            throw lang::IllegalArgumentException();
        IdToOleNameHash[ aName ] = std::move(xElement);
    }
    virtual void SAL_CALL removeByName( const OUString& aName ) override
    {
        std::unique_lock aGuard( m_aMutex );
        if ( IdToOleNameHash.erase( aName ) == 0 )
            throw container::NoSuchElementException();
    }
    virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override
    {
        std::unique_lock aGuard( m_aMutex );
        auto it = IdToOleNameHash.find( aName );
        if ( it == IdToOleNameHash.end() )
            throw container::NoSuchElementException();
        uno::Reference< container::XIndexContainer > xElement;
        if ( ! ( aElement >>= xElement ) )
            throw lang::IllegalArgumentException();
        it->second = std::move(xElement);
    }
};

/** Future Record Type header.
    @return whether read rt matches nRecordID
 */
bool readFrtHeader( XclImpStream& rStrm, sal_uInt16 nRecordID )
{
    sal_uInt16 nRt = rStrm.ReaduInt16();
    rStrm.Ignore(10);   // grbitFrt (2 bytes) and reserved (8 bytes)
    return nRt == nRecordID;
}

}

ImportExcel8::ImportExcel8( XclImpRootData& rImpData, SvStream& rStrm ) :
    ImportExcel( rImpData, rStrm )
{
    // replace BIFF2-BIFF5 formula importer with BIFF8 formula importer
    pFormConv.reset(new ExcelToSc8( GetRoot() ));
    pExcRoot->pFmlaConverter = pFormConv.get();
}

ImportExcel8::~ImportExcel8()
{
}

void ImportExcel8::Calccount()
{
    ScDocOptions    aOpt = rD.GetDocOptions();
    aOpt.SetIterCount( aIn.ReaduInt16() );
    rD.SetDocOptions( aOpt );
}

void ImportExcel8::Precision()
{
    ScDocOptions aOpt = rD.GetDocOptions();
    aOpt.SetCalcAsShown( aIn.ReaduInt16() == 0 );
    rD.SetDocOptions( aOpt );
}

void ImportExcel8::Delta()
{
    ScDocOptions    aOpt = rD.GetDocOptions();
    aOpt.SetIterEps( aIn.ReadDouble() );
    rD.SetDocOptions( aOpt );
}

void ImportExcel8::Iteration()
{
    ScDocOptions    aOpt = rD.GetDocOptions();
    aOpt.SetIter( aIn.ReaduInt16() == 1 );
    rD.SetDocOptions( aOpt );
}

void ImportExcel8::Boundsheet()
{
    sal_uInt8           nLen;
    sal_uInt16          nGrbit;

    aIn.DisableDecryption();
    maSheetOffsets.push_back( aIn.ReaduInt32() );
    aIn.EnableDecryption();
    nGrbit = aIn.ReaduInt16();
    nLen = aIn.ReaduInt8();

    OUString aName( aIn.ReadUniString( nLen ) );
    GetTabInfo().AppendXclTabName( aName, nBdshtTab );

    SCTAB nScTab = nBdshtTab;
    if( nScTab > 0 )
    {
        OSL_ENSURE( !rD.HasTable( nScTab ), "ImportExcel8::Boundsheet - sheet exists already" );
        rD.MakeTable( nScTab );
    }

    if( ( nGrbit & 0x0001 ) || ( nGrbit & 0x0002 ) )
        rD.SetVisible( nScTab, false );

    if( !rD.RenameTab( nScTab, aName ) )
    {
        rD.CreateValidTabName( aName );
        rD.RenameTab( nScTab, aName );
    }

    nBdshtTab++;
}

void ImportExcel8::Scenman()
{
    sal_uInt16              nLastDispl;

    aIn.Ignore( 4 );
    nLastDispl = aIn.ReaduInt16();

    maScenList.nLastScenario = nLastDispl;
}

void ImportExcel8::Scenario()
{
    maScenList.aEntries.push_back( std::make_unique<ExcScenario>( aIn, *pExcRoot ) );
}

void ImportExcel8::Labelsst()
{
    XclAddress aXclPos;
    sal_uInt16 nXF;
    sal_uInt32  nSst;

    aIn >> aXclPos;
    nXF = aIn.ReaduInt16();
    nSst = aIn.ReaduInt32(  );

    ScAddress aScPos( ScAddress::UNINITIALIZED );
    if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
    {
        GetXFRangeBuffer().SetXF( aScPos, nXF );
        const XclImpString* pXclStr = GetSst().GetString(nSst);
        if (pXclStr)
            XclImpStringHelper::SetToDocument(GetDocImport(), aScPos, *this, *pXclStr, nXF);
    }
}

void ImportExcel8::FeatHdr()
{
    if (!readFrtHeader( aIn, 0x0867))
        return;

    // Feature type (isf) can be EXC_ISFPROTECTION, EXC_ISFFEC2 or
    // EXC_ISFFACTOID.
    sal_uInt16 nFeatureType = aIn.ReaduInt16();
    if (nFeatureType != EXC_ISFPROTECTION)
        // We currently only support import of enhanced protection data.
        return;

    aIn.Ignore(1); // always 1

    GetSheetProtectBuffer().ReadOptions( aIn, GetCurrScTab() );
}

void ImportExcel8::Feat()
{
    if (!readFrtHeader( aIn, 0x0868))
        return;

    // Feature type (isf) can be EXC_ISFPROTECTION, EXC_ISFFEC2 or
    // EXC_ISFFACTOID.
    sal_uInt16 nFeatureType = aIn.ReaduInt16();
    if (nFeatureType != EXC_ISFPROTECTION)
        // We currently only support import of enhanced protection data.
        return;

    aIn.Ignore(5);                          // reserved1 (1 byte) and reserved2 (4 bytes)

    sal_uInt16 nCref = aIn.ReaduInt16();    // number of ref elements
    aIn.Ignore(4);                          // size if EXC_ISFFEC2, else 0 and to be ignored
    aIn.Ignore(2);                          // reserved3 (2 bytes)

    ScEnhancedProtection aProt;
    if (nCref)
    {
        XclRangeList aRefs;
        aRefs.Read( aIn, true, nCref);
        if (!aRefs.empty())
        {
            aProt.maRangeList = new ScRangeList;
            GetAddressConverter().ConvertRangeList( *aProt.maRangeList, aRefs, GetCurrScTab(), false);
        }
    }

    // FeatProtection structure follows in record.

    aProt.mnAreserved = aIn.ReaduInt32();
    aProt.mnPasswordVerifier = aIn.ReaduInt32();
    aProt.maTitle = aIn.ReadUniString();
    if ((aProt.mnAreserved & 0x00000001) == 0x00000001)
    {
        sal_uInt32 nCbSD = aIn.ReaduInt32();
        // TODO: could here be some sanity check applied to not allocate 4GB?
        aProt.maSecurityDescriptor.resize( nCbSD);
        std::size_t nRead = aIn.Read(aProt.maSecurityDescriptor.data(), nCbSD);
        if (nRead < nCbSD)
            aProt.maSecurityDescriptor.resize( nRead);
    }

    GetSheetProtectBuffer().AppendEnhancedProtection( aProt, GetCurrScTab() );
}

void ImportExcel8::ReadBasic()
{
    ScDocShell* pShell = GetDocShell();
    rtl::Reference<SotStorage> xRootStrg = GetRootStorage();
    if( !pShell || !xRootStrg.is() )
        return;

    try
    {
        // #FIXME need to get rid of this, we can also do this from within oox
        // via the "ooo.vba.VBAGlobals" service
        if( ( officecfg::Office::Calc::Filter::Import::VBA::Load::get() ||
              officecfg::Office::Calc::Filter::Import::VBA::Save::get() ) &&
              officecfg::Office::Calc::Filter::Import::VBA::Executable::get() )
        {
            // see if we have the XCB stream
            rtl::Reference<SotStorageStream> xXCB = xRootStrg->OpenSotStream( u"XCB"_ustr, StreamMode::STD_READ );
            if ( xXCB.is()|| ERRCODE_NONE == xXCB->GetError() )
            {
                ScCTBWrapper wrapper;
                if ( wrapper.Read( *xXCB ) )
                {
#ifdef DEBUG_SC_EXCEL
                    wrapper.Print( stderr );
#endif
                    wrapper.ImportCustomToolBar( *pShell );
                }
            }
        }
        try
        {
            const uno::Reference< uno::XComponentContext >& aCtx( ::comphelper::getProcessComponentContext() );
            SfxMedium& rMedium = GetMedium();
            uno::Reference< io::XInputStream > xIn = rMedium.GetInputStream();
            oox::ole::OleStorage root( aCtx, xIn, false );
            oox::StorageRef vbaStg = root.openSubStorage( u"_VBA_PROJECT_CUR"_ustr, false );
            if ( vbaStg )
            {
                oox::ole::VbaProject aVbaPrj( aCtx, pShell->GetModel(), u"Calc" );
                // collect names of embedded form controls, as specified in the VBA project
                uno::Reference< container::XNameContainer > xOleNameOverrideSink( new OleNameOverrideContainer );
                aVbaPrj.setOleOverridesSink( xOleNameOverrideSink );
                aVbaPrj.importVbaProject( *vbaStg );
                GetObjectManager().SetOleNameOverrideInfo( xOleNameOverrideSink );
            }
        }
        catch( uno::Exception& )
        {
        }
    }
    catch( uno::Exception& )
    {
    }
}

void ImportExcel8::EndSheet()
{
    ImportExcel::EndSheet();
    GetCondFormatManager().Apply();
    GetValidationManager().Apply();
}

void ImportExcel8::PostDocLoad()
{
#if HAVE_FEATURE_SCRIPTING
    // reading basic has been delayed until sheet objects (codenames etc.) are read
    if( HasBasic() )
        ReadBasic();
#endif
    // #i11776# filtered ranges before outlines and hidden rows
    if( pExcRoot->pAutoFilterBuffer )
        pExcRoot->pAutoFilterBuffer->Apply();

    GetWebQueryBuffer().Apply();    //TODO: test if extant
    GetSheetProtectBuffer().Apply();
    GetDocProtectBuffer().Apply();

    ImportExcel::PostDocLoad();

    // check scenarios; Attention: This increases the table count of the document!!
    if( !rD.IsClipboard() && !maScenList.aEntries.empty() )
    {
        rD.UpdateChartListenerCollection();    // references in charts must be updated

        maScenList.Apply( GetRoot() );
    }

    // read doc info (no docshell while pasting from clipboard)
    ScDocShell* pShell = GetDocShell();
    if(!pShell)
        return;

    // BIFF5+ without storage is possible
    rtl::Reference<SotStorage> xRootStrg = GetRootStorage();
    if( xRootStrg.is() ) try
    {
        uno::Reference< document::XDocumentPropertiesSupplier > xDPS( static_cast<cppu::OWeakObject*>(pShell->GetModel()), uno::UNO_QUERY_THROW );
        uno::Reference< document::XDocumentProperties > xDocProps( xDPS->getDocumentProperties(), uno::UNO_SET_THROW );
        sfx2::LoadOlePropertySet( xDocProps, xRootStrg.get() );
    }
    catch( uno::Exception& )
    {
    }

    // #i45843# Pivot tables are now handled outside of PostDocLoad, so they are available
    // when formula cells are calculated, for the GETPIVOTDATA function.
}

// autofilter

void ImportExcel8::FilterMode()
{
    // The FilterMode record exists: if either the AutoFilter
    // record exists or an Advanced Filter is saved and stored
    // in the sheet. Thus if the FilterMode records only exists
    // then the latter is true...
    if( !pExcRoot->pAutoFilterBuffer ) return;

    XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
    if( pData )
        pData->SetAutoOrAdvanced();
}

void ImportExcel8::AutoFilterInfo()
{
    if( !pExcRoot->pAutoFilterBuffer ) return;

    XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
    if( pData )
    {
        pData->SetAdvancedRange( nullptr );
        pData->Activate();
    }
}

void ImportExcel8::AutoFilter()
{
    if( !pExcRoot->pAutoFilterBuffer ) return;

    XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
    if( pData )
        pData->ReadAutoFilter(aIn, GetDoc().GetSharedStringPool());
}

XclImpAutoFilterData::XclImpAutoFilterData( RootData* pRoot, const ScRange& rRange ) :
        ExcRoot( pRoot ),
        pCurrDBData(nullptr),
        bActive( false ),
        bCriteria( false ),
        bAutoOrAdvanced(false)
{
    aParam.nCol1 = rRange.aStart.Col();
    aParam.nRow1 = rRange.aStart.Row();
    aParam.nTab = rRange.aStart.Tab();
    aParam.nCol2 = rRange.aEnd.Col();
    aParam.nRow2 = rRange.aEnd.Row();

    aParam.bInplace = true;

}

namespace {

OUString CreateFromDouble( double fVal )
{
    return rtl::math::doubleToUString(fVal,
                rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
                ScGlobal::getLocaleData().getNumDecimalSep()[0], true);
}

}

void XclImpAutoFilterData::SetCellAttribs()
{
    ScDocument& rDoc = pExcRoot->pIR->GetDoc();
    for ( SCCOL nCol = StartCol(); nCol <= EndCol(); nCol++ )
    {
        ScMF nFlag = rDoc.GetAttr( nCol, StartRow(), Tab(), ATTR_MERGE_FLAG ).GetValue();
        rDoc.ApplyAttr( nCol, StartRow(), Tab(), ScMergeFlagAttr( nFlag | ScMF::Auto) );
    }
}

void XclImpAutoFilterData::InsertQueryParam()
{
    if (!pCurrDBData)
        return;

    ScRange aAdvRange;
    bool    bHasAdv = pCurrDBData->GetAdvancedQuerySource( aAdvRange );
    if( bHasAdv )
        pExcRoot->pIR->GetDoc().CreateQueryParam(aAdvRange, aParam);

    pCurrDBData->SetQueryParam( aParam );
    if( bHasAdv )
        pCurrDBData->SetAdvancedQuerySource( &aAdvRange );
    else
    {
        pCurrDBData->SetAutoFilter( true );
        SetCellAttribs();
    }
}

static void ExcelQueryToOooQuery( OUString& aStr, ScQueryEntry& rEntry )
{
    if (rEntry.eOp != SC_EQUAL && rEntry.eOp != SC_NOT_EQUAL)
        return;

    sal_Int32   nLen = aStr.getLength();
    sal_Unicode nStart = aStr[0];
    sal_Unicode nEnd   = aStr[ nLen-1 ];
    if( nLen > 2 && nStart == '*' && nEnd == '*' )
    {
        aStr = aStr.copy( 1, nLen-2 );
        rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_CONTAINS : SC_DOES_NOT_CONTAIN;
    }
    else if( nLen > 1 && nStart == '*' && nEnd != '*' )
    {
        aStr = aStr.copy( 1 );
        rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_ENDS_WITH : SC_DOES_NOT_END_WITH;
    }
    else if( nLen > 1 && nStart != '*' && nEnd == '*' )
    {
        aStr = aStr.copy( 0, nLen-1 );
        rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_BEGINS_WITH : SC_DOES_NOT_BEGIN_WITH;
    }
    else if( nLen == 2 && nStart == '*' && nEnd == '*' )
    {
        aStr = aStr.copy( 1 );
    }
}

void XclImpAutoFilterData::ReadAutoFilter(
    XclImpStream& rStrm, svl::SharedStringPool& rPool )
{
    sal_uInt16 nCol, nFlags;
    nCol = rStrm.ReaduInt16();
    nFlags = rStrm.ReaduInt16();

    ScQueryConnect eConn = ::get_flagvalue( nFlags, EXC_AFFLAG_ANDORMASK, SC_OR, SC_AND );
    bool bSimple1    = ::get_flag(nFlags, EXC_AFFLAG_SIMPLE1);
    bool bSimple2    = ::get_flag(nFlags, EXC_AFFLAG_SIMPLE2);
    bool bTop10      = ::get_flag(nFlags, EXC_AFFLAG_TOP10);
    bool bTopOfTop10 = ::get_flag(nFlags, EXC_AFFLAG_TOP10TOP);
    bool bPercent    = ::get_flag(nFlags, EXC_AFFLAG_TOP10PERC);
    sal_uInt16 nCntOfTop10 = nFlags >> 7;

    if( bTop10 )
    {
        ScQueryEntry& aEntry = aParam.AppendEntry();
        ScQueryEntry::Item& rItem = aEntry.GetQueryItem();
        aEntry.bDoQuery = true;
        aEntry.nField = static_cast<SCCOLROW>(StartCol() + static_cast<SCCOL>(nCol));
        aEntry.eOp = bTopOfTop10 ?
            (bPercent ? SC_TOPPERC : SC_TOPVAL) : (bPercent ? SC_BOTPERC : SC_BOTVAL);
        aEntry.eConnect = SC_AND;

        rItem.meType = ScQueryEntry::ByString;
        rItem.maString = rPool.intern(OUString::number(nCntOfTop10));

        rStrm.Ignore(20);
        return;
    }

    sal_uInt8   nType, nOper, nBoolErr, nVal;
    sal_Int32   nRK;
    double      fVal;

    sal_uInt8 nStrLen[2] = { 0, 0 };
    ScQueryEntry aEntries[2];

    for (size_t nE = 0; nE < 2; ++nE)
    {
        ScQueryEntry& rEntry = aEntries[nE];
        ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
        bool bIgnore = false;

        nType = rStrm.ReaduInt8();
        nOper = rStrm.ReaduInt8();
        switch( nOper )
        {
            case EXC_AFOPER_LESS:
                rEntry.eOp = SC_LESS;
            break;
            case EXC_AFOPER_EQUAL:
                rEntry.eOp = SC_EQUAL;
            break;
            case EXC_AFOPER_LESSEQUAL:
                rEntry.eOp = SC_LESS_EQUAL;
            break;
            case EXC_AFOPER_GREATER:
                rEntry.eOp = SC_GREATER;
            break;
            case EXC_AFOPER_NOTEQUAL:
                rEntry.eOp = SC_NOT_EQUAL;
            break;
            case EXC_AFOPER_GREATEREQUAL:
                rEntry.eOp = SC_GREATER_EQUAL;
            break;
            default:
                rEntry.eOp = SC_EQUAL;
        }

        switch( nType )
        {
            case EXC_AFTYPE_RK:
                nRK = rStrm.ReadInt32();
                rStrm.Ignore( 4 );
                rItem.maString = rPool.intern(
                    CreateFromDouble(XclTools::GetDoubleFromRK(nRK)));
            break;
            case EXC_AFTYPE_DOUBLE:
                fVal = rStrm.ReadDouble();
                rItem.maString = rPool.intern(CreateFromDouble(fVal));
            break;
            case EXC_AFTYPE_STRING:
                rStrm.Ignore( 4 );
                nStrLen[ nE ] = rStrm.ReaduInt8();
                rStrm.Ignore( 3 );
                rItem.maString = svl::SharedString();
            break;
            case EXC_AFTYPE_BOOLERR:
                nBoolErr = rStrm.ReaduInt8();
                nVal = rStrm.ReaduInt8();
                rStrm.Ignore( 6 );
                rItem.maString = rPool.intern(OUString::number(nVal));
                bIgnore = (nBoolErr != 0);
            break;
            case EXC_AFTYPE_EMPTY:
                rEntry.SetQueryByEmpty();
            break;
            case EXC_AFTYPE_NOTEMPTY:
                rEntry.SetQueryByNonEmpty();
            break;
            default:
                rStrm.Ignore( 8 );
                bIgnore = true;
        }

        if (!bIgnore)
        {
            rEntry.bDoQuery = true;
            rItem.meType = ScQueryEntry::ByString;
            rEntry.nField = static_cast<SCCOLROW>(StartCol() + static_cast<SCCOL>(nCol));
            rEntry.eConnect = nE ? eConn : SC_AND;
        }
    }

    if (eConn == SC_AND)
    {
        for (size_t nE = 0; nE < 2; ++nE)
        {
            if (nStrLen[nE] && aEntries[nE].bDoQuery)
            {
                OUString aStr = rStrm.ReadUniString(nStrLen[nE]);
                ExcelQueryToOooQuery(aStr, aEntries[nE]);
                aEntries[nE].GetQueryItem().maString = rPool.intern(aStr);
                aParam.AppendEntry() = aEntries[nE];
            }
        }
    }
    else
    {
        assert( eConn == SC_OR && "eConn should be SC_AND or SC_OR");
        // Import only when both conditions are for simple equality, else
        // import only the 1st condition due to conflict with the ordering of
        // conditions. #i39464#.
        //
        // Example: Let A1 be a condition of column A, and B1 and B2
        // conditions of column B, connected with OR. Excel performs 'A1 AND
        // (B1 OR B2)' in this case, but Calc would do '(A1 AND B1) OR B2'
        // instead.

        if (bSimple1 && bSimple2 && nStrLen[0] && nStrLen[1])
        {
            // Two simple OR'ed equal conditions.  We can import this correctly.
            ScQueryEntry& rEntry = aParam.AppendEntry();
            rEntry.bDoQuery = true;
            rEntry.eOp = SC_EQUAL;
            rEntry.eConnect = SC_AND;
            ScQueryEntry::QueryItemsType aItems;
            aItems.reserve(2);
            ScQueryEntry::Item aItem1, aItem2;
            aItem1.maString = rPool.intern(rStrm.ReadUniString(nStrLen[0]));
            aItem1.meType = ScQueryEntry::ByString;
            aItem2.maString = rPool.intern(rStrm.ReadUniString(nStrLen[1]));
            aItem2.meType = ScQueryEntry::ByString;
            aItems.push_back(aItem1);
            aItems.push_back(aItem2);
            rEntry.GetQueryItems().swap(aItems);
        }
        else if (nStrLen[0] && aEntries[0].bDoQuery)
        {
            // Due to conflict, we can import only the first condition.
            OUString aStr = rStrm.ReadUniString(nStrLen[0]);
            ExcelQueryToOooQuery(aStr, aEntries[0]);
            aEntries[0].GetQueryItem().maString = rPool.intern(aStr);
            aParam.AppendEntry() = aEntries[0];
        }
    }
}

void XclImpAutoFilterData::SetAdvancedRange( const ScRange* pRange )
{
    if (pRange)
    {
        aCriteriaRange = *pRange;
        bCriteria = true;
    }
    else
        bCriteria = false;
}

void XclImpAutoFilterData::SetExtractPos( const ScAddress& rAddr )
{
    aParam.nDestCol = rAddr.Col();
    aParam.nDestRow = rAddr.Row();
    aParam.nDestTab = rAddr.Tab();
    aParam.bInplace = false;
    aParam.bDestPers = true;
}

void XclImpAutoFilterData::Apply()
{
    // Create the ScDBData() object if the AutoFilter is activated
    // or if we need to create the Advanced Filter.
    if( bActive || bCriteria)
    {
        ScDocument& rDoc = pExcRoot->pIR->GetDoc();
        pCurrDBData = new ScDBData(STR_DB_LOCAL_NONAME, Tab(),
                                StartCol(),StartRow(), EndCol(),EndRow() );
        if(bCriteria)
        {
            EnableRemoveFilter();

            pCurrDBData->SetQueryParam( aParam );
            pCurrDBData->SetAdvancedQuerySource(&aCriteriaRange);
        }
        else
            pCurrDBData->SetAdvancedQuerySource(nullptr);
        rDoc.SetAnonymousDBData(Tab(), std::unique_ptr<ScDBData>(pCurrDBData));
    }

    if( bActive )
    {
        InsertQueryParam();
    }
}

void XclImpAutoFilterData::EnableRemoveFilter()
{
    // only if this is a saved Advanced filter
    if( !bActive && bAutoOrAdvanced )
    {
        ScQueryEntry& aEntry = aParam.AppendEntry();
        aEntry.bDoQuery = true;
    }

    // TBD: force the automatic activation of the
    // "Remove Filter" by setting a virtual mouse click
    // inside the advanced range
}

void XclImpAutoFilterBuffer::Insert( RootData* pRoot, const ScRange& rRange)
{
    if( !GetByTab( rRange.aStart.Tab() ) )
        maFilters.push_back( std::make_shared<XclImpAutoFilterData>( pRoot, rRange ));
}

void XclImpAutoFilterBuffer::AddAdvancedRange( const ScRange& rRange )
{
    XclImpAutoFilterData* pData = GetByTab( rRange.aStart.Tab() );
    if( pData )
        pData->SetAdvancedRange( &rRange );
}

void XclImpAutoFilterBuffer::AddExtractPos( const ScRange& rRange )
{
    XclImpAutoFilterData* pData = GetByTab( rRange.aStart.Tab() );
    if( pData )
        pData->SetExtractPos( rRange.aStart );
}

void XclImpAutoFilterBuffer::Apply()
{
    for( const auto& rFilterPtr : maFilters )
        rFilterPtr->Apply();
}

XclImpAutoFilterData* XclImpAutoFilterBuffer::GetByTab( SCTAB nTab )
{
    for( const auto& rFilterPtr : maFilters )
    {
        if( rFilterPtr->Tab() == nTab )
            return rFilterPtr.get();
    }
    return nullptr;
}

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