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

#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <com/sun/star/sheet/XDatabaseRanges.hpp>
#include <com/sun/star/sheet/XSpreadsheet.hpp>
#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
#include <com/sun/star/sheet/NamedRangeFlag.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/document/XDocumentProperties.hpp>
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <com/sun/star/document/XViewDataSupplier.hpp>
#include <comphelper/servicehelper.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <o3tl/any.hxx>
#include <osl/thread.h>
#include <osl/diagnose.h>
#include <oox/helper/progressbar.hxx>
#include <oox/helper/propertyset.hxx>
#include <oox/ole/vbaproject.hxx>
#include <oox/token/properties.hxx>
#include <addressconverter.hxx>
#include <connectionsbuffer.hxx>
#include <defnamesbuffer.hxx>
#include <excelchartconverter.hxx>
#include <excelfilter.hxx>
#include <externallinkbuffer.hxx>
#include <formulaparser.hxx>
#include <pagesettings.hxx>
#include <pivotcachebuffer.hxx>
#include <pivottablebuffer.hxx>
#include <scenariobuffer.hxx>
#include <sharedstringsbuffer.hxx>
#include <stylesbuffer.hxx>
#include <tablebuffer.hxx>
#include <themebuffer.hxx>
#include <unitconverter.hxx>
#include <viewsettings.hxx>
#include <workbooksettings.hxx>
#include <worksheetbuffer.hxx>
#include <docsh.hxx>
#include <document.hxx>
#include <docuno.hxx>
#include <dbdocfun.hxx>
#include <rangenam.hxx>
#include <tokenarray.hxx>
#include <tokenuno.hxx>
#include <dbdata.hxx>
#include <datauno.hxx>
#include <globalnames.hxx>
#include <documentimport.hxx>
#include <drwlayer.hxx>
#include <globstr.hrc>
#include <scresid.hxx>

#include <formulabuffer.hxx>
#include <editutil.hxx>
#include <editeng/editstat.hxx>
#include <unotools/charclass.hxx>
#include <o3tl/string_view.hxx>
#include <ViewSettingsSequenceDefines.hxx>

#include <memory>

namespace oox::xls {

using namespace ::com::sun::star::container;
using namespace ::com::sun::star::document;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::sheet;
using namespace ::com::sun::star::style;
using namespace ::com::sun::star::table;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;

using ::oox::core::FilterBase;
using ::oox::core::FragmentHandler;
using ::oox::core::XmlFilterBase;

bool IgnoreCaseCompare::operator()( std::u16string_view rName1, std::u16string_view rName2 ) const
{
    // TODO: compare with collator
    return o3tl::compareToIgnoreAsciiCase(rName1, rName2 ) < 0;
}

class WorkbookGlobals
{
public:
    // noncopyable ------------------------------------------------------------

    WorkbookGlobals(const WorkbookGlobals&) = delete;
    const WorkbookGlobals& operator=(const WorkbookGlobals&) = delete;

    explicit            WorkbookGlobals( ExcelFilter& rFilter );
                        ~WorkbookGlobals();

    /** Returns true, if this helper refers to a valid document. */
    bool         isValid() const { return mxDoc.is(); }

    // filter -----------------------------------------------------------------

    /** Returns the base filter object (base class of all filters). */
    FilterBase&  getBaseFilter() const { return mrBaseFilter; }
    /** Returns the filter progress bar. */
    SegmentProgressBar& getProgressBar() const { return *mxProgressBar; }
    /** Returns the VBA project storage. */
    const StorageRef&   getVbaProjectStorage() const { return mxVbaPrjStrg; }
    /** Returns the index of the current Calc sheet, if filter currently processes a sheet. */
    sal_Int16    getCurrentSheetIndex() const { return mnCurrSheet; }
    /** Returns true when reading a file generated by a known good generator. */
    bool         isGeneratorKnownGood() const { return mbGeneratorKnownGood; }
    /** Returns true if any formula cell appears to have a calculated value
        not 0.0 nor "", or when reading a known good generator's file. */
    bool         hasCalculatedFormulaCells() const { return mbHasCalculatedFormulaCells; }
    void         setCalculatedFormulaCells() { mbHasCalculatedFormulaCells = true; }

    /** Sets the VBA project storage used to import VBA source code and forms. */
    void         setVbaProjectStorage( const StorageRef& rxVbaPrjStrg ) { mxVbaPrjStrg = rxVbaPrjStrg; }
    /** Sets the index of the current Calc sheet, if filter currently processes a sheet. */
    void         setCurrentSheetIndex( SCTAB nSheet ) { mnCurrSheet = nSheet; }

    // document model ---------------------------------------------------------

    ScEditEngineDefaulter& getEditEngine() const
    {
        return *mxEditEngine;
    }

    ScDocument& getScDocument() { return *mpDoc; }

    ScDocumentImport& getDocImport();

