/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"



// INCLUDE ---------------------------------------------------------------

#include <vcl/virdev.hxx>

#include "undobase.hxx"
#include "refundo.hxx"
#include "docsh.hxx"
#include "dbdocfun.hxx"
#include "tabvwsh.hxx"
#include "undoolk.hxx"
#include "undodraw.hxx"
#include "dbcolect.hxx"
#include "attrib.hxx"
#include "queryparam.hxx"
#include "globstr.hrc"

// STATIC DATA -----------------------------------------------------------

TYPEINIT1(ScSimpleUndo,		SfxUndoAction);
TYPEINIT1(ScBlockUndo,      ScSimpleUndo);
TYPEINIT1(ScMoveUndo,       ScSimpleUndo);
TYPEINIT1(ScDBFuncUndo,     ScSimpleUndo);
TYPEINIT1(ScUndoWrapper,    SfxUndoAction);

// -----------------------------------------------------------------------

ScSimpleUndo::ScSimpleUndo( ScDocShell* pDocSh ) :
	pDocShell( pDocSh ),
	pDetectiveUndo( NULL )
{
}

__EXPORT ScSimpleUndo::~ScSimpleUndo()
{
	delete pDetectiveUndo;
}

bool ScSimpleUndo::SetViewMarkData( const ScMarkData& rMarkData )
{
    if ( IsPaintLocked() )
        return false;

	ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
    if ( !pViewShell )
        return false;

    pViewShell->SetMarkData( rMarkData );
    return true;
}

sal_Bool __EXPORT ScSimpleUndo::Merge( SfxUndoAction *pNextAction )
{
	//	Zu jeder Undo-Action kann eine SdrUndoGroup fuer das Aktualisieren
	//	der Detektiv-Pfeile gehoeren.
	//	DetectiveRefresh kommt immer hinterher, die SdrUndoGroup ist in
	//	eine ScUndoDraw Action verpackt.
	//	Nur beim automatischen Aktualisieren wird AddUndoAction mit
	//	bTryMerg=sal_True gerufen.

	if ( !pDetectiveUndo && pNextAction->ISA(ScUndoDraw) )
	{
		//	SdrUndoAction aus der ScUndoDraw Action uebernehmen,
		//	ScUndoDraw wird dann vom UndoManager geloescht

		ScUndoDraw* pCalcUndo = (ScUndoDraw*)pNextAction;
		pDetectiveUndo = pCalcUndo->GetDrawUndo();
		pCalcUndo->ForgetDrawUndo();
		return sal_True;
	}

	return sal_False;
}

void ScSimpleUndo::BeginUndo()
{
	pDocShell->SetInUndo( sal_True );

	ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
	if (pViewShell)
		pViewShell->HideAllCursors();		// z.B. wegen zusammengefassten Zellen

	//	detective updates happened last, must be undone first
	if (pDetectiveUndo)
		pDetectiveUndo->Undo();
}

void ScSimpleUndo::EndUndo()
{
	pDocShell->SetDocumentModified();

	ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
	if (pViewShell)
	{
		pViewShell->UpdateAutoFillMark();
		pViewShell->UpdateInputHandler();
		pViewShell->ShowAllCursors();
	}

	pDocShell->SetInUndo( sal_False );
}

void ScSimpleUndo::BeginRedo()
{
	pDocShell->SetInUndo( sal_True );	//! eigenes Flag fuer Redo?

	ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
	if (pViewShell)
		pViewShell->HideAllCursors();		// z.B. wegen zusammengefassten Zellen
}

void ScSimpleUndo::EndRedo()
{
	if (pDetectiveUndo)
		pDetectiveUndo->Redo();

	pDocShell->SetDocumentModified();

	ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
	if (pViewShell)
	{
		pViewShell->UpdateAutoFillMark();
		pViewShell->UpdateInputHandler();
		pViewShell->ShowAllCursors();
	}

	pDocShell->SetInUndo( sal_False );
}

void ScSimpleUndo::ShowTable( SCTAB nTab )			// static
{
	ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
	if (pViewShell)
		pViewShell->SetTabNo( nTab );
}

void ScSimpleUndo::ShowTable( const ScRange& rRange )			// static
{
	ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
	if (pViewShell)
	{
		SCTAB nStart = rRange.aStart.Tab();
		SCTAB nEnd   = rRange.aEnd.Tab();
		SCTAB nTab = pViewShell->GetViewData()->GetTabNo();
		if ( nTab < nStart || nTab > nEnd )						// wenn nicht im Bereich:
			pViewShell->SetTabNo( nStart );						// auf erste des Bereiches
	}
}


