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

#ifndef INCLUDED_SC_INC_DBDATA_HXX
#define INCLUDED_SC_INC_DBDATA_HXX

#include "scdllapi.h"
#include "refreshtimer.hxx"
#include "address.hxx"
#include "global.hxx"
#include "rangelst.hxx"

#include <svl/listener.hxx>

#include <memory>
#include <set>
#include <vector>

class ScDocument;
struct ScSortParam;
struct ScQueryParam;
struct ScSubTotalParam;

/** Enum used to indicate which portion of the DBArea is to be considered. */
enum class ScDBDataPortion
{
    TOP_LEFT,   ///< top left cell of area
    AREA        ///< entire area
};

/** Container base class to provide selected access for ScDBData. */
class ScDBDataContainerBase
{
public:
    ScDBDataContainerBase( ScDocument& rDoc ) : mrDoc(rDoc) {}
    virtual ~ScDBDataContainerBase() {}
    ScDocument& GetDocument() const;
    ScRangeList& GetDirtyTableColumnNames();

protected:
    ScDocument& mrDoc;
    ScRangeList maDirtyTableColumnNames;
};

class ScDBData : public SvtListener, public ScRefreshTimer
{
private:
    std::unique_ptr<ScSortParam> mpSortParam;
    std::unique_ptr<ScQueryParam> mpQueryParam;
    std::unique_ptr<ScSubTotalParam> mpSubTotal;
    std::unique_ptr<ScImportParam> mpImportParam;

    ScDBDataContainerBase* mpContainer;

    /// DBParam
    const OUString aName;
    OUString aUpper;
    SCTAB           nTable;
    SCCOL           nStartCol;
    SCROW           nStartRow;
    SCCOL           nEndCol;
    SCROW           nEndRow;
    bool            bByRow;
    bool            bHasHeader;
    bool            bHasTotals;
    bool            bDoSize;
    bool            bKeepFmt;
    bool            bStripData;

    /// QueryParam
    bool            bIsAdvanced;        ///< true if created by advanced filter
    ScRange         aAdvSource;         ///< source range

    bool            bDBSelection;       ///< not in Param: if selection, block update

    sal_uInt16      nIndex;             ///< unique index formulas
    bool            bAutoFilter;        ///< AutoFilter? (not saved)
    bool            bModified;          ///< is set/cleared for/by(?) UpdateReference

    ::std::vector< OUString > maTableColumnNames;   ///< names of table columns
    bool            mbTableColumnNamesDirty;
    SCSIZE          nFilteredRowCount;

    using ScRefreshTimer::operator==;

public:
    struct less
    {
        bool operator() (const std::unique_ptr<ScDBData>& left, const std::unique_ptr<ScDBData>& right) const;
    };

    SC_DLLPUBLIC ScDBData(const OUString& rName,
             SCTAB nTab,
             SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
             bool bByR = true, bool bHasH = true, bool bTotals = false);
    ScDBData(const ScDBData& rData);
    ScDBData(const OUString& rName, const ScDBData& rData);
    virtual ~ScDBData() override;

    virtual void Notify( const SfxHint& rHint ) override;

    ScDBData&   operator= (const ScDBData& rData) ;

    bool        operator== (const ScDBData& rData) const;