    /** Returns a reference to the source/target spreadsheet document model. */
    const rtl::Reference< ScModelObj >& getDocument() const { return mxDoc; }
    /** Returns the cell or page styles container from the Calc document. */
    Reference< XNameContainer > getStyleFamily( bool bPageStyles ) const;
    /** Returns the specified cell or page style from the Calc document. */
    Reference< XStyle > getStyleObject( const OUString& rStyleName, bool bPageStyle ) const;
    /** Creates and returns a defined name on-the-fly in the Calc document. */
    WorkbookHelper::RangeDataRet createNamedRangeObject(OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags);
    /** Creates and returns a defined name on the-fly in the correct Calc sheet. */
    WorkbookHelper::RangeDataRet createLocalNamedRangeObject(OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags, sal_Int32 nTab);
    /** Creates and returns a database range on-the-fly in the Calc document. */
    rtl::Reference<ScDatabaseRangeObj> createDatabaseRangeObject( OUString& orName, const ScRange& rRangeAddr );
    /** Creates and returns an unnamed database range on-the-fly in the Calc document. */
    Reference< XDatabaseRange > createUnnamedDatabaseRangeObject( const ScRange& rRangeAddr );
    /** Finds the (already existing) database range of the given formula token index. */
    ScDBData* findDatabaseRangeByIndex( sal_uInt16 nIndex );
    /** Creates and returns a com.sun.star.style.Style object for cells or pages. */
    Reference< XStyle > createStyleObject( OUString& orStyleName, bool bPageStyle );
    /** Helper to switch chart data table - specifically for xlsx imports */
    void useInternalChartDataTable( bool bInternal );

    // buffers ----------------------------------------------------------------

    FormulaBuffer& getFormulaBuffer() const { return *mxFormulaBuffer; }
    /** Returns the global workbook settings object. */
    WorkbookSettings& getWorkbookSettings() const { return *mxWorkbookSettings; }
    /** Returns the workbook and sheet view settings object. */
    ViewSettings& getViewSettings() const { return *mxViewSettings; }
    /** Returns the worksheet buffer containing sheet names and properties. */
    WorksheetBuffer& getWorksheets() const { return *mxWorksheets; }
    /** Returns the office theme object read from the theme substorage. */
    ThemeBuffer& getTheme() const { return *mxTheme; }
    /** Returns all cell formatting objects read from the styles substream. */
    StylesBuffer& getStyles() const { return *mxStyles; }
    /** Returns the shared strings read from the shared strings substream. */
    SharedStringsBuffer& getSharedStrings() const { return *mxSharedStrings; }
    /** Returns the external links read from the external links substream. */
    ExternalLinkBuffer& getExternalLinks() const { return *mxExtLinks; }
    /** Returns the defined names read from the workbook globals. */
    DefinedNamesBuffer& getDefinedNames() const { return *mxDefNames; }
    /** Returns the tables collection (equivalent to Calc's database ranges). */
    TableBuffer& getTables() const { return *mxTables; }
    /** Returns the scenarios collection. */
    ScenarioBuffer& getScenarios() const { return *mxScenarios; }
    /** Returns the collection of external data connections. */
    ConnectionsBuffer&  getConnections() const { return *mxConnections; }
    /** Returns the collection of pivot caches. */
    PivotCacheBuffer& getPivotCaches() const { return *mxPivotCaches; }
    /** Returns the collection of pivot tables. */
    PivotTableBuffer& getPivotTables() { return *mxPivotTables; }
    /** Shared cache of Font Classifications to avoid repeated lookups */
    FontClassificationMap& getFontClassificationCache() { return maFontClassificationMap; }

    // converters -------------------------------------------------------------

    /** Returns a shared import formula parser. */
    FormulaParser& getFormulaParser() const { return *mxFmlaParser; }
    /** Returns an unshared import formula parser. */
    FormulaParser* createFormulaParser() { return new FormulaParser(*this); }
    /** Returns the measurement unit converter. */
    UnitConverter& getUnitConverter() const { return *mxUnitConverter; }
    /** Returns the converter for string to cell address/range conversion. */
    AddressConverter& getAddressConverter() const { return *mxAddrConverter; }
    /** Returns the chart object converter. */
    oox::drawingml::chart::ChartConverter* getChartConverter() const { return mxChartConverter.get(); }
    /** Returns the page/print settings converter. */
    PageSettingsConverter& getPageSettingsConverter() const { return *mxPageSettConverter; }

    // OOXML/BIFF12 specific --------------------------------------------------

    /** Returns the base OOXML/BIFF12 filter object. */
    XmlFilterBase& getOoxFilter() const { return *mpOoxFilter; }

    // BIFF2-BIFF8 specific ---------------------------------------------------

    /** Returns the text encoding used to import/export byte strings. */
    rtl_TextEncoding getTextEncoding() const { return meTextEnc; }

private:
    /** Initializes some basic members and sets needed document properties. */
    void                initialize();
    /** Finalizes the filter process (sets some needed document properties). */
    void                finalize();

    void                ImplDestroy();

private:
    typedef ::std::unique_ptr< ScEditEngineDefaulter >    EditEngineDefaulterPtr;
    typedef ::std::unique_ptr< FormulaBuffer >          FormulaBufferPtr;
    typedef ::std::unique_ptr< SegmentProgressBar >     ProgressBarPtr;
    typedef ::std::unique_ptr< WorkbookSettings >       WorkbookSettPtr;
    typedef ::std::unique_ptr< ViewSettings >           ViewSettingsPtr;
    typedef ::std::unique_ptr< WorksheetBuffer >        WorksheetBfrPtr;
    typedef ::std::shared_ptr< ThemeBuffer >          ThemeBfrRef;
    typedef ::std::unique_ptr< StylesBuffer >           StylesBfrPtr;
    typedef ::std::unique_ptr< SharedStringsBuffer >    SharedStrBfrPtr;
    typedef ::std::unique_ptr< ExternalLinkBuffer >     ExtLinkBfrPtr;
    typedef ::std::unique_ptr< DefinedNamesBuffer >     DefNamesBfrPtr;
    typedef ::std::unique_ptr< TableBuffer >            TableBfrPtr;
    typedef ::std::unique_ptr< ScenarioBuffer >         ScenarioBfrPtr;
    typedef ::std::unique_ptr< ConnectionsBuffer >      ConnectionsBfrPtr;
    typedef ::std::unique_ptr< PivotCacheBuffer >       PivotCacheBfrPtr;
    typedef ::std::unique_ptr< PivotTableBuffer >       PivotTableBfrPtr;
    typedef ::std::unique_ptr< FormulaParser >          FormulaParserPtr;
    typedef ::std::unique_ptr< UnitConverter >          UnitConvPtr;
    typedef ::std::unique_ptr< AddressConverter >       AddressConvPtr;
    typedef ::std::unique_ptr< oox::drawingml::chart::ChartConverter > ExcelChartConvPtr;
    typedef ::std::unique_ptr< PageSettingsConverter >  PageSettConvPtr;