// -----------------------------------------------------------------------

ScBlockUndo::ScBlockUndo( ScDocShell* pDocSh, const ScRange& rRange,
											ScBlockUndoMode eBlockMode ) :
	ScSimpleUndo( pDocSh ),
	aBlockRange( rRange ),
	eMode( eBlockMode )
{
	pDrawUndo = GetSdrUndoAction( pDocShell->GetDocument() );
}

__EXPORT ScBlockUndo::~ScBlockUndo()
{
	DeleteSdrUndoAction( pDrawUndo );
}

void ScBlockUndo::BeginUndo()
{
	ScSimpleUndo::BeginUndo();
	EnableDrawAdjust( pDocShell->GetDocument(), sal_False );
}

void ScBlockUndo::EndUndo()
{
	if (eMode == SC_UNDO_AUTOHEIGHT)
		AdjustHeight();

	EnableDrawAdjust( pDocShell->GetDocument(), sal_True );
    DoSdrUndoAction( pDrawUndo, pDocShell->GetDocument() );

	ShowBlock();
	ScSimpleUndo::EndUndo();
}

/*
void ScBlockUndo::BeginRedo()
{
	ScSimpleUndo::BeginRedo();
}
*/

void ScBlockUndo::EndRedo()
{
	if (eMode == SC_UNDO_AUTOHEIGHT)
		AdjustHeight();

	ShowBlock();
	ScSimpleUndo::EndRedo();
}

sal_Bool ScBlockUndo::AdjustHeight()
{
	ScDocument* pDoc = pDocShell->GetDocument();

	VirtualDevice aVirtDev;
	Fraction aZoomX( 1, 1 );
	Fraction aZoomY = aZoomX;
	double nPPTX, nPPTY;
	ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
	if (pViewShell)
	{
		ScViewData* pData = pViewShell->GetViewData();
		nPPTX = pData->GetPPTX();
		nPPTY = pData->GetPPTY();
		aZoomX = pData->GetZoomX();
		aZoomY = pData->GetZoomY();
	}
	else
	{
		//	Zoom auf 100 lassen
		nPPTX = ScGlobal::nScreenPPTX;
		nPPTY = ScGlobal::nScreenPPTY;
	}

	sal_Bool bRet = pDoc->SetOptimalHeight( aBlockRange.aStart.Row(), aBlockRange.aEnd.Row(),
/*!*/									aBlockRange.aStart.Tab(), 0, &aVirtDev,
										nPPTX, nPPTY, aZoomX, aZoomY, sal_False );

	if (bRet)
		pDocShell->PostPaint( 0,      aBlockRange.aStart.Row(), aBlockRange.aStart.Tab(),
							  MAXCOL, MAXROW,                   aBlockRange.aEnd.Tab(),
							  PAINT_GRID | PAINT_LEFT );

	return bRet;
}

void ScBlockUndo::ShowBlock()
{
    if ( IsPaintLocked() )
        return;

	ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
	if (pViewShell)
	{
		ShowTable( aBlockRange );		// bei mehreren Tabs im Range ist jede davon gut
		pViewShell->MoveCursorAbs( aBlockRange.aStart.Col(), aBlockRange.aStart.Row(),
								   SC_FOLLOW_JUMP, sal_False, sal_False );
		SCTAB nTab = pViewShell->GetViewData()->GetTabNo();
		ScRange aRange = aBlockRange;
		aRange.aStart.SetTab( nTab );
		aRange.aEnd.SetTab( nTab );
		pViewShell->MarkRange( aRange );

		//	nicht per SetMarkArea an MarkData, wegen evtl. fehlendem Paint
	}
}


// -----------------------------------------------------------------------

ScMoveUndo::ScMoveUndo( ScDocShell* pDocSh, ScDocument* pRefDoc, ScRefUndoData* pRefData,
												ScMoveUndoMode eRefMode ) :
	ScSimpleUndo( pDocSh ),
	pRefUndoDoc( pRefDoc ),
	pRefUndoData( pRefData ),
	eMode( eRefMode )
{
	ScDocument* pDoc = pDocShell->GetDocument();
	if (pRefUndoData)
		pRefUndoData->DeleteUnchanged(pDoc);
	pDrawUndo = GetSdrUndoAction( pDoc );
}