    const OUString& GetName() const { return aName; }
    const OUString& GetUpperName() const { return aUpper; }
    SCTAB       GetTab() const                  { return nTable; }
    void        GetArea(SCTAB& rTab, SCCOL& rCol1, SCROW& rRow1, SCCOL& rCol2, SCROW& rRow2) const;
    SC_DLLPUBLIC void GetArea(ScRange& rRange) const;
    void        SetArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2);
    void        MoveTo(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2);
    void        SetByRow(bool bByR)             { bByRow = bByR; }
    bool        HasHeader() const               { return bHasHeader; }
    void        SetHeader(bool bHasH)           { bHasHeader = bHasH; }
    bool        HasTotals() const               { return bHasTotals; }
    void        SetTotals(bool bTotals)         { bHasTotals = bTotals; }
    void        SetIndex(sal_uInt16 nInd)           { nIndex = nInd; }
    sal_uInt16  GetIndex() const                { return nIndex; }
    bool        IsDoSize() const                { return bDoSize; }
    void        SetDoSize(bool bSet)            { bDoSize = bSet; }
    bool        IsKeepFmt() const               { return bKeepFmt; }
    void        SetKeepFmt(bool bSet)           { bKeepFmt = bSet; }
    bool        IsStripData() const             { return bStripData; }
    void        SetStripData(bool bSet)         { bStripData = bSet; }

    void        SetContainer( ScDBDataContainerBase* pContainer ) { mpContainer = pContainer; }
    /** Returns header row range if has headers, else invalid range. */
    ScRange     GetHeaderArea() const;
    void        StartTableColumnNamesListener();
    void        EndTableColumnNamesListener();
    SC_DLLPUBLIC void SetTableColumnNames( const ::std::vector< OUString >& rNames );
    SC_DLLPUBLIC const ::std::vector< OUString >& GetTableColumnNames() const { return maTableColumnNames; }
    bool        AreTableColumnNamesDirty() const { return mbTableColumnNamesDirty; }

    /** Refresh/update the column names with the header row's cell contents. */
    SC_DLLPUBLIC void RefreshTableColumnNames( ScDocument* pDoc );

    /** Refresh/update the column names with the header row's cell contents
        within the given range. */
    void RefreshTableColumnNames( ScDocument* pDoc, const ScRange& rRange );

    /** Finds the column named rName and returns the corresponding offset
        within the table.
        @returns -1 if not found.

        XXX NOTE: there is no refresh of names or anything implemented yet, use
        this only during document load time.
     */
    sal_Int32   GetColumnNameOffset( const OUString& rName ) const;

    /** Returns table column name if nCol is within column range and name
        is stored, else empty string. */
    const OUString& GetTableColumnName( SCCOL nCol ) const;

    OUString GetSourceString() const;
    OUString GetOperations() const;

    void        GetSortParam(ScSortParam& rSortParam) const;
    void        SetSortParam(const ScSortParam& rSortParam);

    /** Remember some more settings of ScSortParam, only to be called at
        anonymous DB ranges as it at least overwrites bHasHeader. */
    void        UpdateFromSortParam( const ScSortParam& rSortParam );

    SC_DLLPUBLIC void       GetQueryParam(ScQueryParam& rQueryParam) const;
    SC_DLLPUBLIC void       SetQueryParam(const ScQueryParam& rQueryParam);
    SC_DLLPUBLIC bool       GetAdvancedQuerySource(ScRange& rSource) const;
    SC_DLLPUBLIC void       SetAdvancedQuerySource(const ScRange* pSource);

    void        GetSubTotalParam(ScSubTotalParam& rSubTotalParam) const;
    void        SetSubTotalParam(const ScSubTotalParam& rSubTotalParam);

    void        GetImportParam(ScImportParam& rImportParam) const;
    void        SetImportParam(const ScImportParam& rImportParam);

    bool        IsDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) const;
    bool        IsDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const;

    bool        HasImportParam() const;
    SC_DLLPUBLIC bool HasQueryParam() const;
    bool        HasSortParam() const;
    bool        HasSubTotalParam() const;

    bool        HasImportSelection() const      { return bDBSelection; }
    void        SetImportSelection(bool bSet)   { bDBSelection = bSet; }

    bool        HasAutoFilter() const       { return bAutoFilter; }
    void        SetAutoFilter(bool bSet)    { bAutoFilter = bSet; }

    bool        IsModified() const          { return bModified; }
    void        SetModified(bool bMod)      { bModified = bMod; }

    void    UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos );
    void    UpdateReference(const ScDocument* pDoc, UpdateRefMode eUpdateRefMode,
                        SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
                        SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
                        SCCOL nDx, SCROW nDy, SCTAB nDz);

    void ExtendDataArea(const ScDocument* pDoc);
    void CalcSaveFilteredCount(SCSIZE nNonFilteredRowCount);
    void GetFilterSelCount(SCSIZE& nSelected, SCSIZE& nTotal);