    OUString            maCellStyles;           /// Style family name for cell styles.
    OUString            maPageStyles;           /// Style family name for page styles.
    OUString            maCellStyleServ;        /// Service name for a cell style.
    OUString            maPageStyleServ;        /// Service name for a page style.
    rtl::Reference< ScModelObj > mxDoc;         /// Document model (XSpreadsheetDocument)
    FilterBase&         mrBaseFilter;           /// Base filter object.
    ExcelFilter&        mrExcelFilter;          /// Base object for registration of this structure.
    ProgressBarPtr      mxProgressBar;          /// The progress bar.
    StorageRef          mxVbaPrjStrg;           /// Storage containing the VBA project.
    sal_Int16           mnCurrSheet;            /// Current sheet index in Calc document.
    bool                mbGeneratorKnownGood;   /// Whether reading a file generated by Excel or Calc.
    bool                mbHasCalculatedFormulaCells;  /// Assumed to have calculated formula cells.

    // buffers
    FormulaBufferPtr    mxFormulaBuffer;
    WorkbookSettPtr     mxWorkbookSettings;     /// Global workbook settings.
    ViewSettingsPtr     mxViewSettings;         /// Workbook and sheet view settings.
    WorksheetBfrPtr     mxWorksheets;           /// Sheet info buffer.
    ThemeBfrRef         mxTheme;                /// Formatting theme from theme substream.
    StylesBfrPtr        mxStyles;               /// All cell style objects from styles substream.
    SharedStrBfrPtr     mxSharedStrings;        /// All strings from shared strings substream.
    ExtLinkBfrPtr       mxExtLinks;             /// All external links.
    DefNamesBfrPtr      mxDefNames;             /// All defined names.
    TableBfrPtr         mxTables;               /// All tables (database ranges).
    ScenarioBfrPtr      mxScenarios;            /// All scenarios.
    ConnectionsBfrPtr   mxConnections;          /// All external data connections.
    PivotCacheBfrPtr    mxPivotCaches;          /// All pivot caches in the document.
    PivotTableBfrPtr    mxPivotTables;          /// All pivot tables in the document.
    FontClassificationMap maFontClassificationMap; /// Shared to avoid repeated lookups

    // converters
    FormulaParserPtr    mxFmlaParser;           /// Import formula parser.
    UnitConvPtr         mxUnitConverter;        /// General unit converter.
    AddressConvPtr      mxAddrConverter;        /// Cell address and cell range address converter.
    ExcelChartConvPtr   mxChartConverter;       /// Chart object converter.
    PageSettConvPtr     mxPageSettConverter;    /// Page/print settings converter.

    EditEngineDefaulterPtr mxEditEngine;

    // OOXML/BIFF12 specific
    XmlFilterBase*      mpOoxFilter;            /// Base OOXML/BIFF12 filter object.