__EXPORT ScMoveUndo::~ScMoveUndo()
{
	delete pRefUndoData;
	delete pRefUndoDoc;
	DeleteSdrUndoAction( pDrawUndo );
}

void ScMoveUndo::UndoRef()
{
	ScDocument* pDoc = pDocShell->GetDocument();
	ScRange aRange(0,0,0, MAXCOL,MAXROW,pRefUndoDoc->GetTableCount()-1);
	pRefUndoDoc->CopyToDocument( aRange, IDF_FORMULA, sal_False, pDoc, NULL, sal_False );
	if (pRefUndoData)
		pRefUndoData->DoUndo( pDoc, (eMode == SC_UNDO_REFFIRST) );
		// #65055# HACK: ScDragDropUndo ist der einzige mit REFFIRST.
		// Falls nicht, resultiert daraus evtl. ein zu haeufiges Anpassen
		// der ChartRefs, nicht schoen, aber auch nicht schlecht..
}

void ScMoveUndo::BeginUndo()
{
	ScSimpleUndo::BeginUndo();

	EnableDrawAdjust( pDocShell->GetDocument(), sal_False );

	if (pRefUndoDoc && eMode == SC_UNDO_REFFIRST)
		UndoRef();
}

void ScMoveUndo::EndUndo()
{
    //@17.12.97 Reihenfolge der Fkt.s geaendert
    DoSdrUndoAction( pDrawUndo, pDocShell->GetDocument() );     // #125875# must also be called when pointer is null

	if (pRefUndoDoc && eMode == SC_UNDO_REFLAST)
		UndoRef();

	EnableDrawAdjust( pDocShell->GetDocument(), sal_True );

	ScSimpleUndo::EndUndo();
}

/*
void ScMoveUndo::BeginRedo()
{
	ScSimpleUndo::BeginRedo();
}
*/

/*
void ScMoveUndo::EndRedo()
{
	ScSimpleUndo::EndRedo();
}
*/

// -----------------------------------------------------------------------

ScDBFuncUndo::ScDBFuncUndo( ScDocShell* pDocSh, const ScRange& rOriginal, SdrUndoAction* pDrawUndo ) :
    ScSimpleUndo( pDocSh ),
    aOriginalRange( rOriginal ),
    mpDrawUndo( pDrawUndo )
{
    pAutoDBRange = pDocSh->GetOldAutoDBRange();
}

ScDBFuncUndo::~ScDBFuncUndo()
{
    DeleteSdrUndoAction( mpDrawUndo );
    delete pAutoDBRange;
}

void ScDBFuncUndo::SetDrawUndoAction( SdrUndoAction* pDrawUndo )
{
    DeleteSdrUndoAction( mpDrawUndo );
    mpDrawUndo = pDrawUndo;
}

void ScDBFuncUndo::BeginUndo()
{
    ScSimpleUndo::BeginUndo();
    DoSdrUndoAction( mpDrawUndo, pDocShell->GetDocument() );
}

void ScDBFuncUndo::EndUndo()
{
    ScSimpleUndo::EndUndo();

    if ( pAutoDBRange )
    {
        sal_uInt16 nNoNameIndex;
        ScDocument* pDoc = pDocShell->GetDocument();
        ScDBCollection* pColl = pDoc->GetDBCollection();
        if ( pColl->SearchName( pAutoDBRange->GetName(), nNoNameIndex ) )
        {
            ScDBData* pNoNameData = (*pColl)[nNoNameIndex];

            SCCOL nRangeX1;
            SCROW nRangeY1;
            SCCOL nRangeX2;
            SCROW nRangeY2;
            SCTAB nRangeTab;
            pNoNameData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
            pDocShell->DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );

            *pNoNameData = *pAutoDBRange;
            /*if (pAutoDBRange->HasQueryParam())   //maybe conflict with AOO
            {
                ScQueryParam    aParam;
                pAutoDBRange->GetQueryParam(aParam);
                ScDBDocFunc aDBDocFunc( *pDocShell );
                aDBDocFunc.Query( nRangeTab, aParam, NULL, sal_False, sal_False );
            }*/

            if ( pAutoDBRange->HasAutoFilter() )
            {
                // restore AutoFilter buttons
                pAutoDBRange->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
                pDoc->ApplyFlagsTab( nRangeX1, nRangeY1, nRangeX2, nRangeY1, nRangeTab, SC_MF_AUTO );
                pDocShell->PostPaint( nRangeX1, nRangeY1, nRangeTab, nRangeX2, nRangeY1, nRangeTab, PAINT_GRID );
            }
        }
    }
}