private:

    void AdjustTableColumnNames( UpdateRefMode eUpdateRefMode, SCCOL nDx, SCCOL nCol1,
            SCCOL nOldCol1, SCCOL nOldCol2, SCCOL nNewCol1, SCCOL nNewCol2 );
    void InvalidateTableColumnNames( bool bSwapToEmptyNames );
};

class SC_DLLPUBLIC ScDBCollection
{
public:
    enum RangeType { GlobalNamed, GlobalAnonymous, SheetAnonymous };

    /**
     * Stores global named database ranges.
     */
    class SC_DLLPUBLIC NamedDBs : public ScDBDataContainerBase
    {
        friend class ScDBCollection;

        typedef ::std::set<std::unique_ptr<ScDBData>, ScDBData::less> DBsType;
        DBsType m_DBs;
        ScDBCollection& mrParent;
        NamedDBs(ScDBCollection& rParent, ScDocument& rDoc);
        NamedDBs(const NamedDBs& r, ScDBCollection& rParent);
        NamedDBs(const NamedDBs&) = delete;
        virtual ~NamedDBs() override;
        NamedDBs & operator=(NamedDBs const&) = delete;
        void initInserted( ScDBData* p );

    public:
        typedef DBsType::iterator iterator;
        typedef DBsType::const_iterator const_iterator;

        iterator begin();
        iterator end();
        const_iterator begin() const;
        const_iterator end() const;
        ScDBData* findByIndex(sal_uInt16 nIndex);
        ScDBData* findByUpperName(const OUString& rName);
        iterator findByUpperName2(const OUString& rName);

        /** Takes ownership of p and attempts to insert it into the collection.
            Deletes p if it could not be inserted, i.e. duplicate name.
            @return <TRUE/> if inserted, else <FALSE/>.
         */
        bool insert(std::unique_ptr<ScDBData> p);

        void erase(const iterator& itr);
        bool empty() const;
        size_t size() const;
        bool operator== (const NamedDBs& r) const;
    };

    /**
     * Stores global anonymous database ranges.
     */
    class AnonDBs
    {
        typedef ::std::vector<std::unique_ptr<ScDBData>> DBsType;
        DBsType m_DBs;

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

    public:
        AnonDBs();
        AnonDBs(AnonDBs const&);

        typedef DBsType::iterator iterator;
        typedef DBsType::const_iterator const_iterator;

        iterator begin();
        iterator end();
        const_iterator begin() const;
        const_iterator end() const;
        const ScDBData* findAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) const;
        const ScDBData* findByRange(const ScRange& rRange) const;
        void deleteOnTab(SCTAB nTab);
        ScDBData* getByRange(const ScRange& rRange);
        void insert(ScDBData* p);
        bool empty() const;
        bool has( const ScDBData* p ) const;
        bool operator== (const AnonDBs& r) const;
    };

private:
    Link<Timer *, void> aRefreshHandler;
    ScDocument* pDoc;
    sal_uInt16 nEntryIndex;         ///< counter for unique indices
    NamedDBs maNamedDBs;
    AnonDBs maAnonDBs;

public:
    ScDBCollection(ScDocument* pDocument);
    ScDBCollection(const ScDBCollection& r);

    NamedDBs& getNamedDBs() { return maNamedDBs;}
    const NamedDBs& getNamedDBs() const { return maNamedDBs;}

    AnonDBs& getAnonDBs() { return maAnonDBs;}
    const AnonDBs& getAnonDBs() const { return maAnonDBs;}

    const ScDBData* GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) const;
    ScDBData* GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion);
    const ScDBData* GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const;
    ScDBData* GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2);
    ScDBData* GetDBNearCursor(SCCOL nCol, SCROW nRow, SCTAB nTab );

    void RefreshDirtyTableColumnNames();

    void    DeleteOnTab( SCTAB nTab );
    void    UpdateReference(UpdateRefMode eUpdateRefMode,
                                SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
                                SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
                                SCCOL nDx, SCROW nDy, SCTAB nDz);
    void    UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos );

    void            SetRefreshHandler( const Link<Timer *, void>& rLink )
                        { aRefreshHandler = rLink; }
    const Link<Timer *, void>& GetRefreshHandler() const { return aRefreshHandler; }

    bool empty() const;
    bool operator== (const ScDBCollection& r) const;
};

#endif

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