/* -*- 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 <sfx2/request.hxx>
#include <svl/eitem.hxx>
#include <svl/stritem.hxx>
#include <editeng/numitem.hxx>
#include <editeng/brushitem.hxx>
#include <osl/diagnose.h>
#include <numrule.hxx>

#include <cmdid.h>
#include <wrtsh.hxx>
#include <view.hxx>
#include <viewopt.hxx>
#include <wdocsh.hxx>
#include <poolfmt.hxx>
#include <textsh.hxx>
#include <swabstdlg.hxx>
#include <SwStyleNameMapper.hxx>
#include <sfx2/tabdlg.hxx>
#include <svx/nbdtmg.hxx>
#include <svx/nbdtmgfact.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/bindings.hxx>
#include <memory>

void SwTextShell::ExecEnterNum(SfxRequest &rReq)
{
    //Because the record before any shell exchange.
    switch(rReq.GetSlot())
    {
    case FN_NUM_NUMBERING_ON:
    {
        GetShell().StartAllAction();
        const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_1);
        bool bMode = !GetShell().SelectionHasNumber(); // #i29560#
        if ( pItem )
            bMode = pItem->GetValue();
        else
            rReq.AppendItem( SfxBoolItem( FN_PARAM_1, bMode ) );

        if ( bMode != (GetShell().SelectionHasNumber()) ) // #i29560#
        {
            rReq.Done();
            if( bMode )
                GetShell().NumOn();
            else
                GetShell().NumOrBulletOff(); // #i29560#
        }
        bool bNewResult = GetShell().SelectionHasNumber();
        if (bNewResult!=bMode) {
            SfxBindings& rBindings = GetView().GetViewFrame().GetBindings();
            SfxBoolItem aItem(FN_NUM_NUMBERING_ON,!bNewResult);
            rBindings.SetState(aItem);
            SfxBoolItem aNewItem(FN_NUM_NUMBERING_ON,bNewResult);
            rBindings.SetState(aNewItem);
        }
        GetShell().EndAllAction();
    }
    break;
    case FN_NUM_BULLET_ON:
    {
        GetShell().StartAllAction();
        const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(FN_PARAM_1);
        bool bMode = !GetShell().SelectionHasBullet(); // #i29560#
        if ( pItem )
            bMode = pItem->GetValue();
        else
            rReq.AppendItem( SfxBoolItem( FN_PARAM_1, bMode ) );

        if ( bMode != (GetShell().SelectionHasBullet()) ) // #i29560#
        {
            rReq.Done();
            if( bMode )
                GetShell().BulletOn();
            else
                GetShell().NumOrBulletOff(); // #i29560#
        }
        bool bNewResult = GetShell().SelectionHasBullet();
        if (bNewResult!=bMode) {
            SfxBindings& rBindings = GetView().GetViewFrame().GetBindings();
            SfxBoolItem aItem(FN_NUM_BULLET_ON,!bNewResult);
            rBindings.SetState(aItem);
            SfxBoolItem aNewItem(FN_NUM_BULLET_ON,bNewResult);
            rBindings.SetState(aNewItem);
        }
        GetShell().EndAllAction();
    }
    break;

    case FN_NUM_BULLET_OFF:
    {
        GetShell().StartAllAction();
        SfxRequest aReq(GetView().GetViewFrame(), FN_NUM_BULLET_ON);
        aReq.AppendItem(SfxBoolItem(FN_PARAM_1, false));
        aReq.Done();
        GetShell().DelNumRules();
        GetShell().EndAllAction();
    }
    break;

    case FN_NUMBER_BULLETS:
    case SID_OUTLINE_BULLET:
    {
        SfxItemSetFixed<SID_HTML_MODE, SID_HTML_MODE,
                     SID_ATTR_NUMBERING_RULE, SID_PARAM_CUR_NUM_LEVEL>  aSet( GetPool() );
        SwDocShell* pDocSh = GetView().GetDocShell();
        const bool bHtml = dynamic_cast<SwWebDocShell*>( pDocSh  ) !=  nullptr;
        const SwNumRule* pNumRuleAtCurrentSelection = GetShell().GetNumRuleAtCurrentSelection();
        if ( pNumRuleAtCurrentSelection != nullptr )
        {
            SvxNumRule aRule = pNumRuleAtCurrentSelection->MakeSvxNumRule();

            //convert type of linked bitmaps from SVX_NUM_BITMAP to (SVX_NUM_BITMAP|LINK_TOKEN)
            for ( sal_uInt16 i = 0; i < aRule.GetLevelCount(); i++ )
            {
                SvxNumberFormat aFormat( aRule.GetLevel( i ) );
                if ( SVX_NUM_BITMAP == aFormat.GetNumberingType() )
                {
                    const SvxBrushItem* pBrush = aFormat.GetBrush();
                    if(pBrush && !pBrush->GetGraphicLink().isEmpty())
                        aFormat.SetNumberingType(SvxNumType(SVX_NUM_BITMAP|LINK_TOKEN));
                    aRule.SetLevel(i, aFormat, aRule.Get(i) != nullptr);
                }
            }
            if(bHtml)
                aRule.SetFeatureFlag(SvxNumRuleFlags::ENABLE_EMBEDDED_BMP, false);

            aSet.Put(SvxNumBulletItem(aRule));
            OSL_ENSURE( GetShell().GetNumLevel() < MAXLEVEL,
                    "<SwTextShell::ExecEnterNum()> - numbered node without valid list level. Serious defect." );
            sal_uInt16 nLevel = GetShell().GetNumLevel();
            if( nLevel < MAXLEVEL )
            {
                nLevel = 1 << nLevel;
                aSet.Put( SfxUInt16Item( SID_PARAM_CUR_NUM_LEVEL, nLevel ) );
            }
        }
        else
        {
            SwNumRule aRule( GetShell().GetUniqueNumRuleName(),
                             // #i89178#
                             numfunc::GetDefaultPositionAndSpaceMode() );
            SvxNumRule aSvxRule = aRule.MakeSvxNumRule();
            const bool bRightToLeft = GetShell().IsInRightToLeftText();

            if ( bHtml || bRightToLeft )
            {
                for ( sal_uInt8 n = 0; n < MAXLEVEL; ++n )
                {
                    SvxNumberFormat aFormat( aSvxRule.GetLevel( n ) );
                    if ( n && bHtml )
                    {
                        // 1/2" for HTML
                        aFormat.SetAbsLSpace(n * 720);
                    }
                    // #i38904#  Default alignment for
                    // numbering/bullet should be rtl in rtl paragraph:
                    if ( bRightToLeft )
                    {
                        aFormat.SetNumAdjust( SvxAdjust::Right );
                    }
                    aSvxRule.SetLevel( n, aFormat, false );
                }
                aSvxRule.SetFeatureFlag(SvxNumRuleFlags::ENABLE_EMBEDDED_BMP, false);
            }
            aSet.Put( SvxNumBulletItem( std::move(aSvxRule) ) );
        }

        aSet.Put( SfxBoolItem( SID_PARAM_NUM_PRESET,false ));

        // Before the dialogue of the HTML mode will be dropped at the Docshell.
        pDocSh->PutItem(SfxUInt16Item(SID_HTML_MODE, ::GetHtmlMode(pDocSh)));

        SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
        weld::Window *pParent = rReq.GetFrameWeld();
        VclPtr<AbstractNumBulletDialog> pDlg(pFact->CreateSvxNumBulletTabDialog(pParent, aSet, GetShell()));
        const SfxStringItem* pPageItem = rReq.GetArg<SfxStringItem>(FN_PARAM_1);
        if ( pPageItem )
            pDlg->SetCurPageId( pPageItem->GetValue() );

        auto xRequest = std::make_shared<SfxRequest>(rReq);
        rReq.Ignore(); // the 'old' request is not relevant any more

        pDlg->StartExecuteAsync([pDlg, pNumRuleAtCurrentSelection, xRequest=std::move(xRequest), this](sal_Int32 nResult){
            if (RET_OK == nResult)
            {
                const SvxNumBulletItem* pBulletItem = pDlg->GetOutputItemSet()->GetItemIfSet(SID_ATTR_NUMBERING_RULE, false);
                if (pBulletItem)
                {
                    xRequest->AppendItem(*pBulletItem);
                    xRequest->Done();
                    SvxNumRule& rSetRule = const_cast<SvxNumRule&>(pBulletItem->GetNumRule());
                    rSetRule.UnLinkGraphics();
                    SwNumRule aSetRule(pNumRuleAtCurrentSelection != nullptr
                                       ? pNumRuleAtCurrentSelection->GetName()
                                       : GetShell().GetUniqueNumRuleName(),
                        numfunc::GetDefaultPositionAndSpaceMode());
                    aSetRule.SetSvxRule(rSetRule, GetShell().GetDoc());
                    aSetRule.SetAutoRule(true);
                    // No start of new list, if an existing list style is edited.
                    // Otherwise start a new list.
                    const bool bCreateList = (pNumRuleAtCurrentSelection == nullptr);
                    GetShell().SetCurNumRule(aSetRule, bCreateList);
                }
                // If the Dialog was leaved with OK but nothing was chosen then the
                // numbering must be at least activated, if it is not already.
                else if (pNumRuleAtCurrentSelection == nullptr
                         && (pBulletItem = pDlg->GetInputItemSet()->GetItemIfSet(SID_ATTR_NUMBERING_RULE, false)))
                {
                    xRequest->AppendItem(*pBulletItem);
                    xRequest->Done();
                    const SvxNumRule& rSetRule = pBulletItem->GetNumRule();
                    SwNumRule aSetRule(
                        GetShell().GetUniqueNumRuleName(),
                        numfunc::GetDefaultPositionAndSpaceMode());
                    aSetRule.SetSvxRule(rSetRule, GetShell().GetDoc());
                    aSetRule.SetAutoRule(true);
                    // start new list
                    GetShell().SetCurNumRule(aSetRule, true);
                }
            }
            else if (RET_USER == nResult)
                GetShell().DelNumRules();
            pDlg->disposeOnce();
        });
    }
    break;

    default:
        OSL_FAIL("wrong dispatcher");
        return;
    }
}


void SwTextShell::ExecSetNumber(SfxRequest const &rReq)
{
    const sal_uInt16 nSlot = rReq.GetSlot();
    switch ( nSlot )
    {
    case FN_SVX_SET_NUMBER:
    case FN_SVX_SET_BULLET:
    case FN_SVX_SET_OUTLINE:
        {
            const SfxUInt16Item* pIndexItem = nullptr;
            const SfxStringItem* pCharItem = nullptr;
            const SfxStringItem* pFontItem = nullptr;

            // tdf#162264 check if rReq.GetArgs() is a nullptr
            if ( rReq.GetArgs() )
            {
                pIndexItem = rReq.GetArgs()->GetItem( SID_ATTR_BULLET_INDEX );
                if (!pIndexItem) // tdf#161653
                    pIndexItem = rReq.GetArg<SfxUInt16Item>(nSlot);
                pCharItem = rReq.GetArgs()->GetItem( SID_ATTR_BULLET_CHAR );
                pFontItem = rReq.GetArgs()->GetItem( SID_ATTR_BULLET_FONT );
            }

            if ( pIndexItem != nullptr || ( pCharItem != nullptr && pFontItem != nullptr ) )
            {
                svx::sidebar::NBOType nNBOType = svx::sidebar::NBOType::Bullets;
                if ( nSlot == FN_SVX_SET_NUMBER )
                    nNBOType = svx::sidebar::NBOType::Numbering;
                else if ( nSlot == FN_SVX_SET_OUTLINE )
                    nNBOType = svx::sidebar::NBOType::Outline;

                svx::sidebar::NBOTypeMgrBase* pNBOTypeMgr = svx::sidebar::NBOutlineTypeMgrFact::CreateInstance( nNBOType );

                if ( pNBOTypeMgr != nullptr )
                {
                    const SwNumRule* pNumRuleAtCurrentSelection = GetShell().GetNumRuleAtCurrentSelection();
                    sal_uInt16 nActNumLvl = USHRT_MAX;
                    if ( pNumRuleAtCurrentSelection != nullptr )
                    {
                        const sal_uInt16 nLevel = GetShell().GetNumLevel();
                        if ( nLevel < MAXLEVEL )
                        {
                            nActNumLvl = 1 << nLevel;
                        }
                    }
                    SwNumRule aNewNumRule(
                        pNumRuleAtCurrentSelection != nullptr ? pNumRuleAtCurrentSelection->GetName() : GetShell().GetUniqueNumRuleName(),
                        numfunc::GetDefaultPositionAndSpaceMode() );
                    SvxNumRule aNewSvxNumRule = pNumRuleAtCurrentSelection != nullptr
                                                    ? pNumRuleAtCurrentSelection->MakeSvxNumRule()
                                                    : aNewNumRule.MakeSvxNumRule();

                    UIName aNumCharFormat, aBulletCharFormat;
                    SwStyleNameMapper::FillUIName( RES_POOLCHR_NUM_LEVEL, aNumCharFormat );
                    SwStyleNameMapper::FillUIName( RES_POOLCHR_BULLET_LEVEL, aBulletCharFormat );

                    SfxAllItemSet aSet( GetPool() );
                    aSet.Put( SfxStringItem( SID_NUM_CHAR_FMT, aNumCharFormat.toString() ) );
                    aSet.Put( SfxStringItem( SID_BULLET_CHAR_FMT, aBulletCharFormat.toString() ) );
                    aSet.Put( SvxNumBulletItem( aNewSvxNumRule, SID_ATTR_NUMBERING_RULE ) );

                    pNBOTypeMgr->SetItems( &aSet );
                    if (pIndexItem)
                        pNBOTypeMgr->ApplyNumRule( aNewSvxNumRule, pIndexItem->GetValue() - 1, nActNumLvl );
                    else
                    {
                        svx::sidebar::BulletsTypeMgr* pBulletsTypeMgr
                            = dynamic_cast<svx::sidebar::BulletsTypeMgr*>(pNBOTypeMgr);
                        pBulletsTypeMgr->ApplyCustomRule(aNewSvxNumRule, pCharItem->GetValue(),
                                                         pFontItem->GetValue(), nActNumLvl);
                    }

                    aNewNumRule.SetSvxRule( aNewSvxNumRule, GetShell().GetDoc() );
                    aNewNumRule.SetAutoRule( true );
                    const bool bCreateNewList = ( pNumRuleAtCurrentSelection == nullptr );
                    GetShell().SetCurNumRule( aNewNumRule, bCreateNewList );
                }
            }
            else if (nSlot == FN_SVX_SET_OUTLINE)
            {
                // no outline provided: launch dialog to request a specific outline
                SfxBindings& rBindings = GetView().GetViewFrame().GetBindings();
                const SfxStringItem aPage(FN_PARAM_1, u"outlinenum"_ustr);
                const SfxPoolItem* aItems[] = { &aPage, nullptr };
                rBindings.Execute(SID_OUTLINE_BULLET, aItems);
            }
        }
        break;

    default:
        OSL_ENSURE(false, "wrong Dispatcher");
        return;
    }
}

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