/* -*- 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_SVX_SOURCE_INC_DATANAVI_HXX
#define INCLUDED_SVX_SOURCE_INC_DATANAVI_HXX

#include <config_options.h>
#include <o3tl/sorted_vector.hxx>
#include <vcl/builderpage.hxx>
#include <vcl/idle.hxx>
#include <vcl/transfer.hxx>
#include <vcl/weld.hxx>
#include <svtools/inettbc.hxx>
#include <sfx2/dockwin.hxx>
#include <sfx2/childwin.hxx>
#include <sfx2/ctrlitem.hxx>
#include <svx/dialmgr.hxx>
#include <svx/strings.hrc>
#include <svx/svxdllapi.h>
#include <rtl/ref.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XContainer.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/frame/XFrame.hpp>
#include <com/sun/star/xforms/XFormsUIHelper1.hpp>
#include <com/sun/star/xforms/XModel.hpp>
#include <com/sun/star/xforms/XSubmission.hpp>
#include <com/sun/star/xml/dom/XNode.hpp>
#include <com/sun/star/xml/dom/events/XEventTarget.hpp>

#include "datalistener.hxx"

#include <memory>
#include <string_view>
#include <vector>


namespace svxform
{


    enum DataGroupType
    {
        DGTUnknown = 0,
        DGTInstance,
        DGTSubmission,
        DGTBinding
    };

    enum DataItemType
    {
        DITNone = 0,
        DITText,
        DITAttribute,
        DITElement,
        DITBinding
    };

    struct ItemNode;
    class DataNavigatorWindow;

    class ReplaceString
    {
        OUString m_sDoc_UI;
        OUString m_sInstance_UI;
        OUString m_sNone_UI;

        static constexpr OUString m_sDoc_API = u"all"_ustr;
        static constexpr OUString m_sInstance_API = u"instance"_ustr;
        static constexpr OUString m_sNone_API = u"none"_ustr;

        ReplaceString( const ReplaceString& ) = delete;

    public:
        ReplaceString()
        {
            m_sDoc_UI = SvxResId(RID_STR_REPLACE_DOC);
            m_sInstance_UI = SvxResId(RID_STR_REPLACE_INST);
            m_sNone_UI = SvxResId(RID_STR_REPLACE_NONE);
        }

        /** convert submission replace string from API value to UI value.
            Use 'none' as default. */
        OUString const & toUI( std::u16string_view rStr ) const
        {
            if( rStr == m_sDoc_API )
                return m_sDoc_UI;
            else if( rStr == m_sInstance_API )
                return m_sInstance_UI;
            else
                return m_sNone_UI;
        }

        /** convert submission replace string from UI to API.
            Use 'none' as default. */
        const OUString & toAPI( std::u16string_view rStr ) const
        {
            if( rStr == m_sDoc_UI )
                return m_sDoc_API;
            else if( rStr == m_sInstance_UI )
                return m_sInstance_API;
            else
                return m_sNone_API;
        }
    };

    class MethodString
    {
        OUString m_sPost_UI;
        OUString m_sPut_UI;
        OUString m_sGet_UI;

        static constexpr OUString m_sPost_API = u"post"_ustr;
        static constexpr OUString m_sPut_API = u"put"_ustr;
        static constexpr OUString m_sGet_API = u"get"_ustr;

        MethodString( const MethodString& ) = delete;

    public:

        MethodString()
        {
            m_sPost_UI = SvxResId(RID_STR_METHOD_POST);
            m_sPut_UI  = SvxResId(RID_STR_METHOD_PUT);
            m_sGet_UI  = SvxResId(RID_STR_METHOD_GET);
        }

        /** convert from API to UI; put is default. */
        OUString const & toUI( std::u16string_view rStr ) const
        {
            if( rStr == m_sGet_API )
                return m_sGet_UI;
            else if( rStr == m_sPost_API )
                return m_sPost_UI;
            else
                return m_sPut_UI;
        }

        /** convert from UI to API; put is default */
        const OUString & toAPI( std::u16string_view rStr ) const
        {
            if( rStr == m_sGet_UI )
                return m_sGet_API;
            else if( rStr == m_sPost_UI )
                return m_sPost_API;
            else
                return m_sPut_API;
        }
    };

    class DataTreeDropTarget final : public DropTargetHelper
    {
    private:
        virtual sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt) override;
        virtual sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt) override;

    public:
        DataTreeDropTarget(weld::TreeView& rWidget);
    };

    class XFormsPage final : public BuilderPage
    {
    private:
        MethodString                m_aMethodString;
        ReplaceString               m_aReplaceString;

        weld::Container* m_pParent;
        std::unique_ptr<weld::Toolbar> m_xToolBox;
        std::unique_ptr<weld::Menu> m_xMenu;
        std::unique_ptr<weld::TreeView> m_xItemList;
        std::unique_ptr<weld::TreeIter> m_xScratchIter;

        o3tl::sorted_vector<OUString> m_aRemovedMenuEntries;

        DataTreeDropTarget m_aDropHelper;

        css::uno::Reference< css::xforms::XFormsUIHelper1 >
                                    m_xUIHelper;

        DataNavigatorWindow*        m_pNaviWin;
        bool                        m_bHasModel;
        DataGroupType               m_eGroup;
        // these strings are not valid on the Submission and Binding Page
        // mb: furthermore these are properties of an instance, thus
        // it would be much better to get/set them through the UIHelper
        // interface.
        OUString                    m_sInstanceName;
        OUString                    m_sInstanceURL;
        bool                        m_bLinkOnce;

        DECL_LINK(TbxSelectHdl, const OUString&, void);
        DECL_LINK(ItemSelectHdl, weld::TreeView&, void);
        DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
        DECL_LINK(PopupMenuHdl, const CommandEvent&, bool);

        void                        AddChildren(const weld::TreeIter* _pParent,
                                                const css::uno::Reference< css::xml::dom::XNode >& _xNode);
        bool                        DoToolBoxAction(std::u16string_view rToolBoxID);
        void                        AddEntry(std::unique_ptr<ItemNode> _pNewNode, bool _bIsElement, weld::TreeIter* pRet = nullptr);
        void                        AddEntry(const css::uno::Reference< css::beans::XPropertySet >& _rPropSet, weld::TreeIter* pRet = nullptr);
        void                        EditEntry( const css::uno::Reference< css::beans::XPropertySet >& _rPropSet );
        bool                        RemoveEntry();

        void                        PrepDnD();

        void                        DeleteAndClearTree();

        void                        SetMenuEntrySensitive(const OUString& rIdent, bool bSensitive);

    public:
        XFormsPage(weld::Container* pParent, DataNavigatorWindow* _pNaviWin, DataGroupType _eGroup);
        virtual ~XFormsPage() override;

        bool                 HasModel() const { return m_bHasModel; }
        OUString             SetModel( const css::uno::Reference< css::xforms::XModel > & _xModel, int _nPagePos );
        void                 ClearModel();
        OUString             LoadInstance(const css::uno::Sequence< css::beans::PropertyValue >& _xPropSeq);

        bool                 DoMenuAction(std::u16string_view rMenuID);
        void                 EnableMenuItems();
        void                 SelectFirstEntry();

        const OUString&      GetInstanceName() const { return m_sInstanceName; }
        const OUString&      GetInstanceURL() const { return m_sInstanceURL; }
        bool                 GetLinkOnce() const { return m_bLinkOnce; }
        void                 SetInstanceName( const OUString &name ) { m_sInstanceName=name; }
        void                 SetInstanceURL( const OUString &url ) { m_sInstanceURL=url; }
        void                 SetLinkOnce( bool bLinkOnce ) { m_bLinkOnce=bLinkOnce; }

        css::uno::Reference<css::beans::XPropertySet>
                             GetBindingForNode( const css::uno::Reference<css::xml::dom::XNode> &xNode ) { return m_xUIHelper->getBindingForNode(xNode,true); }
        OUString             GetServiceNameForNode( const css::uno::Reference<css::xml::dom::XNode> &xNode ) { return m_xUIHelper->getDefaultServiceNameForNode(xNode); }
        const css::uno::Reference< css::xforms::XFormsUIHelper1 >&
                             GetXFormsHelper() const { return m_xUIHelper; }
    };

    class DataNavigatorWindow final
    {
    private:
        VclPtr<vcl::Window> m_xParent;
        std::unique_ptr<weld::ComboBox> m_xModelsBox;
        std::unique_ptr<weld::MenuButton> m_xModelBtn;
        std::unique_ptr<weld::Notebook> m_xTabCtrl;
        std::unique_ptr<weld::MenuButton> m_xInstanceBtn;

        std::unique_ptr<XFormsPage> m_xInstPage;
        std::unique_ptr<XFormsPage> m_xSubmissionPage;
        std::unique_ptr<XFormsPage> m_xBindingPage;

        sal_Int32                   m_nLastSelectedPos;
        bool                        m_bShowDetails;
        bool                        m_bIsNotifyDisabled;
        std::vector< std::unique_ptr<XFormsPage> >
                                    m_aPageList;
        std::vector< css::uno::Reference< css::container::XContainer >  >
                                    m_aContainerList;
        std::vector< css::uno::Reference< css::xml::dom::events::XEventTarget > >
                                    m_aEventTargetList;
        Timer                       m_aUpdateTimer;

        ::rtl::Reference < DataListener >
                                    m_xDataListener;
        css::uno::Reference< css::container::XNameContainer >
                                    m_xDataContainer;
        css::uno::Reference< css::frame::XFrame >
                                    m_xFrame;
        css::uno::Reference< css::frame::XModel >
                                    m_xFrameModel;

        DECL_LINK(            ModelSelectListBoxHdl, weld::ComboBox&, void );
        DECL_LINK(            MenuSelectHdl, const OUString&, void );
        DECL_LINK(            MenuActivateHdl, weld::Toggleable&, void );
        DECL_LINK(            ActivatePageHdl, const OUString&, void);
        DECL_LINK(            UpdateHdl, Timer *, void);
        void ModelSelectHdl(const weld::ComboBox*);
        OUString                     GetCurrentPage() const;
        XFormsPage*                 GetPage(const OUString& rCurId);
        void                        LoadModels();
        void                        SetPageModel(const OUString& rCurId);
        void                        ClearAllPageModels( bool bClearPages );
        void                        InitPages();
        void                        CreateInstancePage( const css::uno::Sequence< css::beans::PropertyValue >& _xPropSeq );
        bool                        HasFirstInstancePage() const;
        OUString                     GetNewPageId() const;

        static bool                 IsAdditionalPage(std::u16string_view rIdent);

    public:
        DataNavigatorWindow(vcl::Window* pParent, weld::Builder& rBuilder, SfxBindings const * pBindings);
        ~DataNavigatorWindow();

        static void                 SetDocModified();
        void                        NotifyChanges( bool _bLoadAll = false );
        void                        AddContainerBroadcaster( const css::uno::Reference< css::container::XContainer > & xContainer );
        void                        AddEventBroadcaster( const css::uno::Reference< css::xml::dom::events::XEventTarget >& xTarget );
        void                        RemoveBroadcaster();

        weld::Window*               GetFrameWeld() const { return m_xParent->GetFrameWeld(); }

        bool                        IsShowDetails() const { return m_bShowDetails; }
        void                        DisableNotify( bool _bDisable ) { m_bIsNotifyDisabled = _bDisable; }
    };

    class DataNavigator final : public SfxDockingWindow, public SfxControllerItem
    {
    private:
        std::unique_ptr<DataNavigatorWindow> m_xDataWin;

        virtual Size                CalcDockingSize( SfxChildAlignment ) override;
        virtual SfxChildAlignment   CheckAlignment( SfxChildAlignment, SfxChildAlignment ) override;

    public:
        DataNavigator( SfxBindings* pBindings, SfxChildWindow* pMgr, vcl::Window* pParent );
        virtual ~DataNavigator() override;
        virtual void dispose() override;

        using                       SfxDockingWindow::StateChanged;

        void                        StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState,
                                                  const SfxPoolItem* pState ) override;
    };

    class UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) DataNavigatorManager final : public SfxChildWindow
    {
    public:
        SVX_DLLPRIVATE DataNavigatorManager( vcl::Window* pParent, sal_uInt16 nId,
                              SfxBindings* pBindings, SfxChildWinInfo* pInfo );
        SFX_DECL_CHILDWINDOW( DataNavigatorManager );
    };

    class AddDataItemDialog final : public weld::GenericDialogController
    {
    private:
        css::uno::Reference< css::xforms::XFormsUIHelper1 >
                            m_xUIHelper;
        css::uno::Reference< css::beans::XPropertySet >
                            m_xBinding;
        css::uno::Reference< css::beans::XPropertySet >
                            m_xTempBinding;

        ItemNode*           m_pItemNode;
        DataItemType        m_eItemType;
        OUString            m_sFL_Element;
        OUString            m_sFL_Attribute;
        OUString            m_sFL_Binding;
        OUString            m_sFT_BindingExp;

        std::unique_ptr<weld::Frame> m_xItemFrame;
        std::unique_ptr<weld::Label> m_xNameFT;
        std::unique_ptr<weld::Entry> m_xNameED;
        std::unique_ptr<weld::Label> m_xDefaultFT;
        std::unique_ptr<weld::Entry> m_xDefaultED;
        std::unique_ptr<weld::Button> m_xDefaultBtn;
        std::unique_ptr<weld::Widget> m_xSettingsFrame;
        std::unique_ptr<weld::ComboBox> m_xDataTypeLB;
        std::unique_ptr<weld::CheckButton> m_xRequiredCB;
        std::unique_ptr<weld::Button> m_xRequiredBtn;
        std::unique_ptr<weld::CheckButton> m_xRelevantCB;
        std::unique_ptr<weld::Button> m_xRelevantBtn;
        std::unique_ptr<weld::CheckButton> m_xConstraintCB;
        std::unique_ptr<weld::Button> m_xConstraintBtn;
        std::unique_ptr<weld::CheckButton> m_xReadonlyCB;
        std::unique_ptr<weld::Button> m_xReadonlyBtn;
        std::unique_ptr<weld::CheckButton> m_xCalculateCB;
        std::unique_ptr<weld::Button> m_xCalculateBtn;
        std::unique_ptr<weld::Button> m_xOKBtn;

        void Check(const weld::Toggleable* pBox);
        DECL_LINK(CheckHdl, weld::Toggleable&, void);
        DECL_LINK(ConditionHdl, weld::Button&, void);
        DECL_LINK(OKHdl, weld::Button&, void);

        void                InitDialog();
        void                InitFromNode();
        void                InitDataTypeBox();

    public:
        AddDataItemDialog(
            weld::Window* pParent, ItemNode* _pNode,
            const css::uno::Reference< css::xforms::XFormsUIHelper1 >& _rUIHelper );
        virtual ~AddDataItemDialog() override;

        void                InitText( DataItemType _eType );
    };

    class AddConditionDialog final : public weld::GenericDialogController
    {
    private:
        Idle                           m_aResultIdle;
        OUString                       m_sPropertyName;

        css::uno::Reference< css::xforms::XFormsUIHelper1 >
                                       m_xUIHelper;
        css::uno::Reference< css::beans::XPropertySet >
                                       m_xBinding;

        std::unique_ptr<weld::TextView> m_xConditionED;
        std::unique_ptr<weld::TextView> m_xResultWin;
        std::unique_ptr<weld::Button> m_xEditNamespacesBtn;
        std::unique_ptr<weld::Button> m_xOKBtn;

        DECL_LINK(ModifyHdl, weld::TextView&, void);
        DECL_LINK(ResultHdl, Timer *, void);
        DECL_LINK(EditHdl, weld::Button&, void);
        DECL_LINK(OKHdl, weld::Button&, void);

    public:
        AddConditionDialog(weld::Window* pParent,
            OUString _aPropertyName, const css::uno::Reference< css::beans::XPropertySet >& _rBinding);
        virtual ~AddConditionDialog() override;

        const css::uno::Reference< css::xforms::XFormsUIHelper1 >& GetUIHelper() const { return m_xUIHelper; }
        OUString GetCondition() const { return m_xConditionED->get_text(); }
        void SetCondition(const OUString& _rCondition)
        {
            m_xConditionED->set_text(_rCondition);
            m_aResultIdle.Start();
        }
    };

    class NamespaceItemDialog final : public weld::GenericDialogController
    {
    private:
        AddConditionDialog* m_pConditionDlg;
        std::vector< OUString >    m_aRemovedList;

        css::uno::Reference< css::container::XNameContainer >&
                                   m_rNamespaces;

        std::unique_ptr<weld::TreeView> m_xNamespacesList;
        std::unique_ptr<weld::Button> m_xAddNamespaceBtn;
        std::unique_ptr<weld::Button> m_xEditNamespaceBtn;
        std::unique_ptr<weld::Button> m_xDeleteNamespaceBtn;
        std::unique_ptr<weld::Button> m_xOKBtn;

        DECL_LINK(SelectHdl, weld::TreeView&, void);
        DECL_LINK(ClickHdl, weld::Button&, void);
        DECL_LINK(OKHdl, weld::Button&, void);

        void                LoadNamespaces();

    public:
        NamespaceItemDialog(AddConditionDialog* pParent, css::uno::Reference< css::container::XNameContainer >& _rContainer);
        virtual ~NamespaceItemDialog() override;
    };

    class ManageNamespaceDialog final : public weld::GenericDialogController
    {
    private:
        AddConditionDialog* m_pConditionDlg;

        std::unique_ptr<weld::Entry> m_xPrefixED;
        std::unique_ptr<weld::Entry> m_xUrlED;
        std::unique_ptr<weld::Button> m_xOKBtn;
        std::unique_ptr<weld::Label> m_xAltTitle;

        DECL_LINK(OKHdl, weld::Button&, void);

    public:
        ManageNamespaceDialog(weld::Window* pParent, AddConditionDialog* _pCondDlg, bool bIsEdit);
        virtual ~ManageNamespaceDialog() override;

        void SetNamespace(const OUString& _rPrefix, const OUString& _rURL)
        {
            m_xPrefixED->set_text(_rPrefix);
            m_xUrlED->set_text(_rURL);
        }
        OUString GetPrefix() const { return m_xPrefixED->get_text(); }
        OUString GetURL() const { return m_xUrlED->get_text(); }
    };

    class AddSubmissionDialog final : public weld::GenericDialogController
    {
    private:
        MethodString         m_aMethodString;
        ReplaceString        m_aReplaceString;

        ItemNode*            m_pItemNode;

        css::uno::Reference< css::xforms::XFormsUIHelper1 >
                            m_xUIHelper;
        css::uno::Reference< css::xforms::XSubmission >
                            m_xNewSubmission;
        css::uno::Reference< css::beans::XPropertySet >
                            m_xSubmission;
        css::uno::Reference< css::beans::XPropertySet >
                            m_xTempBinding;
        css::uno::Reference< css::beans::XPropertySet >
                            m_xCreatedBinding;

        std::unique_ptr<weld::Entry> m_xNameED;
        std::unique_ptr<weld::Entry> m_xActionED;
        std::unique_ptr<weld::ComboBox> m_xMethodLB;
        std::unique_ptr<weld::Entry> m_xRefED;
        std::unique_ptr<weld::Button> m_xRefBtn;
        std::unique_ptr<weld::ComboBox> m_xBindLB;
        std::unique_ptr<weld::ComboBox> m_xReplaceLB;
        std::unique_ptr<weld::Button> m_xOKBtn;

        DECL_LINK(RefHdl, weld::Button&, void);
        DECL_LINK(OKHdl, weld::Button&, void);

        void                FillAllBoxes();

    public:
        AddSubmissionDialog(weld::Window* pParent, ItemNode* pNode,
            const css::uno::Reference< css::xforms::XFormsUIHelper1 >& rUIHelper);
        virtual ~AddSubmissionDialog() override;

        const css::uno::Reference< css::xforms::XSubmission >& GetNewSubmission() const { return m_xNewSubmission; }
    };

    class AddModelDialog final : public weld::GenericDialogController
    {
    private:
        std::unique_ptr<weld::Entry> m_xNameED;
        std::unique_ptr<weld::CheckButton> m_xModifyCB;
        std::unique_ptr<weld::Label> m_xAltTitle;

    public:
        AddModelDialog(weld::Window* pParent, bool _bEdit);
        virtual ~AddModelDialog() override;

        OUString         GetName() const { return m_xNameED->get_text(); }
        void             SetName( const OUString& _rName ) { m_xNameED->set_text( _rName );}

        bool             GetModifyDoc() const { return m_xModifyCB->get_active(); }
        void             SetModifyDoc( const bool bModify ) { m_xModifyCB->set_active(bModify); }
    };

    class AddInstanceDialog final : public weld::GenericDialogController
    {
    private:
        OUString                m_sAllFilterName;

        std::unique_ptr<weld::Entry> m_xNameED;
        std::unique_ptr<SvtURLBox> m_xURLED;
        std::unique_ptr<weld::Button> m_xFilePickerBtn;
        std::unique_ptr<weld::CheckButton> m_xLinkInstanceCB;
        std::unique_ptr<weld::Label> m_xAltTitle;

        DECL_LINK(FilePickerHdl, weld::Button&, void);

    public:
        AddInstanceDialog(weld::Window* pParent, bool _bEdit);
        virtual ~AddInstanceDialog() override;

        OUString         GetName() const { return m_xNameED->get_text(); }
        void             SetName( const OUString& rName ) { m_xNameED->set_text( rName );}
        OUString         GetURL() const { return m_xURLED->get_active_text(); }
        void             SetURL( const OUString& rURL ) { m_xURLED->set_entry_text( rURL );}
        bool             IsLinkInstance() const { return m_xLinkInstanceCB->get_active(); }
        void             SetLinkInstance( bool bLink ) { m_xLinkInstanceCB->set_active(bLink); }
    };

    class LinkedInstanceWarningBox final : public weld::MessageDialogController
    {
    public:
        LinkedInstanceWarningBox(weld::Widget* pParent);
    };
}


#endif // INCLUDED_SVX_SOURCE_INC_DATANAVI_HXX

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