/**************************************************************
 * 
 * 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_svx.hxx"

#include <svx/svdorect.hxx>
#include <math.h>
#include <stdlib.h>
#include <svx/xpool.hxx>
#include <svx/xpoly.hxx>
#include <svx/svdattr.hxx>
#include <svx/svdpool.hxx>
#include <svx/svdtrans.hxx>
#include <svx/svdetc.hxx>
#include <svx/svddrag.hxx>
#include <svx/svdmodel.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdocapt.hxx> // fuer Import von SdrFileVersion 2
#include <svx/svdpagv.hxx> // fuer
#include <svx/svdview.hxx> // das
#include <svx/svdundo.hxx> // Macro-Beispiel
#include <svx/svdopath.hxx>
#include "svx/svdglob.hxx"  // Stringcache
#include "svx/svdstr.hrc"   // Objektname
#include <svx/xflclit.hxx>
#include <svx/xlnclit.hxx>
#include <svx/xlnwtit.hxx>
#include "svdoimp.hxx"
#include <svx/sdr/properties/rectangleproperties.hxx>
#include <svx/sdr/contact/viewcontactofsdrrectobj.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>

//////////////////////////////////////////////////////////////////////////////
// BaseProperties section

sdr::properties::BaseProperties* SdrRectObj::CreateObjectSpecificProperties()
{
	return new sdr::properties::RectangleProperties(*this);
}

//////////////////////////////////////////////////////////////////////////////
// DrawContact section

sdr::contact::ViewContact* SdrRectObj::CreateObjectSpecificViewContact()
{
	return new sdr::contact::ViewContactOfSdrRectObj(*this);
}

//////////////////////////////////////////////////////////////////////////////

TYPEINIT1(SdrRectObj,SdrTextObj);

SdrRectObj::SdrRectObj()
:	mpXPoly(0L)
{
	bClosedObj=sal_True;
}

SdrRectObj::SdrRectObj(const Rectangle& rRect)
:	SdrTextObj(rRect),
	mpXPoly(NULL)
{
	bClosedObj=sal_True;
}

SdrRectObj::SdrRectObj(SdrObjKind eNewTextKind)
:	SdrTextObj(eNewTextKind),
	mpXPoly(NULL)
{
	DBG_ASSERT(eTextKind==OBJ_TEXT || eTextKind==OBJ_TEXTEXT ||
			   eTextKind==OBJ_OUTLINETEXT || eTextKind==OBJ_TITLETEXT,
			   "SdrRectObj::SdrRectObj(SdrObjKind) ist nur fuer Textrahmen gedacht");
	bClosedObj=sal_True;
}

SdrRectObj::SdrRectObj(SdrObjKind eNewTextKind, const Rectangle& rRect)
:	SdrTextObj(eNewTextKind,rRect),
	mpXPoly(NULL)
{
	DBG_ASSERT(eTextKind==OBJ_TEXT || eTextKind==OBJ_TEXTEXT ||
			   eTextKind==OBJ_OUTLINETEXT || eTextKind==OBJ_TITLETEXT,
			   "SdrRectObj::SdrRectObj(SdrObjKind,...) ist nur fuer Textrahmen gedacht");
	bClosedObj=sal_True;
}

SdrRectObj::SdrRectObj(SdrObjKind eNewTextKind, const Rectangle& rNewRect, SvStream& rInput, const String& rBaseURL, sal_uInt16 eFormat)
:    SdrTextObj(eNewTextKind,rNewRect,rInput,rBaseURL,eFormat),
	mpXPoly(NULL)
{
	DBG_ASSERT(eTextKind==OBJ_TEXT || eTextKind==OBJ_TEXTEXT ||
			   eTextKind==OBJ_OUTLINETEXT || eTextKind==OBJ_TITLETEXT,
			   "SdrRectObj::SdrRectObj(SdrObjKind,...) ist nur fuer Textrahmen gedacht");
	bClosedObj=sal_True;
}

SdrRectObj::~SdrRectObj()
{
	if(mpXPoly) 
	{
		delete mpXPoly;
	}
}

void SdrRectObj::SetXPolyDirty()
{
	if(mpXPoly) 
	{
		delete mpXPoly;
		mpXPoly = 0L;
	}
}

FASTBOOL SdrRectObj::PaintNeedsXPoly(long nEckRad) const
{
	FASTBOOL bNeed=aGeo.nDrehWink!=0 || aGeo.nShearWink!=0 || nEckRad!=0;
	return bNeed;
}

XPolygon SdrRectObj::ImpCalcXPoly(const Rectangle& rRect1, long nRad1) const
{
	XPolygon aXPoly(rRect1,nRad1,nRad1);
	const sal_uInt16 nPointAnz(aXPoly.GetPointCount());
	XPolygon aNeuPoly(nPointAnz+1);
	sal_uInt16 nShift=nPointAnz-2;
	if (nRad1!=0) nShift=nPointAnz-5;
	sal_uInt16 j=nShift;
	for (sal_uInt16 i=1; i<nPointAnz; i++) {
		aNeuPoly[i]=aXPoly[j];
		aNeuPoly.SetFlags(i,aXPoly.GetFlags(j));
		j++;
		if (j>=nPointAnz) j=1;
	}
	aNeuPoly[0]=rRect1.BottomCenter();
	aNeuPoly[nPointAnz]=aNeuPoly[0];
	aXPoly=aNeuPoly;

	// Die Winkelangaben beziehen sich immer auf die linke obere Ecke von !aRect!
	if (aGeo.nShearWink!=0) ShearXPoly(aXPoly,aRect.TopLeft(),aGeo.nTan);
	if (aGeo.nDrehWink!=0) RotateXPoly(aXPoly,aRect.TopLeft(),aGeo.nSin,aGeo.nCos);
	return aXPoly;
}

void SdrRectObj::RecalcXPoly()
{
	mpXPoly = new XPolygon(ImpCalcXPoly(aRect,GetEckenradius()));
}

const XPolygon& SdrRectObj::GetXPoly() const
{
	if(!mpXPoly) 
	{
		((SdrRectObj*)this)->RecalcXPoly();
	}

	return *mpXPoly;
}

void SdrRectObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
{
	FASTBOOL bNoTextFrame=!IsTextFrame();
	rInfo.bResizeFreeAllowed=bNoTextFrame || aGeo.nDrehWink%9000==0;
	rInfo.bResizePropAllowed=sal_True;
	rInfo.bRotateFreeAllowed=sal_True;
	rInfo.bRotate90Allowed  =sal_True;
	rInfo.bMirrorFreeAllowed=bNoTextFrame;
	rInfo.bMirror45Allowed  =bNoTextFrame;
	rInfo.bMirror90Allowed  =bNoTextFrame;

	// allow transparence
	rInfo.bTransparenceAllowed = sal_True;

	// gradient depends on fillstyle
	XFillStyle eFillStyle = ((XFillStyleItem&)(GetObjectItem(XATTR_FILLSTYLE))).GetValue();
	rInfo.bGradientAllowed = (eFillStyle == XFILL_GRADIENT);

	rInfo.bShearAllowed     =bNoTextFrame;
	rInfo.bEdgeRadiusAllowed=sal_True;

	FASTBOOL bCanConv=!HasText() || ImpCanConvTextToCurve();
	if (bCanConv && !bNoTextFrame && !HasText()) {
		bCanConv=HasFill() || HasLine();
	}
	rInfo.bCanConvToPath    =bCanConv;
	rInfo.bCanConvToPoly    =bCanConv;
	rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
}

sal_uInt16 SdrRectObj::GetObjIdentifier() const
{
	if (IsTextFrame()) return sal_uInt16(eTextKind);
	else return sal_uInt16(OBJ_RECT);
}

void SdrRectObj::TakeUnrotatedSnapRect(Rectangle& rRect) const
{
	rRect=aRect;
	if (aGeo.nShearWink!=0) {
		long nDst=Round((aRect.Bottom()-aRect.Top())*aGeo.nTan);
		if (aGeo.nShearWink>0) {
			Point aRef(rRect.TopLeft());
			rRect.Left()-=nDst;
			Point aTmpPt(rRect.TopLeft());
			RotatePoint(aTmpPt,aRef,aGeo.nSin,aGeo.nCos);
			aTmpPt-=rRect.TopLeft();
			rRect.Move(aTmpPt.X(),aTmpPt.Y());
		} else {
			rRect.Right()-=nDst;
		}
	}
}

void SdrRectObj::TakeObjNameSingul(XubString& rName) const
{
	if (IsTextFrame())
	{
		SdrTextObj::TakeObjNameSingul(rName);
	}
	else
	{
		sal_uInt16 nResId=STR_ObjNameSingulRECT;
		if (aGeo.nShearWink!=0) {
			nResId+=4;  // Parallelogramm oder Raute
			// Raute ist nicht, weil Shear die vertikalen Kanten verlaengert!
			// Wenn Zeit ist, werde ich das mal berechnen.
		} else {
			if (aRect.GetWidth()==aRect.GetHeight()) nResId+=2; // Quadrat
		}
		if (GetEckenradius()!=0) nResId+=8; // abgerundet
		rName=ImpGetResStr(nResId);

		String aName( GetName() );
		if(aName.Len())
		{
			rName += sal_Unicode(' ');
			rName += sal_Unicode('\'');
			rName += aName;
			rName += sal_Unicode('\'');
		}
	}
}

void SdrRectObj::TakeObjNamePlural(XubString& rName) const
{
	if (IsTextFrame()) SdrTextObj::TakeObjNamePlural(rName);
	else {
		sal_uInt16 nResId=STR_ObjNamePluralRECT;
		if (aGeo.nShearWink!=0) {
			nResId+=4;  // Parallelogramm oder Raute
		} else {
			if (aRect.GetWidth()==aRect.GetHeight()) nResId+=2; // Quadrat
		}
		if (GetEckenradius()!=0) nResId+=8; // abgerundet
		rName=ImpGetResStr(nResId);
	}
}

void SdrRectObj::operator=(const SdrObject& rObj)
{
	SdrTextObj::operator=(rObj);
}

basegfx::B2DPolyPolygon SdrRectObj::TakeXorPoly() const
{
	XPolyPolygon aXPP;
	aXPP.Insert(ImpCalcXPoly(aRect,GetEckenradius()));
	return aXPP.getB2DPolyPolygon();
}

void SdrRectObj::RecalcSnapRect()
{
	long nEckRad=GetEckenradius();
	if ((aGeo.nDrehWink!=0 || aGeo.nShearWink!=0) && nEckRad!=0) {
		maSnapRect=GetXPoly().GetBoundRect();
	} else {
		SdrTextObj::RecalcSnapRect();
	}
}

void SdrRectObj::NbcSetSnapRect(const Rectangle& rRect)
{
	SdrTextObj::NbcSetSnapRect(rRect);
	SetXPolyDirty();
}

void SdrRectObj::NbcSetLogicRect(const Rectangle& rRect)
{
	SdrTextObj::NbcSetLogicRect(rRect);
	SetXPolyDirty();
}

sal_uInt32 SdrRectObj::GetHdlCount() const
{
    return IsTextFrame() ? 10 : 9;
}

SdrHdl* SdrRectObj::GetHdl(sal_uInt32 nHdlNum) const
{
	SdrHdl* pH = NULL;
	Point aPnt;
	SdrHdlKind eKind = HDL_MOVE;

    if(!IsTextFrame())
    {
        nHdlNum++;
    }

    switch(nHdlNum) 
    {
        case 0:
        {
            OSL_ENSURE(!IsTextEditActive(), "Do not use a ImpTextframeHdl for hilighting text in active text edit, this will collide with EditEngine paints (!)");
            pH = new ImpTextframeHdl(aRect);
		    pH->SetObj((SdrObject*)this);
		    pH->SetDrehWink(aGeo.nDrehWink);
            break;
        }
        case 1: 
        {
            long a = GetEckenradius();
            long b = Max(aRect.GetWidth(),aRect.GetHeight())/2; // Wird aufgerundet, da GetWidth() eins draufaddiert
            if (a>b) a=b;
            if (a<0) a=0;
            aPnt=aRect.TopLeft();
            aPnt.X()+=a;
            eKind = HDL_CIRC;
            break;
        }
        case 2: aPnt=aRect.TopLeft();      eKind = HDL_UPLFT; break; // Oben links
        case 3: aPnt=aRect.TopCenter();    eKind = HDL_UPPER; break; // Oben
        case 4: aPnt=aRect.TopRight();     eKind = HDL_UPRGT; break; // Oben rechts
        case 5: aPnt=aRect.LeftCenter();   eKind = HDL_LEFT ; break; // Links
        case 6: aPnt=aRect.RightCenter();  eKind = HDL_RIGHT; break; // Rechts
        case 7: aPnt=aRect.BottomLeft();   eKind = HDL_LWLFT; break; // Unten links
        case 8: aPnt=aRect.BottomCenter(); eKind = HDL_LOWER; break; // Unten
        case 9: aPnt=aRect.BottomRight();  eKind = HDL_LWRGT; break; // Unten rechts
    }

    if(!pH)
    {
        if(aGeo.nShearWink) 
        {
            ShearPoint(aPnt,aRect.TopLeft(),aGeo.nTan);
        }

        if(aGeo.nDrehWink) 
        {
            RotatePoint(aPnt,aRect.TopLeft(),aGeo.nSin,aGeo.nCos);
        }

		pH = new SdrHdl(aPnt,eKind);
		pH->SetObj((SdrObject*)this);
		pH->SetDrehWink(aGeo.nDrehWink);
	}

    return pH;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

bool SdrRectObj::hasSpecialDrag() const
{
	return true;
}

bool SdrRectObj::beginSpecialDrag(SdrDragStat& rDrag) const
{
	const bool bRad(rDrag.GetHdl() && HDL_CIRC == rDrag.GetHdl()->GetKind());

    if(bRad) 
    {
		rDrag.SetEndDragChangesAttributes(true);

        return true;
	} 

    return SdrTextObj::beginSpecialDrag(rDrag);
}

bool SdrRectObj::applySpecialDrag(SdrDragStat& rDrag)
{
	const bool bRad(rDrag.GetHdl() && HDL_CIRC == rDrag.GetHdl()->GetKind());

    if (bRad) 
    {
		Rectangle aBoundRect0; 
		Point aPt(rDrag.GetNow());

        if(aGeo.nDrehWink) 
            RotatePoint(aPt,aRect.TopLeft(),-aGeo.nSin,aGeo.nCos);

		sal_Int32 nRad(aPt.X() - aRect.Left());

        if (nRad < 0) 
            nRad = 0;

        if(nRad != GetEckenradius()) 
        {
            NbcSetEckenradius(nRad);
        }
		
        return true;
	} 
    else
    {
        return SdrTextObj::applySpecialDrag(rDrag);
    }
}

String SdrRectObj::getSpecialDragComment(const SdrDragStat& rDrag) const
{
    const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());

    if(bCreateComment)
    {
        return String();
    }
    else
    {
	    const bool bRad(rDrag.GetHdl() && HDL_CIRC == rDrag.GetHdl()->GetKind());

	    if(bRad)
	    {
		    Point aPt(rDrag.GetNow());

		    // -sin fuer Umkehrung
		    if(aGeo.nDrehWink)
			    RotatePoint(aPt, aRect.TopLeft(), -aGeo.nSin, aGeo.nCos);

		    sal_Int32 nRad(aPt.X() - aRect.Left());

		    if(nRad < 0)
			    nRad = 0;

		    XubString aStr;

		    ImpTakeDescriptionStr(STR_DragRectEckRad, aStr);
		    aStr.AppendAscii(" (");
		    aStr += GetMetrStr(nRad);
		    aStr += sal_Unicode(')');

		    return aStr;
	    }
	    else
	    {
		    return SdrTextObj::getSpecialDragComment(rDrag);
	    }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////

basegfx::B2DPolyPolygon SdrRectObj::TakeCreatePoly(const SdrDragStat& rDrag) const
{
	Rectangle aRect1;
	rDrag.TakeCreateRect(aRect1);
	aRect1.Justify();

	basegfx::B2DPolyPolygon aRetval;
	aRetval.append(ImpCalcXPoly(aRect1,GetEckenradius()).getB2DPolygon());
	return aRetval;
}

Pointer SdrRectObj::GetCreatePointer() const
{
	if (IsTextFrame()) return Pointer(POINTER_DRAW_TEXT);
	return Pointer(POINTER_DRAW_RECT);
}

void SdrRectObj::NbcMove(const Size& rSiz)
{
	SdrTextObj::NbcMove(rSiz);
	SetXPolyDirty();
}

void SdrRectObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
{
	SdrTextObj::NbcResize(rRef,xFact,yFact);
	SetXPolyDirty();
}

void SdrRectObj::NbcRotate(const Point& rRef, long nWink, double sn, double cs)
{
	SdrTextObj::NbcRotate(rRef,nWink,sn,cs);
	SetXPolyDirty();
}

void SdrRectObj::NbcShear(const Point& rRef, long nWink, double tn, FASTBOOL bVShear)
{
	SdrTextObj::NbcShear(rRef,nWink,tn,bVShear);
	SetXPolyDirty();
}

void SdrRectObj::NbcMirror(const Point& rRef1, const Point& rRef2)
{
	SdrTextObj::NbcMirror(rRef1,rRef2);
	SetXPolyDirty();
}

FASTBOOL SdrRectObj::DoMacro(const SdrObjMacroHitRec& rRec)
{
	return SdrTextObj::DoMacro(rRec);
}

XubString SdrRectObj::GetMacroPopupComment(const SdrObjMacroHitRec& rRec) const
{
	return SdrTextObj::GetMacroPopupComment(rRec);
}

SdrGluePoint SdrRectObj::GetVertexGluePoint(sal_uInt16 nPosNum) const
{
	sal_Int32 nWdt = ImpGetLineWdt(); // #i25616# ((XLineWidthItem&)(GetObjectItem(XATTR_LINEWIDTH))).GetValue();
	
	// #i25616#
	if(!LineIsOutsideGeometry())
	{
		nWdt++;
		nWdt /= 2;
	}

	Point aPt;
	switch (nPosNum) {
		case 0: aPt=aRect.TopCenter();    aPt.Y()-=nWdt; break;
		case 1: aPt=aRect.RightCenter();  aPt.X()+=nWdt; break;
		case 2: aPt=aRect.BottomCenter(); aPt.Y()+=nWdt; break;
		case 3: aPt=aRect.LeftCenter();   aPt.X()-=nWdt; break;
	}
	if (aGeo.nShearWink!=0) ShearPoint(aPt,aRect.TopLeft(),aGeo.nTan);
	if (aGeo.nDrehWink!=0) RotatePoint(aPt,aRect.TopLeft(),aGeo.nSin,aGeo.nCos);
	aPt-=GetSnapRect().Center();
	SdrGluePoint aGP(aPt);
	aGP.SetPercent(sal_False);
	return aGP;
}

SdrGluePoint SdrRectObj::GetCornerGluePoint(sal_uInt16 nPosNum) const
{
	sal_Int32 nWdt = ImpGetLineWdt(); // #i25616# ((XLineWidthItem&)(GetObjectItem(XATTR_LINEWIDTH))).GetValue();
	
	// #i25616#
	if(!LineIsOutsideGeometry())
	{
		nWdt++;
		nWdt /= 2;
	}

	Point aPt;
	switch (nPosNum) {
		case 0: aPt=aRect.TopLeft();     aPt.X()-=nWdt; aPt.Y()-=nWdt; break;
		case 1: aPt=aRect.TopRight();    aPt.X()+=nWdt; aPt.Y()-=nWdt; break;
		case 2: aPt=aRect.BottomRight(); aPt.X()+=nWdt; aPt.Y()+=nWdt; break;
		case 3: aPt=aRect.BottomLeft();  aPt.X()-=nWdt; aPt.Y()+=nWdt; break;
	}
	if (aGeo.nShearWink!=0) ShearPoint(aPt,aRect.TopLeft(),aGeo.nTan);
	if (aGeo.nDrehWink!=0) RotatePoint(aPt,aRect.TopLeft(),aGeo.nSin,aGeo.nCos);
	aPt-=GetSnapRect().Center();
	SdrGluePoint aGP(aPt);
	aGP.SetPercent(sal_False);
	return aGP;
}

SdrObject* SdrRectObj::DoConvertToPolyObj(sal_Bool bBezier, bool bAddText) const
{
	XPolygon aXP(ImpCalcXPoly(aRect,GetEckenradius()));
	{ // #40608# Nur Uebergangsweise bis zum neuen TakeContour()
		aXP.Remove(0,1);
		aXP[aXP.GetPointCount()-1]=aXP[0];
	}

	basegfx::B2DPolyPolygon aPolyPolygon(aXP.getB2DPolygon());
    aPolyPolygon.removeDoublePoints();
	SdrObject* pRet = 0L;
	
    // small correction: Do not create something when no fill and no line. To
    // be sure to not damage something with non-text frames, do this only
    // when used with bAddText==false from other converters
	if((bAddText && !IsTextFrame()) || HasFill() || HasLine()) 
	{
		pRet = ImpConvertMakeObj(aPolyPolygon, sal_True, bBezier);
	}

    if(bAddText)
    {
    	pRet = ImpConvertAddText(pRet, bBezier);
    }

	return pRet;
}

void SdrRectObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
{
	SdrTextObj::Notify(rBC,rHint);
	SetXPolyDirty(); // wg. Eckenradius
}

void SdrRectObj::RestGeoData(const SdrObjGeoData& rGeo)
{
	SdrTextObj::RestGeoData(rGeo);
	SetXPolyDirty();
}

// eof