/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



#include "precompiled_svx.hxx"
#include <svx/sdr/primitive2d/sdrmeasureprimitive2d.hxx>
#include <svx/sdr/primitive2d/sdrdecompositiontools.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx>
#include <svx/sdr/attribute/sdrtextattribute.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/tools/canvastools.hxx>
#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>

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

using namespace com::sun::star;

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

namespace drawinglayer
{
    namespace primitive2d
    {
        Primitive2DReference SdrMeasurePrimitive2D::impCreatePart(
            const attribute::SdrLineAttribute& rLineAttribute,
            const basegfx::B2DHomMatrix& rObjectMatrix, 
            const basegfx::B2DPoint& rStart, 
            const basegfx::B2DPoint& rEnd, 
            bool bLeftActive, 
            bool bRightActive) const
        {
            const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
            basegfx::B2DPolygon aPolygon;

            aPolygon.append(rStart);
            aPolygon.append(rEnd);
            aPolygon.transform(rObjectMatrix);

            if(rLineStartEnd.isDefault() || (!bLeftActive && !bRightActive))
            {
                return createPolygonLinePrimitive(
                    aPolygon, 
                    rLineAttribute, 
                    attribute::SdrLineStartEndAttribute());
            }
        
            if(bLeftActive && bRightActive)
            {
                return createPolygonLinePrimitive(
                    aPolygon, 
                    rLineAttribute, 
                    rLineStartEnd);
            }
            
            const basegfx::B2DPolyPolygon aEmpty;
            const attribute::SdrLineStartEndAttribute aLineStartEnd(
                bLeftActive ? rLineStartEnd.getStartPolyPolygon() : aEmpty, bRightActive ? rLineStartEnd.getEndPolyPolygon() : aEmpty,
                bLeftActive ? rLineStartEnd.getStartWidth() : 0.0, bRightActive ? rLineStartEnd.getEndWidth() : 0.0,
                bLeftActive ? rLineStartEnd.isStartActive() : false, bRightActive ? rLineStartEnd.isEndActive() : false,
                bLeftActive ? rLineStartEnd.isStartCentered() : false, bRightActive? rLineStartEnd.isEndCentered() : false);

            return createPolygonLinePrimitive(
                aPolygon, 
                rLineAttribute, 
                aLineStartEnd);
        }

