/**************************************************************
 * 
 * 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 <com/sun/star/embed/NoVisualAreaSizeException.hpp>



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

#include <svx/svditer.hxx>
#include <svx/svdograf.hxx>
#include <svx/svdoole2.hxx>
#include <svx/svdouno.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svdundo.hxx>
#include <svx/xbtmpit.hxx>
#include <svx/xoutbmp.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/viewfrm.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <com/sun/star/embed/Aspects.hpp>

#include "document.hxx"		// fuer MapMode Initialisierung in PasteDraw
#include "viewfunc.hxx"
#include "tabvwsh.hxx"
#include "drawview.hxx"
#include "scmod.hxx"
#include "drwlayer.hxx"
#include "drwtrans.hxx"
#include "globstr.hrc"
#include "chartlis.hxx"
#include "docuno.hxx"
#include "docsh.hxx"
#include "convuno.hxx"

extern Point aDragStartDiff;

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

sal_Bool bPasteIsMove = sal_False;

using namespace com::sun::star;

//==================================================================

void lcl_AdjustInsertPos( ScViewData* pData, Point& rPos, Size& rSize )
{
//	SdrPage* pPage = pData->GetDocument()->GetDrawLayer()->GetPage( pData->GetTabNo() );
	SdrPage* pPage = pData->GetScDrawView()->GetModel()->GetPage( static_cast<sal_uInt16>(pData->GetTabNo()) );
	DBG_ASSERT(pPage,"pPage ???");
	Size aPgSize( pPage->GetSize() );
	if (aPgSize.Width() < 0)
		aPgSize.Width() = -aPgSize.Width();
	long x = aPgSize.Width() - rPos.X() - rSize.Width();
	long y = aPgSize.Height() - rPos.Y() - rSize.Height();
	// ggf. Ajustments (80/200) fuer Pixel-Rundungsfehler
	if( x < 0 )
		rPos.X() += x + 80;
	if( y < 0 )
		rPos.Y() += y + 200;
	rPos.X() += rSize.Width() / 2;			// Position bei Paste gibt Mittelpunkt an
	rPos.Y() += rSize.Height() / 2;
}

void ScViewFunc::PasteDraw( const Point& rLogicPos, SdrModel* pModel,
        sal_Bool bGroup, sal_Bool bSameDocClipboard )
{
	MakeDrawLayer();
	Point aPos( rLogicPos );

	//	#64184# MapMode am Outliner-RefDevice muss stimmen (wie in FuText::MakeOutliner)
	//!	mit FuText::MakeOutliner zusammenfassen?
	MapMode aOldMapMode;
	OutputDevice* pRef = GetViewData()->GetDocument()->GetDrawLayer()->GetRefDevice();
	if (pRef)
	{
		aOldMapMode = pRef->GetMapMode();
		pRef->SetMapMode( MapMode(MAP_100TH_MM) );
	}

	sal_Bool bNegativePage = GetViewData()->GetDocument()->IsNegativePage( GetViewData()->GetTabNo() );

	SdrView* pDragEditView = NULL;
	ScModule* pScMod = SC_MOD();
	const ScDragData& rData = pScMod->GetDragData();
	ScDrawTransferObj* pDrawTrans = rData.pDrawTransfer;
	if (pDrawTrans)
	{
		pDragEditView = pDrawTrans->GetDragSourceView();

		aPos -= aDragStartDiff;
		if ( bNegativePage )
		{
			if (aPos.X() > 0) aPos.X() = 0;
		}
		else
		{
			if (aPos.X() < 0) aPos.X() = 0;
		}
		if (aPos.Y() < 0) aPos.Y() = 0;
	}

	ScDrawView* pScDrawView = GetScDrawView();
	if (bGroup)
		pScDrawView->BegUndo( ScGlobal::GetRscString( STR_UNDO_PASTE ) );

	sal_Bool bSameDoc = ( pDragEditView && pDragEditView->GetModel() == pScDrawView->GetModel() );
	if (bSameDoc)
	{
			// lokal kopieren - incl. Charts

		Point aSourceStart = pDragEditView->GetAllMarkedRect().TopLeft();
		long nDiffX = aPos.X() - aSourceStart.X();
		long nDiffY = aPos.Y() - aSourceStart.Y();

			// innerhalb einer Page verschieben?

		if ( bPasteIsMove &&
				pScDrawView->GetSdrPageView()->GetPage() ==
				pDragEditView->GetSdrPageView()->GetPage() )
		{
			if ( nDiffX != 0 || nDiffY != 0 )
				pDragEditView->MoveAllMarked(Size(nDiffX,nDiffY), sal_False);
		}
		else
		{
			SdrModel* pDrawModel = pDragEditView->GetModel();
            SCTAB nTab = GetViewData()->GetTabNo();
            SdrPage* pDestPage = pDrawModel->GetPage( static_cast< sal_uInt16 >( nTab ) );
			DBG_ASSERT(pDestPage,"nanu, Page?");

            ::std::vector< ::rtl::OUString > aExcludedChartNames;
            if ( pDestPage )
            {
                ScChartHelper::GetChartNames( aExcludedChartNames, pDestPage );
            }

			SdrMarkList aMark = pDragEditView->GetMarkedObjectList();
			aMark.ForceSort();
			sal_uLong nMarkAnz=aMark.GetMarkCount();
			for (sal_uLong nm=0; nm<nMarkAnz; nm++) {
				const SdrMark* pM=aMark.GetMark(nm);
				const SdrObject* pObj=pM->GetMarkedSdrObj();

				// #116235#
				SdrObject* pNeuObj=pObj->Clone();
				//SdrObject* pNeuObj=pObj->Clone(pDestPage,pDrawModel);

				if (pNeuObj!=NULL)
				{
					pNeuObj->SetModel(pDrawModel);
					pNeuObj->SetPage(pDestPage);

					//	#68787# copy graphics within the same model - always needs new name
					if ( pNeuObj->ISA(SdrGrafObj) && !bPasteIsMove )
						pNeuObj->SetName(((ScDrawLayer*)pDrawModel)->GetNewGraphicName());

					if (nDiffX!=0 || nDiffY!=0)
						pNeuObj->NbcMove(Size(nDiffX,nDiffY));
					pDestPage->InsertObject( pNeuObj );
					pScDrawView->AddUndo(new SdrUndoInsertObj( *pNeuObj ));

					//	Chart braucht nicht mehr getrennt behandelt zu werden,
					//	weil es seine Daten jetzt selber hat
				}
			}

			if (bPasteIsMove)
				pDragEditView->DeleteMarked();

            ScDocument* pDocument = GetViewData()->GetDocument();
            ScDocShell* pDocShell = GetViewData()->GetDocShell();
            ScModelObj* pModelObj = ( pDocShell ? ScModelObj::getImplementation( pDocShell->GetModel() ) : NULL );
            if ( pDocument && pDestPage && pModelObj && pDrawTrans )
            {
                const ScRangeListVector& rProtectedChartRangesVector( pDrawTrans->GetProtectedChartRangesVector() );
                ScChartHelper::CreateProtectedChartListenersAndNotify( pDocument, pDestPage, pModelObj, nTab,
                    rProtectedChartRangesVector, aExcludedChartNames, bSameDoc );
            }
		}
	}
	else
	{
		bPasteIsMove = sal_False;		// kein internes Verschieben passiert

        SdrView aView(pModel);      // #i71529# never create a base class of SdrView directly!
		SdrPageView* pPv = aView.ShowSdrPage(aView.GetModel()->GetPage(0));
		aView.MarkAllObj(pPv);
		Size aSize = aView.GetAllMarkedRect().GetSize();
		lcl_AdjustInsertPos( GetViewData(), aPos, aSize );

		//	#41333# Markierung nicht aendern, wenn Ole-Objekt aktiv
		//	(bei Drop aus Ole-Objekt wuerde sonst mitten im ExecuteDrag deaktiviert!)

		sal_uLong nOptions = 0;
		SfxInPlaceClient* pClient = GetViewData()->GetViewShell()->GetIPClient();
        if ( pClient && pClient->IsObjectInPlaceActive() )
			nOptions |= SDRINSERT_DONTMARK;

        ::std::vector< ::rtl::OUString > aExcludedChartNames;
        SCTAB nTab = GetViewData()->GetTabNo();
        SdrPage* pPage = pScDrawView->GetModel()->GetPage( static_cast< sal_uInt16 >( nTab ) );
        DBG_ASSERT( pPage, "Page?" );
        if ( pPage )
        {
            ScChartHelper::GetChartNames( aExcludedChartNames, pPage );
        }

        // #89247# Set flag for ScDocument::UpdateChartListeners() which is
        // called during paste.
        if ( !bSameDocClipboard )
            GetViewData()->GetDocument()->SetPastingDrawFromOtherDoc( sal_True );

		pScDrawView->Paste( *pModel, aPos, NULL, nOptions );

        if ( !bSameDocClipboard )
            GetViewData()->GetDocument()->SetPastingDrawFromOtherDoc( sal_False );

		// #68991# Paste puts all objects on the active (front) layer
		// controls must be on SC_LAYER_CONTROLS
		if (pPage)
		{
			SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS );
			SdrObject* pObject = aIter.Next();
			while (pObject)
			{
				if ( pObject->ISA(SdrUnoObj) && pObject->GetLayer() != SC_LAYER_CONTROLS )
					pObject->NbcSetLayer(SC_LAYER_CONTROLS);
				pObject = aIter.Next();
			}
		}

		// #75299# all graphics objects must have names
		GetViewData()->GetDocument()->EnsureGraphicNames();

        ScDocument* pDocument = GetViewData()->GetDocument();
        ScDocShell* pDocShell = GetViewData()->GetDocShell();
        ScModelObj* pModelObj = ( pDocShell ? ScModelObj::getImplementation( pDocShell->GetModel() ) : NULL );
        ScDrawTransferObj* pTransferObj = ScDrawTransferObj::GetOwnClipboard( NULL );
        if ( pDocument && pPage && pModelObj && ( pTransferObj || pDrawTrans ) )
        {
            const ScRangeListVector& rProtectedChartRangesVector(
                pTransferObj ? pTransferObj->GetProtectedChartRangesVector() : pDrawTrans->GetProtectedChartRangesVector() );
            ScChartHelper::CreateProtectedChartListenersAndNotify( pDocument, pPage, pModelObj, nTab,
                rProtectedChartRangesVector, aExcludedChartNames, bSameDocClipboard );
        }
	}

	if (bGroup)
	{
		pScDrawView->GroupMarked();
		pScDrawView->EndUndo();
	}

	if (pRef)
		pRef->SetMapMode( aOldMapMode );

    // GetViewData()->GetViewShell()->SetDrawShell( sal_True );
    // #99759# It is not sufficient to just set the DrawShell if we pasted, for
    // example, a chart.  SetDrawShellOrSub() would only work for D&D in the
    // same document but not if inserting from the clipboard, therefore
    // MarkListHasChanged() is what we need.
    pScDrawView->MarkListHasChanged();

}

sal_Bool ScViewFunc::PasteObject( const Point& rPos, const uno::Reference < embed::XEmbeddedObject >& xObj,
								const Size* pDescSize, const Graphic* pReplGraph, const ::rtl::OUString& aMediaType, sal_Int64 nAspect )
{
	MakeDrawLayer();
    if ( xObj.is() )
	{
        ::rtl::OUString aName;
        //TODO/MBA: is that OK?
        comphelper::EmbeddedObjectContainer& aCnt = GetViewData()->GetViewShell()->GetObjectShell()->GetEmbeddedObjectContainer();
        if ( !aCnt.HasEmbeddedObject( xObj ) )
            aCnt.InsertEmbeddedObject( xObj, aName );
        else
            aName = aCnt.GetEmbeddedObjectName( xObj );

		svt::EmbeddedObjectRef aObjRef( xObj, nAspect );
		if ( pReplGraph )
			aObjRef.SetGraphic( *pReplGraph, aMediaType );

		Size aSize;
		if ( nAspect == embed::Aspects::MSOLE_ICON )
		{
			MapMode aMapMode( MAP_100TH_MM );
			aSize = aObjRef.GetSize( &aMapMode );
		}
		else
		{
        	// working with visual area can switch object to running state
        	MapUnit aMapObj = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) );
        	MapUnit aMap100 = MAP_100TH_MM;

        	if ( pDescSize && pDescSize->Width() && pDescSize->Height() )
        	{
            	// use size from object descriptor if given
            	aSize = OutputDevice::LogicToLogic( *pDescSize, aMap100, aMapObj );
            	awt::Size aSz;
            	aSz.Width = aSize.Width();
            	aSz.Height = aSize.Height();
            	xObj->setVisualAreaSize( nAspect, aSz );
        	}
		
        	awt::Size aSz;
			try
			{
				aSz = xObj->getVisualAreaSize( nAspect );
			}
			catch ( embed::NoVisualAreaSizeException& )
			{
				// the default size will be set later
			}
		
        	aSize = Size( aSz.Width, aSz.Height );
			aSize = OutputDevice::LogicToLogic( aSize, aMapObj, aMap100 );	// fuer SdrOle2Obj

			if( aSize.Height() == 0 || aSize.Width() == 0 )
			{
				DBG_ERROR("SvObjectDescriptor::GetSize == 0");
				aSize.Width() = 5000;
				aSize.Height() = 5000;
				aSize = OutputDevice::LogicToLogic( aSize, aMap100, aMapObj );
            	aSz.Width = aSize.Width();
            	aSz.Height = aSize.Height();
            	xObj->setVisualAreaSize( nAspect, aSz );
			}
		}

		// don't call AdjustInsertPos
		Point aInsPos = rPos;
		if ( GetViewData()->GetDocument()->IsNegativePage( GetViewData()->GetTabNo() ) )
			aInsPos.X() -= aSize.Width();
		Rectangle aRect( aInsPos, aSize );

		ScDrawView* pDrView = GetScDrawView();
        SdrOle2Obj* pSdrObj = new SdrOle2Obj( aObjRef, aName, aRect );

		SdrPageView* pPV = pDrView->GetSdrPageView();
		pDrView->InsertObjectSafe( pSdrObj, *pPV );				// nicht markieren wenn Ole
		GetViewData()->GetViewShell()->SetDrawShell( sal_True );
		return sal_True;
	}
	else
		return sal_False;
}

sal_Bool ScViewFunc::PasteBitmapEx( const Point& rPos, const BitmapEx& rBmpEx )
{
	String aEmpty;
	Graphic aGraphic(rBmpEx);
	return PasteGraphic( rPos, aGraphic, aEmpty, aEmpty );
}

sal_Bool ScViewFunc::PasteMetaFile( const Point& rPos, const GDIMetaFile& rMtf )
{
	String aEmpty;
	Graphic aGraphic(rMtf);
	return PasteGraphic( rPos, aGraphic, aEmpty, aEmpty );
}

sal_Bool ScViewFunc::PasteGraphic( const Point& rPos, const Graphic& rGraphic,
								const String& rFile, const String& rFilter )
{
	MakeDrawLayer();
	ScDrawView* pScDrawView = GetScDrawView();

    // #123922# check if the drop was over an existing object; if yes, evtl. replace
    // the graphic for a SdrGraphObj (including link state updates) or adapt the fill 
    // style for other objects
    if(pScDrawView)
    {
        SdrObject* pPickObj = 0;
        SdrPageView* pPageView = pScDrawView->GetSdrPageView();

        if(pPageView)
        {
            pScDrawView->PickObj(rPos, pScDrawView->getHitTolLog(), pPickObj, pPageView);
        }

        if(pPickObj)
        {
            const String aBeginUndo(ScGlobal::GetRscString(STR_UNDO_DRAGDROP));
            SdrObject* pResult = pScDrawView->ApplyGraphicToObject(
                *pPickObj, 
                rGraphic, 
                aBeginUndo, 
                rFile, 
                rFilter);

            if(pResult)
            {
                // we are done; mark the modified/new object
                pScDrawView->MarkObj(pResult, pScDrawView->GetSdrPageView());
                return sal_True;
            }
        }
    }

	Point aPos( rPos );
	Window* pWin = GetActiveWin();
	MapMode aSourceMap = rGraphic.GetPrefMapMode();
	MapMode aDestMap( MAP_100TH_MM );

	if (aSourceMap.GetMapUnit() == MAP_PIXEL)
	{
			//	Pixel-Korrektur beruecksichtigen, damit Bitmap auf dem Bildschirm stimmt

		Fraction aScaleX, aScaleY;
		pScDrawView->CalcNormScale( aScaleX, aScaleY );
		aDestMap.SetScaleX(aScaleX);
		aDestMap.SetScaleY(aScaleY);
	}

	Size aSize = pWin->LogicToLogic( rGraphic.GetPrefSize(), &aSourceMap, &aDestMap );
//	lcl_AdjustInsertPos( GetViewData(), aPos, aSize );
	if ( GetViewData()->GetDocument()->IsNegativePage( GetViewData()->GetTabNo() ) )
		aPos.X() -= aSize.Width();

	GetViewData()->GetViewShell()->SetDrawShell( sal_True );
    Rectangle aRect(aPos, aSize);
	SdrGrafObj* pGrafObj = new SdrGrafObj(rGraphic, aRect);

    // #118522# calling SetGraphicLink here doesn't work

	//	#49961# Pfad wird nicht mehr als Name der Grafik gesetzt

	ScDrawLayer* pLayer = (ScDrawLayer*) pScDrawView->GetModel();
	String aName = pLayer->GetNewGraphicName();					// "Grafik x"
	pGrafObj->SetName(aName);

	// nicht markieren wenn Ole
	pScDrawView->InsertObjectSafe(pGrafObj, *pScDrawView->GetSdrPageView());

    // #118522# SetGraphicLink has to be used after inserting the object,
    // otherwise an empty graphic is swapped in and the contact stuff crashes.
    // See #i37444#.
	if (rFile.Len())
		pGrafObj->SetGraphicLink( rFile, rFilter );

	return sal_True;
}