    // BIFF2-BIFF8 specific
    rtl_TextEncoding    meTextEnc;              /// BIFF byte string text encoding.
    ScDocument* mpDoc;
    ScDocShell* mpDocShell;
    std::unique_ptr<ScDocumentImport> mxDocImport;
};

WorkbookGlobals::WorkbookGlobals( ExcelFilter& rFilter ) :
    mrBaseFilter( rFilter ),
    mrExcelFilter( rFilter ),
    mpOoxFilter( &rFilter ),
    mpDoc(nullptr),
    mpDocShell(nullptr)
{
    // register at the filter, needed for virtual callbacks (even during construction)
    mrExcelFilter.registerWorkbookGlobals( *this );
    initialize();
}

void WorkbookGlobals::ImplDestroy()
{
    finalize();
    mrExcelFilter.unregisterWorkbookGlobals();
}

WorkbookGlobals::~WorkbookGlobals()
{
    suppress_fun_call_w_exception(ImplDestroy());
}

ScDocumentImport& WorkbookGlobals::getDocImport()
{
    return *mxDocImport;
}

Reference< XNameContainer > WorkbookGlobals::getStyleFamily( bool bPageStyles ) const
{
    Reference< XNameContainer > xStylesNC;
    try
    {
        Reference< XNameAccess > xFamiliesNA( mxDoc->getStyleFamilies(), UNO_SET_THROW );
        xStylesNC.set( xFamiliesNA->getByName( bPageStyles ? maPageStyles : maCellStyles ), UNO_QUERY );
    }
    catch( Exception& )
    {
    }
    OSL_ENSURE( xStylesNC.is(), "WorkbookGlobals::getStyleFamily - cannot access style family" );
    return xStylesNC;
}

Reference< XStyle > WorkbookGlobals::getStyleObject( const OUString& rStyleName, bool bPageStyle ) const
{
    Reference< XStyle > xStyle;
    try
    {
        Reference< XNameContainer > xStylesNC( getStyleFamily( bPageStyle ), UNO_SET_THROW );
        xStyle.set( xStylesNC->getByName( rStyleName ), UNO_QUERY );
    }
    catch( Exception& )
    {
    }
    OSL_ENSURE( xStyle.is(), "WorkbookGlobals::getStyleObject - cannot access style object" );
    return xStyle;
}

namespace {

WorkbookHelper::RangeDataRet lcl_addNewByName(ScDocument& rDoc, ScRangeName* pNames, const OUString& rName, sal_Int16 nIndex, sal_Int32 nUnoType)
{
    bool bDone = false;
    ScRangeData::Type nNewType = ScRangeData::Type::Name;
    if ( nUnoType & NamedRangeFlag::FILTER_CRITERIA )    nNewType |= ScRangeData::Type::Criteria;
    if ( nUnoType & NamedRangeFlag::PRINT_AREA )         nNewType |= ScRangeData::Type::PrintArea;
    if ( nUnoType & NamedRangeFlag::COLUMN_HEADER )      nNewType |= ScRangeData::Type::ColHeader;
    if ( nUnoType & NamedRangeFlag::ROW_HEADER )         nNewType |= ScRangeData::Type::RowHeader;
    if ( nUnoType & NamedRangeFlag::HIDDEN )             nNewType |= ScRangeData::Type::Hidden;
    ScTokenArray aTokenArray(rDoc);
    ScRangeData* pNew = new ScRangeData(rDoc, rName, aTokenArray, ScAddress(), nNewType);
    pNew->GuessPosition();
    if ( nIndex )
        pNew->SetIndex( nIndex );
    // create but not insert hidden FILTER_CRITERIA named ranges to ScRangeName
    if (((nUnoType & NamedRangeFlag::HIDDEN) == NamedRangeFlag::HIDDEN)
        && ((nUnoType & NamedRangeFlag::FILTER_CRITERIA) == NamedRangeFlag::FILTER_CRITERIA))
    {
        return WorkbookHelper::RangeDataRet(pNew, true);
    }
    if ( pNames->insert(pNew) )
        bDone = true;
    if (!bDone)
    {
        delete pNew;
        throw RuntimeException();
    }
    return WorkbookHelper::RangeDataRet(pNew, false);
}

OUString findUnusedName( const ScRangeName* pRangeName, const OUString& rSuggestedName )
{
    OUString aNewName = rSuggestedName;
    sal_Int32 nIndex = 0;
    while(pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(aNewName)))
        aNewName = rSuggestedName + OUStringChar('_') + OUString::number( nIndex++ );

    return aNewName;
}

}

WorkbookHelper::RangeDataRet WorkbookGlobals::createNamedRangeObject(
    OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags)
{
    // create the name and insert it into the Calc document
    WorkbookHelper::RangeDataRet aScRangeData(nullptr, false);
    if( !orName.isEmpty() )
    {
        ScDocument& rDoc =  getScDocument();
        ScRangeName* pNames = rDoc.GetRangeName();
        // find an unused name
        orName = findUnusedName( pNames, orName );
        // create the named range
        aScRangeData = lcl_addNewByName(rDoc, pNames, orName, nIndex, nNameFlags);
    }
    return aScRangeData;
}

WorkbookHelper::RangeDataRet WorkbookGlobals::createLocalNamedRangeObject(
    OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags, sal_Int32 nTab)
{
    // create the name and insert it into the Calc document
    WorkbookHelper::RangeDataRet aScRangeData(nullptr, false);
    if( !orName.isEmpty() )
    {
        ScDocument& rDoc =  getScDocument();
        ScRangeName* pNames = rDoc.GetRangeName( nTab );
        if(!pNames)
            throw RuntimeException(u"invalid sheet index used"_ustr);
        // find an unused name
        orName = findUnusedName( pNames, orName );
        // create the named range
        aScRangeData = lcl_addNewByName(rDoc, pNames, orName, nIndex, nNameFlags);
    }
    return aScRangeData;
}

rtl::Reference<ScDatabaseRangeObj> WorkbookGlobals::createDatabaseRangeObject( OUString& orName, const ScRange& rRangeAddr )
{
    // validate cell range
    ScRange aDestRange = rRangeAddr;
    bool bValidRange = getAddressConverter().validateCellRange( aDestRange, true, true );

    ScDocShell* pDocSh = getScDocument().GetDocumentShell();
    // create database range and insert it into the Calc document
    if( bValidRange && !orName.isEmpty() && pDocSh) try
    {
        // find an unused name
        OUString aNewName = orName;
        sal_Int32 nIndex = -1;
        ScDBCollection* pNames = pDocSh->GetDocument().GetDBCollection();
        while (pNames && pNames->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(aNewName)) != nullptr )
            aNewName = orName + OUStringChar('_') + OUString::number( nIndex++ );
        orName = aNewName;
        // create the database range
        CellRangeAddress aApiRange( aDestRange.aStart.Tab(), aDestRange.aStart.Col(), aDestRange.aStart.Row(),
                                    aDestRange.aEnd.Col(), aDestRange.aEnd.Row() );
        ScDBDocFunc aFunc(*pDocSh);
        ScRange aNameRange( static_cast<SCCOL>(aApiRange.StartColumn), static_cast<SCROW>(aApiRange.StartRow), aApiRange.Sheet,
                            static_cast<SCCOL>(aApiRange.EndColumn),   static_cast<SCROW>(aApiRange.EndRow),   aApiRange.Sheet );
        if(!( aFunc.AddDBRange( orName, aNameRange ) ))
            throw RuntimeException(u"Could not add database range"_ustr);
        return new ScDatabaseRangeObj(pDocSh, orName);
    }
    catch( Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("sc");
    }
    return {};
}

