/* -*- 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 <svl/hint.hxx>
#include <comphelper/lok.hxx>
#include <svx/numfmtsh.hxx>
#include <svx/numinf.hxx>
#include <svx/svxids.hrc>
#include <sfx2/dispatch.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/viewfrm.hxx>
#include <osl/diagnose.h>

#include <tabvwsh.hxx>
#include <global.hxx>
#include <docsh.hxx>
#include <document.hxx>
#include <formulacell.hxx>
#include <scmod.hxx>
#include <uiitems.hxx>
#include <hints.hxx>
#include <cellvalue.hxx>
#include <svl/numformat.hxx>
#include <svl/sharedstring.hxx>

void ScTabViewShell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
    if (rHint.GetId() == SfxHintId::ScPaint) // draw new
    {
        const ScPaintHint* pPaintHint = static_cast<const ScPaintHint*>(&rHint);
        PaintPartFlags nParts = pPaintHint->GetParts();
        SCTAB nTab = GetViewData().CurrentTabForData();
        if (pPaintHint->GetStartTab() <= nTab && pPaintHint->GetEndTab() >= nTab)
        {
            if (nParts & PaintPartFlags::Extras)          // first if table vanished !!!
                if (PaintExtras())
                    nParts = PaintPartFlags::All;

            // if the current sheet has pending row height updates (sheet links refreshed),
            // execute them before invalidating the window
            GetViewData().GetDocShell()->UpdatePendingRowHeights( GetViewData().CurrentTabForData() );

            if (nParts & PaintPartFlags::Size)
                RepeatResize();                     //! InvalidateBorder ???
            const tools::Long nWidthAffectedHint = pPaintHint->GetMaxWidthAffectedHint();
            if (nParts & PaintPartFlags::Grid)
                PaintArea( pPaintHint->GetStartCol(), pPaintHint->GetStartRow(),
                           pPaintHint->GetEndCol(), pPaintHint->GetEndRow(),
                           ScUpdateMode::All, nWidthAffectedHint );
            if (nParts & PaintPartFlags::Marks)
                PaintArea( pPaintHint->GetStartCol(), pPaintHint->GetStartRow(),
                           pPaintHint->GetEndCol(), pPaintHint->GetEndRow(),
                           ScUpdateMode::Marks, nWidthAffectedHint );
            if (nParts & PaintPartFlags::Left)
                PaintLeftArea( pPaintHint->GetStartRow(), pPaintHint->GetEndRow() );
            if (nParts & PaintPartFlags::Top)
                PaintTopArea( pPaintHint->GetStartCol(), pPaintHint->GetEndCol() );

            // #i84689# call UpdateAllOverlays here instead of in ScTabView::PaintArea
            if (nParts & ( PaintPartFlags::Left | PaintPartFlags::Top ))    // only if widths or heights changed
                UpdateAllOverlays();

            HideNoteOverlay();
        }
    }
    else if (rHint.GetId() == SfxHintId::ScEditView) // create Edit-View
    {
        //  ScEditViewHint is only received at active view
        auto pEditViewHint = static_cast<const ScEditViewHint*>(&rHint);

        SCTAB nTab = GetViewData().CurrentTabForData();
        if ( pEditViewHint->GetTab() == nTab )
        {
            SCCOL nCol = pEditViewHint->GetCol();
            SCROW nRow = pEditViewHint->GetRow();
            {
                HideNoteOverlay();

                MakeEditView( pEditViewHint->GetEngine(), nCol, nRow );

                StopEditShell();                    // shouldn't be set

                ScSplitPos eActive = GetViewData().GetActivePart();
                if ( GetViewData().HasEditView(eActive) )
                {
                    //  MakeEditView will fail, if the cursor is outside the screen.
                    //  Then GetEditView will return a none-active view, therefore
                    //  calling HasEditView.

                    EditView* pView = GetViewData().GetEditView(eActive);  // isn't zero

                    SetEditShell(pView, true);
                }
            }
        }
    }
    else if (rHint.GetId() == SfxHintId::ScTables) // table insert / deleted
    {
        auto pTablesHint = static_cast<const ScTablesHint*>(&rHint);
        // first fetch current table (can be changed during DeleteTab on ViewData)
        SCTAB nActiveTab = GetViewData().CurrentTabForData();

        SCTAB nTab1 = pTablesHint->GetTab1();
        SCTAB nTab2 = pTablesHint->GetTab2();
        sal_uInt16 nId  = pTablesHint->GetTablesHintId();
        switch (nId)
        {
            case SC_TAB_INSERTED:
                GetViewData().InsertTab( nTab1 );
                break;
            case SC_TAB_DELETED:
                GetViewData().DeleteTab( nTab1 );
                break;
            case SC_TAB_MOVED:
                GetViewData().MoveTab( nTab1, nTab2 );
                break;
            case SC_TAB_COPIED:
                GetViewData().CopyTab( nTab1, nTab2 );
                break;
            case SC_TAB_HIDDEN:
                break;
            case SC_TABS_INSERTED:
                GetViewData().InsertTabs( nTab1, nTab2 );
                break;
            case SC_TABS_DELETED:
                GetViewData().DeleteTabs( nTab1, nTab2 );
                break;
            default:
                OSL_FAIL("unknown ScTablesHint");
        }

        //  No calling of IsActive() here, because the actions can be coming from Basic
        //  and then also the active view has to be switched.

        SCTAB nNewTab = nActiveTab;
        bool bStayOnActiveTab = true;
        switch (nId)
        {
            case SC_TAB_INSERTED:
                if ( nTab1 <= nNewTab )             // insert before
                    ++nNewTab;
                break;
            case SC_TAB_DELETED:
                if ( nTab1 < nNewTab )              // deleted before
                    --nNewTab;
                else if ( nTab1 == nNewTab )        // deleted current
                    bStayOnActiveTab = false;
                break;
            case SC_TAB_MOVED:
                if ( nNewTab == nTab1 )             // moved table
                    nNewTab = nTab2;
                else if ( nTab1 < nTab2 )           // moved back
                {
                    if ( nNewTab > nTab1 && nNewTab <= nTab2 )      // succeeding area
                        --nNewTab;
                }
                else                                // move in front
                {
                    if ( nNewTab >= nTab2 && nNewTab < nTab1 )      // succeeding area
                        ++nNewTab;
                }
                break;
            case SC_TAB_COPIED:
                if ( nNewTab >= nTab2 )             // insert before
                    ++nNewTab;
                break;
            case SC_TAB_HIDDEN:
                if ( nTab1 == nNewTab )             // current is hidden
                    bStayOnActiveTab = false;
                break;
            case SC_TABS_INSERTED:
                if ( nTab1 <= nNewTab )
                    nNewTab += nTab2;
                break;
            case SC_TABS_DELETED:
                if ( nTab1 < nNewTab )
                    nNewTab -= nTab2;
                break;
        }

        ScDocument& rDoc = GetViewData().GetDocument();
        if ( nNewTab >= rDoc.GetTableCount() )
            nNewTab = rDoc.GetTableCount() - 1;

        bool bForce = !bStayOnActiveTab;
        SetTabNo( nNewTab, bForce, false, bStayOnActiveTab );
    }
    else if (const ScIndexHint* pIndexHint = dynamic_cast<const ScIndexHint*>(&rHint))
    {
        SfxHintId nId = pIndexHint->GetId();
        sal_uInt16 nIndex = pIndexHint->GetIndex();
        switch (nId)
        {
            case SfxHintId::ScShowRangeFinder:
                PaintRangeFinder( nIndex );
                break;
            default: break;
        }
    }
    else                       // without parameter
    {
        const SfxHintId nSlot = rHint.GetId();
        switch ( nSlot )
        {
            case SfxHintId::ScDataChanged:
                UpdateFormulas();
                break;

            case SfxHintId::ScRefModeChanged:
                {
                    bool bRefMode = ScModule::get()->IsFormulaMode();
                    if (!bRefMode)
                        StopRefMode();
                    else
                        GetSelEngine()->Reset();
                }
                break;

            case SfxHintId::ScKillEditView:
            case SfxHintId::ScKillEditViewNoPaint:
                if (!comphelper::LibreOfficeKit::isActive()
                    || this == SfxViewShell::Current()
                    || bInPrepareClose
                    || bInDispose)
                {
                    StopEditShell();
                    KillEditView( nSlot == SfxHintId::ScKillEditViewNoPaint );
                }
                break;

            case SfxHintId::DocChanged:
                {
                    ScDocument& rDoc = GetViewData().GetDocument();
                    if (!rDoc.HasTable( GetViewData().CurrentTabForData() ))
                    {
                        SetTabNo(0);
                    }
                }
                break;

            case SfxHintId::ScDrawLayerNew:
                MakeDrawView(TRISTATE_INDET);
                break;

            case SfxHintId::ScDocSaved:
                {
                    //  "Save as" can make a write-protected document writable,
                    //  therefore the Layer-Locks anew (#39884#)
                    //  (Invalidate etc. is happening already from Sfx)
                    //  by SID_EDITDOC no SfxHintId::TitleChanged will occur, that
                    //  is why the own hint from DoSaveCompleted
                    //! what is with SfxHintId::SAVECOMPLETED ?

                    UpdateLayerLocks();

                    //  Would be too much to change Design-Mode with every save
                    //  (when saving under the name, it should remain unchanged)
                    //  Therefore only by SfxHintId::ModeChanged (from ViewFrame)
                }
                break;

            case SfxHintId::ModeChanged:
                //  Since you can no longer rely on it where this hint was coming
                //  from, always switch the design mode when the ReadOnly state
                //  really was changed:

                if ( GetViewData().GetSfxDocShell()->IsReadOnly() != bReadOnly )
                {
                    bReadOnly = GetViewData().GetSfxDocShell()->IsReadOnly();

                    SfxBoolItem aItem( SID_FM_DESIGN_MODE, !bReadOnly);
                    GetViewData().GetDispatcher().ExecuteList(SID_FM_DESIGN_MODE,
                            SfxCallMode::ASYNCHRON, { &aItem });

                    UpdateInputContext();
                }
                break;

            case SfxHintId::ScShowRangeFinder:
                PaintRangeFinder(-1);
                break;

            case SfxHintId::ScForceSetTab:
                SetTabNo(GetViewData().GetTabNumber(), true);
                break;

            case SfxHintId::LanguageChanged:
            {
                GetViewFrame().GetBindings().Invalidate(SID_LANGUAGE_STATUS);
                if ( ScGridWindow* pWin = GetViewData().GetActiveWin() )
                    pWin->ResetAutoSpell();
            }
                break;

            default:
                break;
        }
    }

    SfxViewShell::Notify( rBC, rHint );
}

std::unique_ptr<SvxNumberInfoItem> ScTabViewShell::MakeNumberInfoItem( ScDocument& rDoc, const ScViewData& rViewData )
{

    // construct NumberInfo item

    SvxNumberValueType  eValType        = SvxNumberValueType::Undefined;
    double              nCellValue      = 0;
    OUString aCellString;

    ScRefCellValue aCell(rDoc, rViewData.GetCurPos());

    switch (aCell.getType())
    {
        case CELLTYPE_VALUE:
        {
            nCellValue = aCell.getDouble();
            eValType = SvxNumberValueType::Number;
        }
        break;

        case CELLTYPE_STRING:
        {
            aCellString = aCell.getSharedString()->getString();
            eValType = SvxNumberValueType::String;
        }
        break;

        case CELLTYPE_FORMULA:
        {
            if (aCell.getFormula()->IsValue())
            {
                nCellValue = aCell.getFormula()->GetValue();
                eValType = SvxNumberValueType::Number;
            }
            else
            {
                nCellValue = 0;
                eValType   = SvxNumberValueType::Undefined;
            }
        }
        break;

        default:
            nCellValue = 0;
            eValType   = SvxNumberValueType::Undefined;
    }

    switch ( eValType )
    {
        case SvxNumberValueType::String:
            return std::make_unique<SvxNumberInfoItem>(
                                rDoc.GetFormatTable(),
                                aCellString,
                                SID_ATTR_NUMBERFORMAT_INFO );

        case SvxNumberValueType::Number:
            return std::make_unique<SvxNumberInfoItem>(
                                rDoc.GetFormatTable(),
                                nCellValue,
                                SID_ATTR_NUMBERFORMAT_INFO );

        case SvxNumberValueType::Undefined:
        default:
            ;
    }

    return std::make_unique<SvxNumberInfoItem>(
        rDoc.GetFormatTable(), SID_ATTR_NUMBERFORMAT_INFO);
}

void ScTabViewShell::UpdateNumberFormatter(
                        const SvxNumberInfoItem& rInfoItem )
{
    for ( sal_uInt32 key : rInfoItem.GetDelFormats() )
        rInfoItem.GetNumberFormatter()->DeleteEntry( key );
}

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