		Primitive2DSequence SdrMeasurePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& aViewInformation) const
		{
			Primitive2DSequence aRetval;
			SdrBlockTextPrimitive2D* pBlockText = 0;
			basegfx::B2DRange aTextRange;
			double fTextX((getStart().getX() + getEnd().getX()) * 0.5);
			double fTextY((getStart().getX() + getEnd().getX()) * 0.5);
			const basegfx::B2DVector aLine(getEnd() - getStart());
			const double fDistance(aLine.getLength());
			const double fAngle(atan2(aLine.getY(), aLine.getX()));
			bool bAutoUpsideDown(false);
			const attribute::SdrTextAttribute rTextAttribute = getSdrLSTAttribute().getText();
            const basegfx::B2DHomMatrix aObjectMatrix(
                basegfx::tools::createShearXRotateTranslateB2DHomMatrix(0.0, fAngle, getStart()));

            // preapare text, but do not add yet; it needs to be aligned to
            // the line geometry
			if(!rTextAttribute.isDefault())
			{
				basegfx::B2DHomMatrix aTextMatrix;
				double fTestAngle(fAngle);
	
				if(getTextRotation())
				{
					aTextMatrix.rotate(-90.0 * F_PI180);
					fTestAngle -= (90.0 * F_PI180);

					if(getTextAutoAngle() && fTestAngle < -F_PI)
					{
						fTestAngle += F_2PI;
					}
				}

				if(getTextAutoAngle())
				{
					if(fTestAngle > (F_PI / 4.0) || fTestAngle < (-F_PI * (3.0 / 4.0)))
					{
						bAutoUpsideDown = true;
					}
				}

				// create primitive and get text range
				pBlockText = new SdrBlockTextPrimitive2D(
                    &rTextAttribute.getSdrText(), 
                    rTextAttribute.getOutlinerParaObject(), 
                    aTextMatrix, 
                    SDRTEXTHORZADJUST_CENTER,
                    SDRTEXTVERTADJUST_CENTER,
                    rTextAttribute.isScroll(), 
                    false, 
                    false,
                    false,
					false);

                aTextRange = pBlockText->getB2DRange(aViewInformation);
			}

            // prepare line attribute and result
			{
	            const attribute::SdrLineAttribute rLineAttribute(getSdrLSTAttribute().getLine());
				bool bArrowsOutside(false);
				bool bMainLineSplitted(false);
				const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
				double fStartArrowW(0.0);
				double fStartArrowH(0.0);
				double fEndArrowW(0.0);
				double fEndArrowH(0.0);

				if(!rLineStartEnd.isDefault())
				{
					if(rLineStartEnd.isStartActive())
					{
						const basegfx::B2DRange aArrowRange(basegfx::tools::getRange(rLineStartEnd.getStartPolyPolygon()));
						fStartArrowW = rLineStartEnd.getStartWidth();
						fStartArrowH = aArrowRange.getHeight() * fStartArrowW / aArrowRange.getWidth();
						
						if(rLineStartEnd.isStartCentered())
						{
							fStartArrowH *= 0.5;
						}
					}

					if(rLineStartEnd.isEndActive())
					{
						const basegfx::B2DRange aArrowRange(basegfx::tools::getRange(rLineStartEnd.getEndPolyPolygon()));
						fEndArrowW = rLineStartEnd.getEndWidth();
						fEndArrowH = aArrowRange.getHeight() * fEndArrowW / aArrowRange.getWidth();
						
						if(rLineStartEnd.isEndCentered())
						{
							fEndArrowH *= 0.5;
						}
					}
				}

				const double fSpaceNeededByArrows(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.5));
				const double fArrowsOutsideLen((fStartArrowH + fEndArrowH + fStartArrowW + fEndArrowW) * 0.5);
				const double fHalfLineWidth(rLineAttribute.getWidth() * 0.5);

				if(fSpaceNeededByArrows > fDistance)
				{
					bArrowsOutside = true;
				}

				MeasureTextPosition eHorizontal(getHorizontal());
				MeasureTextPosition eVertical(getVertical());

				if(MEASURETEXTPOSITION_AUTOMATIC == eVertical)
				{
					eVertical = MEASURETEXTPOSITION_NEGATIVE;
				}

				if(MEASURETEXTPOSITION_CENTERED == eVertical)
				{
					bMainLineSplitted = true;
				}

				if(MEASURETEXTPOSITION_AUTOMATIC == eHorizontal)
				{
					if(aTextRange.getWidth() > fDistance)
					{
						eHorizontal = MEASURETEXTPOSITION_NEGATIVE;
					}
					else 
					{
						eHorizontal = MEASURETEXTPOSITION_CENTERED;
					}

					if(bMainLineSplitted)
					{
						if(aTextRange.getWidth() + fSpaceNeededByArrows > fDistance)
						{
							bArrowsOutside = true;
						}
					}
					else
					{
						const double fSmallArrowNeed(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.125));

						if(aTextRange.getWidth() + fSmallArrowNeed > fDistance)
						{
							bArrowsOutside = true;
						}
					}
				}

				if(MEASURETEXTPOSITION_CENTERED != eHorizontal)
				{
					bArrowsOutside = true;
				}

				// switch text above/below?
				if(getBelow() || (bAutoUpsideDown && !getTextRotation()))
				{
					if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
					{
						eVertical = MEASURETEXTPOSITION_POSITIVE;
					}
					else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
					{
						eVertical = MEASURETEXTPOSITION_NEGATIVE;
					}
				}

				const double fMainLineOffset(getBelow() ? getDistance() : -getDistance());
				const basegfx::B2DPoint aMainLeft(0.0, fMainLineOffset);
				const basegfx::B2DPoint aMainRight(fDistance, fMainLineOffset);