Reference< XDatabaseRange > WorkbookGlobals::createUnnamedDatabaseRangeObject( const ScRange& rRangeAddr )
{
    // validate cell range
    ScRange aDestRange = rRangeAddr;
    bool bValidRange = getAddressConverter().validateCellRange( aDestRange, true, true );

    // create database range and insert it into the Calc document
    Reference< XDatabaseRange > xDatabaseRange;
    if( bValidRange ) try
    {
        ScDocument& rDoc =  getScDocument();
        if( rDoc.GetTableCount() <= aDestRange.aStart.Tab() )
            throw css::lang::IndexOutOfBoundsException();
        std::unique_ptr<ScDBData> pNewDBData(new ScDBData( STR_DB_LOCAL_NONAME, aDestRange.aStart.Tab(),
                                       aDestRange.aStart.Col(), aDestRange.aStart.Row(),
                                       aDestRange.aEnd.Col(), aDestRange.aEnd.Row() ));
        rDoc.SetAnonymousDBData( aDestRange.aStart.Tab() , std::move(pNewDBData) );
        ScDocShell* pDocSh = rDoc.GetDocumentShell();
        xDatabaseRange.set(new ScDatabaseRangeObj(pDocSh, aDestRange.aStart.Tab()));
    }
    catch( Exception& )
    {
    }
    OSL_ENSURE( xDatabaseRange.is(), "WorkbookData::createDatabaseRangeObject - cannot create database range" );
    return xDatabaseRange;
}

ScDBData* WorkbookGlobals::findDatabaseRangeByIndex( sal_uInt16 nIndex )
{
    ScDBCollection* pDBCollection = getScDocument().GetDBCollection();
    if (!pDBCollection)
        return nullptr;
    return pDBCollection->getNamedDBs().findByIndex( nIndex );
}

Reference< XStyle > WorkbookGlobals::createStyleObject( OUString& orStyleName, bool bPageStyle )
{
    Reference< XStyle > xStyle;
    try
    {
        Reference< XNameContainer > xStylesNC( getStyleFamily( bPageStyle ), UNO_SET_THROW );
        xStyle.set( mrBaseFilter.getModelFactory()->createInstance( bPageStyle ? maPageStyleServ : maCellStyleServ ), UNO_QUERY_THROW );
        orStyleName = ContainerHelper::insertByUnusedName( xStylesNC, orStyleName, ' ', Any( xStyle ) );
    }
    catch( Exception& )
    {
    }
    OSL_ENSURE( xStyle.is(), "WorkbookGlobals::createStyleObject - cannot create style" );
    return xStyle;
}

void WorkbookGlobals::useInternalChartDataTable( bool bInternal )
{
    if( bInternal )
        mxChartConverter.reset( new oox::drawingml::chart::ChartConverter() );
    else
        mxChartConverter.reset( new ExcelChartConverter( *this ) );
}

// BIFF specific --------------------------------------------------------------

// private --------------------------------------------------------------------