void ScDBFuncUndo::BeginRedo()
{
    RedoSdrUndoAction( mpDrawUndo );
    if ( pAutoDBRange )
    {
        // move the database range to this function's position again (see ScDocShell::GetDBData)

        sal_uInt16 nNoNameIndex;
        ScDocument* pDoc = pDocShell->GetDocument();
        ScDBCollection* pColl = pDoc->GetDBCollection();
        if ( pColl->SearchName( pAutoDBRange->GetName(), nNoNameIndex ) )
        {
            ScDBData* pNoNameData = (*pColl)[nNoNameIndex];

            SCCOL nRangeX1;
            SCROW nRangeY1;
            SCCOL nRangeX2;
            SCROW nRangeY2;
            SCTAB nRangeTab;
            pNoNameData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );
            /*if (pAutoDBRange->HasQueryParam())
            {
                ScQueryParam    aParam;
                pAutoDBRange->GetQueryParam(aParam);
                SCSIZE nEC = aParam.GetEntryCount();
                for (SCSIZE i=0; i<nEC; i++)
                    aParam.GetEntry(i).bDoQuery = sal_False;
                aParam.bDuplicate = sal_True;
                ScDBDocFunc aDBDocFunc( *pDocShell );
                aDBDocFunc.Query( nRangeTab, aParam, NULL, sal_False, sal_False );
            }*/
            pDocShell->DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 );

            pNoNameData->SetSortParam( ScSortParam() );
            pNoNameData->SetQueryParam( ScQueryParam() );
            pNoNameData->SetSubTotalParam( ScSubTotalParam() );

            pNoNameData->SetArea( aOriginalRange.aStart.Tab(),
                                  aOriginalRange.aStart.Col(), aOriginalRange.aStart.Row(),
                                  aOriginalRange.aEnd.Col(), aOriginalRange.aEnd.Row() );

            pNoNameData->SetByRow( sal_True );
            pNoNameData->SetAutoFilter( sal_False );
            // header is always set with the operation in redo
        }
    }

    ScSimpleUndo::BeginRedo();
}

void ScDBFuncUndo::EndRedo()
{
    ScSimpleUndo::EndRedo();
}

// -----------------------------------------------------------------------

ScUndoWrapper::ScUndoWrapper( SfxUndoAction* pUndo ) :
    pWrappedUndo( pUndo )
{
}

ScUndoWrapper::~ScUndoWrapper()
{
    delete pWrappedUndo;
}

void ScUndoWrapper::ForgetWrappedUndo()
{
    pWrappedUndo = NULL;    // don't delete in dtor - pointer must be stored outside
}

String ScUndoWrapper::GetComment() const
{
    if (pWrappedUndo)
        return pWrappedUndo->GetComment();
    else
        return String();
}

String ScUndoWrapper::GetRepeatComment(SfxRepeatTarget& rTarget) const
{
    if (pWrappedUndo)
        return pWrappedUndo->GetRepeatComment(rTarget);
    else
        return String();
}

sal_uInt16 ScUndoWrapper::GetId() const
{
    if (pWrappedUndo)
        return pWrappedUndo->GetId();
    else
        return 0;
}

void ScUndoWrapper::SetLinkToSfxLinkUndoAction(SfxLinkUndoAction* pSfxLinkUndoAction)
{
    if (pWrappedUndo)
        pWrappedUndo->SetLinkToSfxLinkUndoAction(pSfxLinkUndoAction);
    else
        SetLinkToSfxLinkUndoAction(pSfxLinkUndoAction);
}

sal_Bool ScUndoWrapper::Merge( SfxUndoAction* pNextAction )
{
    if (pWrappedUndo)
        return pWrappedUndo->Merge(pNextAction);
    else
        return sal_False;
}

void ScUndoWrapper::Undo()
{
    if (pWrappedUndo)
        pWrappedUndo->Undo();
}

void ScUndoWrapper::Redo()
{
    if (pWrappedUndo)
        pWrappedUndo->Redo();
}

void ScUndoWrapper::Repeat(SfxRepeatTarget& rTarget)
{
    if (pWrappedUndo)
        pWrappedUndo->Repeat(rTarget);
}

sal_Bool ScUndoWrapper::CanRepeat(SfxRepeatTarget& rTarget) const
{
    if (pWrappedUndo)
        return pWrappedUndo->CanRepeat(rTarget);
    else
        return sal_False;
}