				// main line
				if(bArrowsOutside)
				{
					double fLenLeft(fArrowsOutsideLen);
					double fLenRight(fArrowsOutsideLen);

					if(!bMainLineSplitted)
					{
						if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
						{
							fLenLeft = fStartArrowH + aTextRange.getWidth();
						}
						else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
						{
							fLenRight = fEndArrowH + aTextRange.getWidth();
						}
					}

					const basegfx::B2DPoint aMainLeftLeft(aMainLeft.getX() - fLenLeft, aMainLeft.getY());
					const basegfx::B2DPoint aMainRightRight(aMainRight.getX() + fLenRight, aMainRight.getY());

					appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeftLeft, aMainLeft, false, true));
					appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainRight, aMainRightRight, true, false));

					if(!bMainLineSplitted || MEASURETEXTPOSITION_CENTERED != eHorizontal)
					{
						appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, false, false));
					}
				}
				else
				{
					if(bMainLineSplitted)
					{
						const double fHalfLength((fDistance - (aTextRange.getWidth() + (fStartArrowH + fEndArrowH) * 0.25)) * 0.5);
						const basegfx::B2DPoint aMainInnerLeft(aMainLeft.getX() + fHalfLength, aMainLeft.getY());
						const basegfx::B2DPoint aMainInnerRight(aMainRight.getX() - fHalfLength, aMainRight.getY());

						appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainInnerLeft, true, false));
						appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainInnerRight, aMainRight, false, true));
					}
					else
					{
						appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, true, true));
					}
				}

				// left/right help line value preparation
				const double fTopEdge(getBelow() ? getUpper() + getDistance() : -getUpper() - getDistance());
				const double fBottomLeft(getBelow() ? getLower() - getLeftDelta() : getLeftDelta() - getLower());
				const double fBottomRight(getBelow() ? getLower() - getRightDelta() : getRightDelta() - getLower());

				// left help line
				const basegfx::B2DPoint aLeftUp(0.0, fTopEdge);
				const basegfx::B2DPoint aLeftDown(0.0, fBottomLeft);

				appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aLeftDown, aLeftUp, false, false));

				// right help line
				const basegfx::B2DPoint aRightUp(fDistance, fTopEdge);
				const basegfx::B2DPoint aRightDown(fDistance, fBottomRight);

				appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, impCreatePart(rLineAttribute, aObjectMatrix, aRightDown, aRightUp, false, false));

				// text horizontal position
				if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
				{
					// left
					const double fSmall(fArrowsOutsideLen * 0.18);
					fTextX = aMainLeft.getX() - (fStartArrowH + aTextRange.getWidth() + fSmall + fHalfLineWidth);

					if(bMainLineSplitted)
					{
						fTextX -= (fArrowsOutsideLen - fStartArrowH);
					}

					if(!rTextAttribute.isDefault())
					{
						fTextX -= rTextAttribute.getTextRightDistance();
					}
				}
				else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
				{
					// right
					const double fSmall(fArrowsOutsideLen * 0.18);
					fTextX = aMainRight.getX() + (fEndArrowH + fSmall + fHalfLineWidth);

					if(bMainLineSplitted)
					{
						fTextX += (fArrowsOutsideLen - fEndArrowH);
					}

					if(!rTextAttribute.isDefault())
					{
						fTextX += rTextAttribute.getTextLeftDistance();
					}
				}
				else // MEASURETEXTPOSITION_CENTERED
				{
					// centered
					fTextX = aMainLeft.getX() + ((fDistance - aTextRange.getWidth()) * 0.5);

					if(!rTextAttribute.isDefault())
					{
						fTextX += (rTextAttribute.getTextLeftDistance() - rTextAttribute.getTextRightDistance()) / 2L;
					}
				}

				// text vertical position
				if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
				{
					// top
					const double fSmall(fArrowsOutsideLen * 0.10);
					fTextY = aMainLeft.getY() - (aTextRange.getHeight() + fSmall + fHalfLineWidth);

					if(!rTextAttribute.isDefault())
					{
						fTextY -= rTextAttribute.getTextLowerDistance();
					}
				}
				else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
				{
					// bottom
					const double fSmall(fArrowsOutsideLen * 0.10);
					fTextY = aMainLeft.getY() + (fSmall + fHalfLineWidth);
					
					if(!rTextAttribute.isDefault())
					{
						fTextY += rTextAttribute.getTextUpperDistance();
					}
				}
				else // MEASURETEXTPOSITION_CENTERED
				{
					// centered
					fTextY = aMainLeft.getY() - (aTextRange.getHeight() * 0.5);
					
					if(!rTextAttribute.isDefault())
					{
						fTextY += (rTextAttribute.getTextUpperDistance() - rTextAttribute.getTextLowerDistance()) / 2L;
					}
				}
			}

			if(getSdrLSTAttribute().getLine().isDefault())
            {
                // embed line geometry to invisible (100% transparent) line group for HitTest
                const Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(aRetval));
                
				aRetval = Primitive2DSequence(&xHiddenLines, 1);
            }

            if(pBlockText)
			{
				// create transformation to text primitive end position
				basegfx::B2DHomMatrix aChange;

				// handle auto text rotation
				if(bAutoUpsideDown)
				{
					aChange.rotate(F_PI);
				}

				// move from aTextRange.TopLeft to fTextX, fTextY
				aChange.translate(fTextX - aTextRange.getMinX(), fTextY - aTextRange.getMinY());

				// apply object matrix
				aChange *= aObjectMatrix;

				// apply to existing text primitive
				SdrTextPrimitive2D* pNewBlockText = pBlockText->createTransformedClone(aChange);
				OSL_ENSURE(pNewBlockText, "SdrMeasurePrimitive2D::create2DDecomposition: Could not create transformed clone of text primitive (!)");
				delete pBlockText;

				// add to local primitives
				appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, Primitive2DReference(pNewBlockText));
			}

			// add shadow
			if(!getSdrLSTAttribute().getShadow().isDefault())
			{
                aRetval = createEmbeddedShadowPrimitive(
					aRetval, 
					getSdrLSTAttribute().getShadow());
			}

			return aRetval;
		}

		SdrMeasurePrimitive2D::SdrMeasurePrimitive2D(
			const attribute::SdrLineShadowTextAttribute& rSdrLSTAttribute,
			const basegfx::B2DPoint& rStart, 
			const basegfx::B2DPoint& rEnd,
			MeasureTextPosition eHorizontal,
			MeasureTextPosition eVertical,
			double fDistance,
			double fUpper,
			double fLower,
			double fLeftDelta,
			double fRightDelta,
			bool bBelow,
			bool bTextRotation,
			bool bTextAutoAngle)
		:	BufferedDecompositionPrimitive2D(),
			maSdrLSTAttribute(rSdrLSTAttribute),
			maStart(rStart),
			maEnd(rEnd),
			meHorizontal(eHorizontal),
			meVertical(eVertical),
			mfDistance(fDistance),
			mfUpper(fUpper),
			mfLower(fLower),
			mfLeftDelta(fLeftDelta),
			mfRightDelta(fRightDelta),
			mbBelow(bBelow),
			mbTextRotation(bTextRotation),
			mbTextAutoAngle(bTextAutoAngle)
		{
		}

		bool SdrMeasurePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
		{
			if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
			{
				const SdrMeasurePrimitive2D& rCompare = (SdrMeasurePrimitive2D&)rPrimitive;

				return (getStart() == rCompare.getStart()
					&& getEnd() == rCompare.getEnd()
					&& getHorizontal() == rCompare.getHorizontal()
					&& getVertical() == rCompare.getVertical()
					&& getDistance() == rCompare.getDistance()
					&& getUpper() == rCompare.getUpper()
					&& getLower() == rCompare.getLower()
					&& getLeftDelta() == rCompare.getLeftDelta()
					&& getRightDelta() == rCompare.getRightDelta()
					&& getBelow() == rCompare.getBelow()
					&& getTextRotation() == rCompare.getTextRotation()
					&& getTextAutoAngle() == rCompare.getTextAutoAngle()
					&& getSdrLSTAttribute() == rCompare.getSdrLSTAttribute());
			}

			return false;
		}

		// provide unique ID
		ImplPrimitrive2DIDBlock(SdrMeasurePrimitive2D, PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D)

	} // end of namespace primitive2d
} // end of namespace drawinglayer

//////////////////////////////////////////////////////////////////////////////
// eof