void WorkbookGlobals::initialize()
{
    maCellStyles = "CellStyles";
    maPageStyles = "PageStyles";
    maCellStyleServ = "com.sun.star.style.CellStyle";
    maPageStyleServ = "com.sun.star.style.PageStyle";
    mnCurrSheet = -1;
    mbGeneratorKnownGood = false;
    mbHasCalculatedFormulaCells = false;
    meTextEnc = osl_getThreadTextEncoding();

    // the spreadsheet document
    mxDoc = &dynamic_cast<ScModelObj&>(*mrBaseFilter.getModel());

    mpDocShell = static_cast<ScDocShell*>(mxDoc->GetEmbeddedObject());
    if (mpDocShell)
        mpDoc = &mpDocShell->GetDocument();

    if (!mpDoc)
        throw RuntimeException(u"Workbookhelper::getScDocument(): Failed to access ScDocument from model"_ustr);

    Reference< XDocumentProperties > xDocProps = mxDoc->getDocumentProperties();
    const OUString aGenerator( xDocProps->getGenerator());

    if (aGenerator.startsWithIgnoreAsciiCase("Microsoft"))
    {
        mbGeneratorKnownGood = true;
        ScCalcConfig aCalcConfig = mpDoc->GetCalcConfig();
        aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_A1 ) ;
        mpDoc->SetCalcConfig(aCalcConfig);
    }
    else if (aGenerator.startsWithIgnoreAsciiCase("LibreOffice"))
    {
        mbGeneratorKnownGood = true;
    }
    mbHasCalculatedFormulaCells = mbGeneratorKnownGood;

    mxDocImport.reset(new ScDocumentImport(*mpDoc));

    mxFormulaBuffer.reset( new FormulaBuffer( *this ) );
    mxWorkbookSettings.reset( new WorkbookSettings( *this ) );
    mxViewSettings.reset( new ViewSettings( *this ) );
    mxWorksheets.reset( new WorksheetBuffer( *this ) );
    mxTheme = std::make_shared<ThemeBuffer>( *this );
    mxStyles.reset( new StylesBuffer( *this ) );
    mxSharedStrings.reset( new SharedStringsBuffer( *this ) );
    mxExtLinks.reset( new ExternalLinkBuffer( *this ) );
    mxDefNames.reset( new DefinedNamesBuffer( *this ) );
    mxTables.reset( new TableBuffer( *this ) );
    mxScenarios.reset( new ScenarioBuffer( *this ) );
    mxConnections.reset( new ConnectionsBuffer( *this ) );
    mxPivotCaches.reset( new PivotCacheBuffer( *this ) );
    mxPivotTables.reset( new PivotTableBuffer( *this ) );

    mxUnitConverter.reset( new UnitConverter( *this ) );
    mxAddrConverter.reset( new AddressConverter( *this ) );
    mxChartConverter.reset( new ExcelChartConverter( *this ) );
    mxPageSettConverter.reset( new PageSettingsConverter( *this ) );

    // initialise edit engine
    ScDocument& rDoc = getScDocument();
    mxEditEngine.reset( new ScEditEngineDefaulter( rDoc.GetEditEnginePool() ) );
    mxEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
    mxEditEngine->SetUpdateLayout( false );
    mxEditEngine->EnableUndo( false );
    mxEditEngine->SetControlWord( mxEditEngine->GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );

    // Initialize a final ODFF OpCodeMap once in case none was created yet, so
    // the subsequent XFormulaOpCodeMapper::getAvailableMappings() via
    // OpCodeProviderImpl::fillEntrySeq() do not have to recreate temporary
    // mappings over and over again.
    // Same for OOXML mapping that is accessed by some base class
    // formula::FormulaCompiler::GetOpCodeMap() calls.
    {
        ScCompiler aCompiler( rDoc, ScAddress(), formula::FormulaGrammar::GRAM_ODFF);
        aCompiler.GetOpCodeMap(css::sheet::FormulaLanguage::ODFF);
        aCompiler.GetOpCodeMap(css::sheet::FormulaLanguage::OOXML);
    }

    // set some document properties needed during import
    if( mrBaseFilter.isImportFilter() )
    {
        // enable editing read-only documents (e.g. from read-only files)
        mpDoc->EnableChangeReadOnly(true);
        // #i76026# disable Undo while loading the document
        mpDoc->EnableUndo(false);
        // #i79826# disable calculating automatic row height while loading the document
        mpDoc->LockAdjustHeight();
        // disable automatic update of linked sheets and DDE links
        mpDoc->EnableExecuteLink(false);

        mxProgressBar.reset( new SegmentProgressBar( mrBaseFilter.getStatusIndicator(), ScResId(STR_LOAD_DOC) ) );
        mxFmlaParser.reset( createFormulaParser() );

        //prevent unnecessary broadcasts and "half way listeners" as
        //is done in ScDocShell::BeforeXMLLoading() for ods
        mpDoc->SetInsertingFromOtherDoc(true);
    }
    else if( mrBaseFilter.isExportFilter() )
    {
        mxProgressBar.reset( new SegmentProgressBar( mrBaseFilter.getStatusIndicator(), ScResId(STR_SAVE_DOC) ) );
    }
}

void WorkbookGlobals::finalize()
{
    // set some document properties needed after import
    if( !mrBaseFilter.isImportFilter() )
        return;

    // #i74668# do not insert default sheets
    mpDocShell->SetEmpty(false);
    // enable automatic update of linked sheets and DDE links
    mpDoc->EnableExecuteLink(true);
    // #i79826# enable updating automatic row height after loading the document
    mpDoc->UnlockAdjustHeight();
    // check settings (potentially asking the user if optimal row height should be run now)
    if (mpDocShell->GetRecalcRowHeightsMode()) // default is to always update
        mpDocShell->UpdateAllRowHeights(/*bOnlyUsedRows=*/true);

    // #i76026# enable Undo after loading the document
    mpDoc->EnableUndo(true);

    // disable editing read-only documents (e.g. from read-only files)
    mpDoc->EnableChangeReadOnly(false);
    // #111099# open forms in alive mode (has no effect, if no controls in document)
    ScDrawLayer* pModel = mpDoc->GetDrawLayer();
    if (pModel)
        pModel->SetOpenInDesignMode(false);
}


WorkbookHelper::~WorkbookHelper()
{
}

/*static*/ WorkbookGlobalsRef WorkbookHelper::constructGlobals( ExcelFilter& rFilter )
{
    WorkbookGlobalsRef xBookGlob = std::make_shared<WorkbookGlobals>( rFilter );
    if( !xBookGlob->isValid() )
        xBookGlob.reset();
    return xBookGlob;
}

// filter ---------------------------------------------------------------------

FilterBase& WorkbookHelper::getBaseFilter() const
{
    return mrBookGlob.getBaseFilter();
}

SegmentProgressBar& WorkbookHelper::getProgressBar() const
{
    return mrBookGlob.getProgressBar();
}

sal_Int16 WorkbookHelper::getCurrentSheetIndex() const
{
    return mrBookGlob.getCurrentSheetIndex();
}

bool WorkbookHelper::isGeneratorKnownGood() const
{
    return mrBookGlob.isGeneratorKnownGood();
}

bool WorkbookHelper::hasCalculatedFormulaCells() const
{
    return mrBookGlob.hasCalculatedFormulaCells();
}

void WorkbookHelper::setCalculatedFormulaCells()
{
    return mrBookGlob.setCalculatedFormulaCells();
}

void WorkbookHelper::setVbaProjectStorage( const StorageRef& rxVbaPrjStrg )
{
    mrBookGlob.setVbaProjectStorage( rxVbaPrjStrg );
}

void WorkbookHelper::setCurrentSheetIndex( SCTAB nSheet )
{
    mrBookGlob.setCurrentSheetIndex( nSheet );
}

