/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <config_features.h>

#include <ViewShell.hxx>
#include <ViewShellBase.hxx>

#include <sfx2/viewfrm.hxx>
#include <svtools/strings.hrc>
#include <svtools/svtresid.hxx>

#include <app.hrc>
#include <strings.hrc>

#include <sal/log.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/bindings.hxx>
#include <svx/svdundo.hxx>
#include <svl/intitem.hxx>
#include <svl/style.hxx>
#include <svl/stritem.hxx>
#include <stlsheet.hxx>
#include <DrawViewShell.hxx>

#include <drawdoc.hxx>
#include <sdpage.hxx>
#include <DrawDocShell.hxx>
#include <sdresid.hxx>
#include <unokywds.hxx>

#include <svx/svxids.hrc>
#include <sfx2/request.hxx>
#include <basic/sbstar.hxx>
#include <basic/sberrors.hxx>
#include <xmloff/autolayout.hxx>

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

namespace sd {

/**
 * set state (enabled/disabled) of Menu SfxSlots
 */
void  ViewShell::GetMenuState( SfxItemSet &rSet )
{
    if( SfxItemState::DEFAULT == rSet.GetItemState( SID_STYLE_FAMILY ) )
    {
        SfxStyleFamily const nFamily = GetDocSh()->GetStyleFamily();

        SdrView* pDrView = GetDrawView();

        const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
        if( rMarkList.GetMarkCount() != 0 )
        {
            SfxStyleSheet* pStyleSheet = pDrView->GetStyleSheet();
            if( pStyleSheet )
            {
                if (pStyleSheet->GetFamily() == SfxStyleFamily::Page)
                    pStyleSheet = static_cast<SdStyleSheet*>(pStyleSheet)->GetPseudoStyleSheet();

                if( pStyleSheet )
                {
                    GetDocSh()->SetStyleFamily(pStyleSheet->GetFamily());
                }
            }
        }

        rSet.Put(SfxUInt16Item(SID_STYLE_FAMILY, static_cast<sal_uInt16>(nFamily)));
    }

    if(SfxItemState::DEFAULT == rSet.GetItemState(SID_GETUNDOSTRINGS))
    {
        ImpGetUndoStrings(rSet);
    }

    if(SfxItemState::DEFAULT == rSet.GetItemState(SID_GETREDOSTRINGS))
    {
        ImpGetRedoStrings(rSet);
    }

    if(SfxItemState::DEFAULT == rSet.GetItemState(SID_UNDO))
    {
        SfxUndoManager* pUndoManager = ImpGetUndoManager();
        if(pUndoManager)
        {
            if(pUndoManager->GetUndoActionCount() != 0)
            {
                // If another view created the first undo action, prevent redoing it from this view.
                const SfxUndoAction* pAction = pUndoManager->GetUndoAction();
                if (pAction->GetViewShellId() != GetViewShellBase().GetViewShellId())
                {
                    rSet.Put(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
                }
                else
                {
                    // Set the necessary string like in
                    // sfx2/source/view/viewfrm.cxx ver 1.23 ln 1072 ff.
                    OUString aTmp = SvtResId(STR_UNDO) +
                        pUndoManager->GetUndoActionComment();
                    rSet.Put(SfxStringItem(SID_UNDO, aTmp));
                }
            }
            else
            {
                rSet.DisableItem(SID_UNDO);
            }
        }
    }

    if(SfxItemState::DEFAULT != rSet.GetItemState(SID_REDO))
        return;

    SfxUndoManager* pUndoManager = ImpGetUndoManager();
    if(!pUndoManager)
        return;

    if(pUndoManager->GetRedoActionCount() != 0)
    {
        // If another view created the first undo action, prevent redoing it from this view.
        const SfxUndoAction* pAction = pUndoManager->GetRedoAction();
        if (pAction->GetViewShellId() != GetViewShellBase().GetViewShellId())
        {
            rSet.Put(SfxUInt32Item(SID_REDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE)));
        }
        else
        {
            // Set the necessary string like in
            // sfx2/source/view/viewfrm.cxx ver 1.23 ln 1081 ff.
            OUString aTmp = SvtResId(STR_REDO) + pUndoManager->GetRedoActionComment();
            rSet.Put(SfxStringItem(SID_REDO, aTmp));
        }
    }
    else
    {
        rSet.DisableItem(SID_REDO);
    }
}

/** This method consists basically of three parts:
    1. Process the arguments of the SFX request.
    2. Use the model to create a new page or duplicate an existing one.
    3. Update the tab control and switch to the new page.
*/
SdPage* ViewShell::CreateOrDuplicatePage (
    SfxRequest& rRequest,
    PageKind ePageKind,
    SdPage* pPage,
    const sal_Int32 nInsertPosition)
{
    sal_uInt16 nSId = rRequest.GetSlot();
    SdDrawDocument* pDocument = GetDoc();
    SdrLayerAdmin& rLayerAdmin = pDocument->GetLayerAdmin();
    SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background);
    SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects);
    SdrLayerIDSet aVisibleLayers;
    // Determine the page from which to copy some values, such as layers,
    // size, master page, to the new page.  This is usually the given page.
    // When the given page is NULL then use the first page of the document.
    SdPage* pTemplatePage = pPage;
    if (pTemplatePage == nullptr)
        pTemplatePage = pDocument->GetSdPage(0, ePageKind);
    if (pTemplatePage != nullptr && pTemplatePage->TRG_HasMasterPage())
        aVisibleLayers = pTemplatePage->TRG_GetMasterPageVisibleLayers();
    else
        aVisibleLayers.SetAll();

    OUString aStandardPageName;
    OUString aNotesPageName;
    AutoLayout eStandardLayout (AUTOLAYOUT_NONE);
    AutoLayout eNotesLayout (AUTOLAYOUT_NOTES);
    bool bIsPageBack = aVisibleLayers.IsSet(aBckgrnd);
    bool bIsPageObj = aVisibleLayers.IsSet(aBckgrndObj);

    // 1. Process the arguments.
    const SfxItemSet* pArgs = rRequest.GetArgs();
    const SfxUInt16Item* pInsertPos = rRequest.GetArg<SfxUInt16Item>(ID_INSERT_POS);

    if (! pArgs || (pArgs->Count() == 1 && pInsertPos))
    {
        // AutoLayouts must be ready
        pDocument->StopWorkStartupDelay();

        // Use the layouts of the previous page and notes page as template.
        if (pTemplatePage != nullptr)
        {
            eStandardLayout = pTemplatePage->GetAutoLayout();
            if( eStandardLayout == AUTOLAYOUT_TITLE )
                eStandardLayout = AUTOLAYOUT_TITLE_CONTENT;

            SdPage* pNotesTemplatePage = static_cast<SdPage*>(pDocument->GetPage(pTemplatePage->GetPageNum()+1));
            if (pNotesTemplatePage != nullptr)
                eNotesLayout = pNotesTemplatePage->GetAutoLayout();
        }
    }
    else if (pArgs->Count() == 1 || pArgs->Count() == 2)
    {
        pDocument->StopWorkStartupDelay();
        const SfxUInt32Item* pLayout = rRequest.GetArg(ID_VAL_WHATLAYOUT);
        if( pLayout )
        {
            if (ePageKind == PageKind::Notes)
            {
                eNotesLayout   = static_cast<AutoLayout>(pLayout->GetValue ());
            }
            else
            {
                eStandardLayout   = static_cast<AutoLayout>(pLayout->GetValue ());
            }
        }
    }
    else if (pArgs->Count() == 4 || pArgs->Count() == 5)
    {
        // AutoLayouts must be ready
        pDocument->StopWorkStartupDelay();

        const SfxStringItem* pPageName = rRequest.GetArg(ID_VAL_PAGENAME);
        const SfxUInt32Item* pLayout = rRequest.GetArg(ID_VAL_WHATLAYOUT);
        const SfxBoolItem* pIsPageBack = rRequest.GetArg<SfxBoolItem>(ID_VAL_ISPAGEBACK);
        const SfxBoolItem* pIsPageObj = rRequest.GetArg<SfxBoolItem>(ID_VAL_ISPAGEOBJ);
        assert(pPageName && pLayout && pIsPageBack && pIsPageObj && "must be present");

        if (CHECK_RANGE (AUTOLAYOUT_START, static_cast<AutoLayout>(pLayout->GetValue ()), AUTOLAYOUT_END))
        {
            if (ePageKind == PageKind::Notes)
            {
                aNotesPageName = pPageName->GetValue ();
                eNotesLayout   = static_cast<AutoLayout>(pLayout->GetValue ());
            }
            else
            {
                aStandardPageName = pPageName->GetValue ();
                eStandardLayout   = static_cast<AutoLayout>(pLayout->GetValue ());
            }

            bIsPageBack = pIsPageBack->GetValue ();
            bIsPageObj  = pIsPageObj->GetValue ();
        }
        else
        {
            Cancel();

            if(HasCurrentFunction( SID_BEZIER_EDIT ) )
                GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON);
#if HAVE_FEATURE_SCRIPTING
            StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE);