void WorkbookHelper::finalizeWorkbookImport()
{
    // workbook settings, document and sheet view settings
    mrBookGlob.getWorkbookSettings().finalizeImport();
    mrBookGlob.getViewSettings().finalizeImport();

    // Import the VBA project (after finalizing workbook settings which
    // contains the workbook code name).  Do it before processing formulas in
    // order to correctly resolve VBA custom function names.
    StorageRef xVbaPrjStrg = mrBookGlob.getVbaProjectStorage();
    if( xVbaPrjStrg && xVbaPrjStrg->isStorage() )
        getBaseFilter().getVbaProject().importModulesAndForms( *xVbaPrjStrg, getBaseFilter().getGraphicHelper() );

    // need to import formulas before scenarios
    mrBookGlob.getFormulaBuffer().finalizeImport();

    // Insert all pivot tables. Must be done after loading all sheets and
    // formulas, because data pilots expect existing source data on
    // creation.
    getPivotTables().finalizeImport();

    /*  Insert scenarios after all sheet processing is done, because new hidden
        sheets are created for scenarios which would confuse code that relies
        on certain sheet indexes. Must be done after pivot tables too. */
    mrBookGlob.getScenarios().finalizeImport();

    /*  Set 'Default' page style to automatic page numbering (default is manual
        number 1). Otherwise hidden sheets (e.g. for scenarios) which have
        'Default' page style will break automatic page numbering for following
        sheets. Automatic numbering is set by passing the value 0. */
    PropertySet aDefPageStyle( getStyleObject( u"Default"_ustr, true ) );
    aDefPageStyle.setProperty< sal_Int16 >( PROP_FirstPageNumber, 0 );

    // Has any string ref syntax been imported?
    // If not, we need to take action
    ScCalcConfig aCalcConfig = getScDocument().GetCalcConfig();

    if ( !aCalcConfig.mbHasStringRefSyntax )
    {
        aCalcConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_A1_XL_A1;
        getScDocument().SetCalcConfig(aCalcConfig);
    }

    // set selected sheet and positionleft/positiontop for OLE objects
    rtl::Reference<ScModelObj> xViewDataSupplier(getDocument());
    if (!xViewDataSupplier.is())
        return;

    Reference<XIndexAccess> xIndexAccess(xViewDataSupplier->getViewData());
    if (!(xIndexAccess.is() && xIndexAccess->getCount() > 0))
        return;

    Sequence< PropertyValue > aSeq;
    if (!(xIndexAccess->getByIndex(0) >>= aSeq))
        return;

    OUString sTabName;
    Reference< XNameAccess > xSheetsNC;
    for (const auto& rProp : aSeq)
    {
        OUString sName(rProp.Name);
        if (sName == SC_ACTIVETABLE)
        {
            if(rProp.Value >>= sTabName)
            {
                SCTAB nTab(0);
                if (getScDocument().GetTable(sTabName, nTab))
                    getScDocument().SetVisibleTab(nTab);
            }
        }
        else if (sName == SC_TABLES)
        {
            rProp.Value >>= xSheetsNC;
        }
    }
    if (!(xSheetsNC.is() && xSheetsNC->hasByName(sTabName)))
        return;

    Sequence<PropertyValue> aProperties;
    Any aAny = xSheetsNC->getByName(sTabName);
    if ( !(aAny >>= aProperties) )
        return;

    for (const auto& rProp : aProperties)
    {
        OUString sName(rProp.Name);
        if (sName == SC_POSITIONLEFT)
        {
            SCCOL nPosLeft = *o3tl::doAccess<SCCOL>(rProp.Value);
            getScDocument().SetPosLeft(nPosLeft);
        }
        else if (sName == SC_POSITIONTOP)
        {
            SCROW nPosTop = *o3tl::doAccess<SCROW>(rProp.Value);
            getScDocument().SetPosTop(nPosTop);
        }
    }
}

// document model -------------------------------------------------------------

ScDocument& WorkbookHelper::getScDocument()
{
    return mrBookGlob.getScDocument();
}

const ScDocument& WorkbookHelper::getScDocument() const
{
    return mrBookGlob.getScDocument();
}

ScDocumentImport& WorkbookHelper::getDocImport()
{
    return mrBookGlob.getDocImport();
}

const ScDocumentImport& WorkbookHelper::getDocImport() const
{
    return mrBookGlob.getDocImport();
}

ScEditEngineDefaulter& WorkbookHelper::getEditEngine() const
{
    return mrBookGlob.getEditEngine();
}

const rtl::Reference< ScModelObj > & WorkbookHelper::getDocument() const
{
    return mrBookGlob.getDocument();
}

rtl::Reference< ScTableSheetObj > WorkbookHelper::getSheetFromDoc( sal_Int32 nSheet ) const
{
    rtl::Reference< ScTableSheetsObj > xSheetsIA( getDocument()->getScSheets() );
    return xSheetsIA->GetSheetByIndex( nSheet );
}

Reference< XSpreadsheet > WorkbookHelper::getSheetFromDoc( const OUString& rSheet ) const
{
    Reference< XSpreadsheet > xSheet;
    try
    {
        Reference< XNameAccess > xSheetsNA( getDocument()->getSheets(), UNO_QUERY_THROW );
        xSheet.set( xSheetsNA->getByName( rSheet ), UNO_QUERY );
    }
    catch( Exception& )
    {
    }
    return xSheet;
}

Reference< XCellRange > WorkbookHelper::getCellRangeFromDoc( const ScRange& rRange ) const
{
    Reference< XCellRange > xRange;
    try
    {
        Reference< XSpreadsheet > xSheet( getSheetFromDoc( rRange.aStart.Tab() ), UNO_SET_THROW );
        xRange = xSheet->getCellRangeByPosition( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row() );
    }
    catch( Exception& )
    {
    }
    return xRange;
}

Reference< XNameContainer > WorkbookHelper::getCellStyleFamily() const
{
    return mrBookGlob.getStyleFamily( false/*bPageStyles*/ );
}

Reference< XStyle > WorkbookHelper::getStyleObject( const OUString& rStyleName, bool bPageStyle ) const
{
    return mrBookGlob.getStyleObject( rStyleName, bPageStyle );
}

WorkbookHelper::RangeDataRet WorkbookHelper::createNamedRangeObject(OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags) const
{
    return mrBookGlob.createNamedRangeObject(orName, nIndex, nNameFlags);
}

WorkbookHelper::RangeDataRet WorkbookHelper::createLocalNamedRangeObject(OUString& orName, sal_Int32 nIndex, sal_Int32 nNameFlags, sal_Int32 nTab) const
{
    return mrBookGlob.createLocalNamedRangeObject(orName, nIndex, nNameFlags, nTab);
}

rtl::Reference<ScDatabaseRangeObj> WorkbookHelper::createDatabaseRangeObject( OUString& orName, const ScRange& rRangeAddr ) const
{
    return mrBookGlob.createDatabaseRangeObject( orName, rRangeAddr );
}

Reference< XDatabaseRange > WorkbookHelper::createUnnamedDatabaseRangeObject( const ScRange& rRangeAddr ) const
{
    return mrBookGlob.createUnnamedDatabaseRangeObject( rRangeAddr );
}

ScDBData* WorkbookHelper::findDatabaseRangeByIndex( sal_uInt16 nIndex ) const
{
    return mrBookGlob.findDatabaseRangeByIndex( nIndex );
}

Reference< XStyle > WorkbookHelper::createStyleObject( OUString& orStyleName, bool bPageStyle ) const
{
    return mrBookGlob.createStyleObject( orStyleName, bPageStyle );
}

// buffers --------------------------------------------------------------------

FormulaBuffer& WorkbookHelper::getFormulaBuffer() const
{
    return mrBookGlob.getFormulaBuffer();
}

WorkbookSettings& WorkbookHelper::getWorkbookSettings() const
{
    return mrBookGlob.getWorkbookSettings();
}

ViewSettings& WorkbookHelper::getViewSettings() const
{
    return mrBookGlob.getViewSettings();
}

WorksheetBuffer& WorkbookHelper::getWorksheets() const
{
    return mrBookGlob.getWorksheets();
}

ThemeBuffer& WorkbookHelper::getTheme() const
{
    return mrBookGlob.getTheme();
}

StylesBuffer& WorkbookHelper::getStyles() const
{
    return mrBookGlob.getStyles();
}

SharedStringsBuffer& WorkbookHelper::getSharedStrings() const
{
    return mrBookGlob.getSharedStrings();
}

ExternalLinkBuffer& WorkbookHelper::getExternalLinks() const
{
    return mrBookGlob.getExternalLinks();
}

DefinedNamesBuffer& WorkbookHelper::getDefinedNames() const
{
    return mrBookGlob.getDefinedNames();
}

TableBuffer& WorkbookHelper::getTables() const
{
    return mrBookGlob.getTables();
}

ScenarioBuffer& WorkbookHelper::getScenarios() const
{
    return mrBookGlob.getScenarios();
}

ConnectionsBuffer& WorkbookHelper::getConnections() const
{
    return mrBookGlob.getConnections();
}

PivotCacheBuffer& WorkbookHelper::getPivotCaches() const
{
    return mrBookGlob.getPivotCaches();
}

PivotTableBuffer& WorkbookHelper::getPivotTables() const
{
    return mrBookGlob.getPivotTables();
}

FontClassificationMap& WorkbookHelper::getFontClassificationCache() const
{
    return mrBookGlob.getFontClassificationCache();
}

// converters -----------------------------------------------------------------

FormulaParser& WorkbookHelper::getFormulaParser() const
{
    return mrBookGlob.getFormulaParser();
}

FormulaParser* WorkbookHelper::createFormulaParser() const
{
    return mrBookGlob.createFormulaParser();
}

UnitConverter& WorkbookHelper::getUnitConverter() const
{
    return mrBookGlob.getUnitConverter();
}

AddressConverter& WorkbookHelper::getAddressConverter() const
{
    return mrBookGlob.getAddressConverter();
}

oox::drawingml::chart::ChartConverter* WorkbookHelper::getChartConverter() const
{
    return mrBookGlob.getChartConverter();
}

void WorkbookHelper::useInternalChartDataTable( bool bInternal )
{
    mrBookGlob.useInternalChartDataTable( bInternal );
}

PageSettingsConverter& WorkbookHelper::getPageSettingsConverter() const
{
    return mrBookGlob.getPageSettingsConverter();
}

// OOXML/BIFF12 specific ------------------------------------------------------

XmlFilterBase& WorkbookHelper::getOoxFilter() const
{
    return mrBookGlob.getOoxFilter();
}

bool WorkbookHelper::importOoxFragment( const rtl::Reference<FragmentHandler>& rxHandler )
{
    return getOoxFilter().importFragment( rxHandler );
}

bool WorkbookHelper::importOoxFragment( const rtl::Reference<FragmentHandler>& rxHandler, oox::core::FastParser& rParser )
{
    return getOoxFilter().importFragment(rxHandler, rParser);
}

// BIFF specific --------------------------------------------------------------

rtl_TextEncoding WorkbookHelper::getTextEncoding() const
{
    return mrBookGlob.getTextEncoding();
}

} // namespace oox

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