#endif
            rRequest.Ignore ();
            return nullptr;
        }
    }
    else
    {
        Cancel();

        if(HasCurrentFunction(SID_BEZIER_EDIT) )
            GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON);
#if HAVE_FEATURE_SCRIPTING
        StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS);
#endif
        rRequest.Ignore ();
        return nullptr;
    }

    // 2. Create a new page or duplicate an existing one.
    View* pDrView = GetView();
    const bool bUndo = pDrView && pDrView->IsUndoEnabled();
    if( bUndo && GetDoc()->GetDocumentType() == DocumentType::Draw)
        pDrView->BegUndo(SdResId(STR_INSERT_PAGE_DRAW));
    else if (bUndo)
        pDrView->BegUndo(SdResId(STR_INSERTPAGE));



    sal_uInt16 nNewPageIndex = 0xffff;
    switch (nSId)
    {
        case SID_INSERTPAGE:
        case SID_INSERTPAGE_QUICK:
        case SID_INSERT_MASTER_PAGE:
            // There are three cases.  a) pPage is not NULL: we use it as a
            // template and create a new slide behind it. b) pPage is NULL
            // but the document is not empty: we use the first slide/notes
            // page as template, create a new slide after it and move it
            // then to the head of the document. c) pPage is NULL and the
            // document is empty: We use CreateFirstPages to create the
            // first page of the document.
            if (pPage == nullptr)
                if (pTemplatePage == nullptr)
                {
                    pDocument->CreateFirstPages();
                    nNewPageIndex = 0;
                }
                else
                {
                    // Create a new page with the first page as template and
                    // insert it after the first page.
                    nNewPageIndex = pDocument->CreatePage (
                        pTemplatePage,
                        ePageKind,
                        aStandardPageName,
                        aNotesPageName,
                        eStandardLayout,
                        eNotesLayout,
                        bIsPageBack,
                        bIsPageObj,
                        nInsertPosition);
                    // Select exactly the new page.
                    sal_uInt16 nPageCount (pDocument->GetSdPageCount(ePageKind));
                    for (sal_uInt16 i=0; i<nPageCount; i++)
                    {
                        pDocument->GetSdPage(i, PageKind::Standard)->SetSelected(
                            i == nNewPageIndex);
                        pDocument->GetSdPage(i, PageKind::Notes)->SetSelected(
                            i == nNewPageIndex);
                    }
                    // Move the selected page to the head of the document
                    pDocument->MoveSelectedPages (sal_uInt16(-1));
                    nNewPageIndex = 0;
                }
            else
                nNewPageIndex = pDocument->CreatePage (
                    pPage,
                    ePageKind,
                    aStandardPageName,
                    aNotesPageName,
                    eStandardLayout,
                    eNotesLayout,
                    bIsPageBack,
                    bIsPageObj,
                    pInsertPos ? (pInsertPos->GetValue()*2)+1 : nInsertPosition);
            break;

        case SID_DUPLICATE_PAGE:
            // Duplication makes no sense when pPage is NULL.
            if (pPage != nullptr)
                nNewPageIndex = pDocument->DuplicatePage (
                    pPage,
                    ePageKind,
                    aStandardPageName,
                    aNotesPageName,
                    bIsPageBack,
                    bIsPageObj,
                    pInsertPos ? (pInsertPos->GetValue()*2)+1 : nInsertPosition);
            break;

        default:
            SAL_INFO("sd", "wrong slot id given to CreateOrDuplicatePage");
            // Try to handle another slot id gracefully.
    }
    SdPage* pNewPage = nullptr;
    if(nNewPageIndex != 0xffff)
        pNewPage = pDocument->GetSdPage(nNewPageIndex, PageKind::Standard);

    if( bUndo )
    {
        if( pNewPage )
        {
            pDrView->AddUndo(pDocument->GetSdrUndoFactory().CreateUndoNewPage(*pNewPage));
            pDrView->AddUndo(pDocument->GetSdrUndoFactory().CreateUndoNewPage(*pDocument->GetSdPage (nNewPageIndex, PageKind::Notes)));
        }

        pDrView->EndUndo();
    }

    return pNewPage;
}

} // end of namespace sd

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