/**************************************************************
 * 
 * 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/sdr/overlay/overlaytools.hxx>
#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <vcl/svapp.hxx>

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

namespace drawinglayer
{
	namespace primitive2d
	{
        OverlayBitmapExPrimitive::OverlayBitmapExPrimitive(
			const BitmapEx& rBitmapEx,
			const basegfx::B2DPoint& rBasePosition,
			sal_uInt16 nCenterX,
			sal_uInt16 nCenterY)
		:   DiscreteMetricDependentPrimitive2D(),
			maBitmapEx(rBitmapEx),
			maBasePosition(rBasePosition),
			mnCenterX(nCenterX),
			mnCenterY(nCenterY)
		{}

		Primitive2DSequence OverlayBitmapExPrimitive::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
		{
            Primitive2DSequence aRetval;
			const Size aBitmapSize(getBitmapEx().GetSizePixel());

            if(aBitmapSize.Width() && aBitmapSize.Height() && basegfx::fTools::more(getDiscreteUnit(), 0.0))
			{
                // calculate back from internal bitmap's extreme coordinates (the edges)
                // to logical coordinates. Only use a unified scaling value (getDiscreteUnit(),
                // the prepared one which expresses how many logic units form a discrete unit) 
                // for this step. This primitive is to be displayed always unscaled (in it's pixel size) 
                // and unrotated, more like a marker
                const double fLeft(((0.0 - getCenterX()) * getDiscreteUnit()) + getBasePosition().getX());
                const double fTop(((0.0 - getCenterY()) * getDiscreteUnit()) + getBasePosition().getY());
                const double fRight(((aBitmapSize.getWidth() - getCenterX()) * getDiscreteUnit()) + getBasePosition().getX());
                const double fBottom(((aBitmapSize.getHeight() - getCenterY()) * getDiscreteUnit()) + getBasePosition().getY());

                // create a BitmapPrimitive2D using those positions
				basegfx::B2DHomMatrix aTransform;

				aTransform.set(0, 0, fRight - fLeft);
				aTransform.set(1, 1, fBottom - fTop);
				aTransform.set(0, 2, fLeft);
				aTransform.set(1, 2, fTop);

                const Primitive2DReference aPrimitive(new BitmapPrimitive2D(getBitmapEx(), aTransform));
                aRetval = Primitive2DSequence(&aPrimitive, 1);
            }
			
            return aRetval;
		}

		bool OverlayBitmapExPrimitive::operator==( const BasePrimitive2D& rPrimitive ) const
		{
			if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
			{
				const OverlayBitmapExPrimitive& rCompare = static_cast< const OverlayBitmapExPrimitive& >(rPrimitive);

				return (getBitmapEx() == rCompare.getBitmapEx()
					&& getBasePosition() == rCompare.getBasePosition()
					&& getCenterX() == rCompare.getCenterX()
					&& getCenterY() == rCompare.getCenterY());
			}

			return false;
		}

		ImplPrimitrive2DIDBlock(OverlayBitmapExPrimitive, PRIMITIVE2D_ID_OVERLAYBITMAPEXPRIMITIVE)

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

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

namespace drawinglayer
{
	namespace primitive2d
	{
        OverlayCrosshairPrimitive::OverlayCrosshairPrimitive(
            const basegfx::B2DPoint& rBasePosition,
			const basegfx::BColor& rRGBColorA,
			const basegfx::BColor& rRGBColorB,
			double fDiscreteDashLength)
        :   ViewportDependentPrimitive2D(),
			maBasePosition(rBasePosition),
			maRGBColorA(rRGBColorA),
			maRGBColorB(rRGBColorB),
			mfDiscreteDashLength(fDiscreteDashLength)
        {}

		Primitive2DSequence OverlayCrosshairPrimitive::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
		{
            // use the prepared Viewport information accessible using getViewport()
            Primitive2DSequence aRetval;

            if(!getViewport().isEmpty())
            {
                aRetval.realloc(2);
                basegfx::B2DPolygon aPolygon;
			    
                aPolygon.append(basegfx::B2DPoint(getViewport().getMinX(), getBasePosition().getY()));
                aPolygon.append(basegfx::B2DPoint(getViewport().getMaxX(), getBasePosition().getY()));
                
                aRetval[0] = Primitive2DReference(
                    new PolygonMarkerPrimitive2D(
                        aPolygon,
                        getRGBColorA(),
                        getRGBColorB(),
                        getDiscreteDashLength()));

                aPolygon.clear();
                aPolygon.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMinY()));
                aPolygon.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMaxY()));
                
                aRetval[1] = Primitive2DReference(
                    new PolygonMarkerPrimitive2D(
                        aPolygon,
                        getRGBColorA(),
                        getRGBColorB(),
                        getDiscreteDashLength()));
            }

            return aRetval;
		}

		bool OverlayCrosshairPrimitive::operator==( const BasePrimitive2D& rPrimitive ) const
		{
			if(ViewportDependentPrimitive2D::operator==(rPrimitive))
			{
				const OverlayCrosshairPrimitive& rCompare = static_cast< const OverlayCrosshairPrimitive& >(rPrimitive);

				return (getBasePosition() == rCompare.getBasePosition()
					&& getRGBColorA() == rCompare.getRGBColorA()
					&& getRGBColorB() == rCompare.getRGBColorB()
                    && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
			}

			return false;
		}

		ImplPrimitrive2DIDBlock(OverlayCrosshairPrimitive, PRIMITIVE2D_ID_OVERLAYCROSSHAIRPRIMITIVE)

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

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

namespace drawinglayer
{
	namespace primitive2d
	{
        OverlayRectanglePrimitive::OverlayRectanglePrimitive(
            const basegfx::B2DRange& rObjectRange,
			const basegfx::BColor& rColor,
            double fTransparence,
            double fDiscreteGrow,
            double fDiscreteShrink,
            double fRotation)
        :   DiscreteMetricDependentPrimitive2D(),
            maObjectRange(rObjectRange),
			maColor(rColor),
            mfTransparence(fTransparence),
            mfDiscreteGrow(fDiscreteGrow),
            mfDiscreteShrink(fDiscreteShrink),
            mfRotation(fRotation)
        {}

		Primitive2DSequence OverlayRectanglePrimitive::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
		{
            Primitive2DSequence aRetval;
            basegfx::B2DRange aInnerRange(getObjectRange());

            if(!aInnerRange.isEmpty() && basegfx::fTools::more(getDiscreteUnit(), 0.0) && getTransparence() <= 1.0)
            {
                basegfx::B2DRange aInnerRange(getObjectRange());
                basegfx::B2DRange aOuterRange(getObjectRange());

                // grow/shrink inner/outer polygons
                aOuterRange.grow(getDiscreteUnit() * getDiscreteGrow());
                aInnerRange.grow(getDiscreteUnit() * -getDiscreteShrink());

                // convert to polygons
                const double fFullGrow(getDiscreteGrow() + getDiscreteShrink());
                const double fRelativeRadiusX(fFullGrow / aOuterRange.getWidth());
                const double fRelativeRadiusY(fFullGrow / aOuterRange.getHeight());
                basegfx::B2DPolygon aOuterPolygon(
                    basegfx::tools::createPolygonFromRect(
                        aOuterRange,
                        fRelativeRadiusX,
                        fRelativeRadiusY));
                basegfx::B2DPolygon aInnerPolygon(
                    basegfx::tools::createPolygonFromRect(
                        aInnerRange));

                // apply evtl. existing rotation
                if(!basegfx::fTools::equalZero(getRotation()))
                {
                    const basegfx::B2DHomMatrix aTransform(basegfx::tools::createRotateAroundPoint(
                        getObjectRange().getMinX(), getObjectRange().getMinY(), getRotation()));

                    aOuterPolygon.transform(aTransform);
                    aInnerPolygon.transform(aTransform);
                }

                // create filled primitive
                basegfx::B2DPolyPolygon aPolyPolygon;

                aPolyPolygon.append(aOuterPolygon);
                aPolyPolygon.append(aInnerPolygon);

                if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
                {
                    // for high contrast, use hatch
                    const basegfx::BColor aHighContrastLineColor(Application::GetSettings().GetStyleSettings().GetFontColor().getBColor());
                    const basegfx::BColor aEmptyColor(0.0, 0.0, 0.0);
                    const double fHatchRotation(45 * F_PI180);
                    const double fDiscreteHatchDistance(3.0);
                    const drawinglayer::attribute::FillHatchAttribute aFillHatchAttribute(
                        drawinglayer::attribute::HATCHSTYLE_SINGLE,
                        fDiscreteHatchDistance * getDiscreteUnit(),
                        fHatchRotation - getRotation(),
                        aHighContrastLineColor,
                        3, // same default as VCL, a minimum of three discrete units (pixels) offset
                        false);
                    const Primitive2DReference aHatch(
                        new PolyPolygonHatchPrimitive2D(
                            aPolyPolygon,
                            aEmptyColor,
                            aFillHatchAttribute));

                    aRetval = Primitive2DSequence(&aHatch, 1);
                }
                else
                {
                    // create fill primitive
                    const Primitive2DReference aFill(
                        new PolyPolygonColorPrimitive2D(
                            aPolyPolygon,
                            getColor()));

                    aRetval = Primitive2DSequence(&aFill, 1);

                    // embed filled to transparency (if used)
                    if(getTransparence() > 0.0)
                    {
                        const Primitive2DReference aFillTransparent(
                            new UnifiedTransparencePrimitive2D(
                                aRetval, 
                                getTransparence()));

                        aRetval = Primitive2DSequence(&aFillTransparent, 1);
                    }
                }
            }

            return aRetval;
        }

		bool OverlayRectanglePrimitive::operator==( const BasePrimitive2D& rPrimitive ) const
		{
			if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
			{
				const OverlayRectanglePrimitive& rCompare = static_cast< const OverlayRectanglePrimitive& >(rPrimitive);

				return (getObjectRange() == rCompare.getObjectRange()
					&& getColor() == rCompare.getColor()
                    && getTransparence() == rCompare.getTransparence()
					&& getDiscreteGrow() == rCompare.getDiscreteGrow()
					&& getDiscreteShrink() == rCompare.getDiscreteShrink()
					&& getRotation() == rCompare.getRotation());
			}

			return false;
		}

		ImplPrimitrive2DIDBlock(OverlayRectanglePrimitive, PRIMITIVE2D_ID_OVERLAYRECTANGLEPRIMITIVE)

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

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

namespace drawinglayer
{
	namespace primitive2d
	{
        OverlayHelplineStripedPrimitive::OverlayHelplineStripedPrimitive(
            const basegfx::B2DPoint& rBasePosition,
            HelplineStyle eStyle,
			const basegfx::BColor& rRGBColorA,
			const basegfx::BColor& rRGBColorB,
			double fDiscreteDashLength)
        :   ViewportDependentPrimitive2D(),
			maBasePosition(rBasePosition),
            meStyle(eStyle),
			maRGBColorA(rRGBColorA),
			maRGBColorB(rRGBColorB),
			mfDiscreteDashLength(fDiscreteDashLength)
        {}

		Primitive2DSequence OverlayHelplineStripedPrimitive::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
		{
            // use the prepared Viewport information accessible using getViewport()
            Primitive2DSequence aRetval;

            if(!getViewport().isEmpty())
            {
			    switch(getStyle())
			    {
				    case HELPLINESTYLE_VERTICAL :
				    {
                        aRetval.realloc(1);
                        basegfx::B2DPolygon aLine;

                        aLine.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMinY()));
					    aLine.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMaxY()));

                        aRetval[0] = Primitive2DReference(
                            new PolygonMarkerPrimitive2D(
                                aLine,
                                getRGBColorA(),
                                getRGBColorB(),
                                getDiscreteDashLength()));
					    break;
				    }

				    case HELPLINESTYLE_HORIZONTAL :
				    {
                        aRetval.realloc(1);
                        basegfx::B2DPolygon aLine;

                        aLine.append(basegfx::B2DPoint(getViewport().getMinX(), getBasePosition().getY()));
					    aLine.append(basegfx::B2DPoint(getViewport().getMaxX(), getBasePosition().getY()));
					    
                        aRetval[0] = Primitive2DReference(
                            new PolygonMarkerPrimitive2D(
                                aLine,
                                getRGBColorA(),
                                getRGBColorB(),
                                getDiscreteDashLength()));
					    break;
				    }

                    default: // case HELPLINESTYLE_POINT :
				    {
            			const double fDiscreteUnit((rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)).getLength());
                        aRetval.realloc(2);
                        basegfx::B2DPolygon aLineA, aLineB;
    					
					    aLineA.append(basegfx::B2DPoint(getBasePosition().getX(), getBasePosition().getY() - fDiscreteUnit));
					    aLineA.append(basegfx::B2DPoint(getBasePosition().getX(), getBasePosition().getY() + fDiscreteUnit));
                        
                        aRetval[0] = Primitive2DReference(
                            new PolygonMarkerPrimitive2D(
                                aLineA,
                                getRGBColorA(),
                                getRGBColorB(),
                                getDiscreteDashLength()));

					    aLineB.append(basegfx::B2DPoint(getBasePosition().getX() - fDiscreteUnit, getBasePosition().getY()));
					    aLineB.append(basegfx::B2DPoint(getBasePosition().getX() + fDiscreteUnit, getBasePosition().getY()));
                        
                        aRetval[1] = Primitive2DReference(
                            new PolygonMarkerPrimitive2D(
                                aLineB,
                                getRGBColorA(),
                                getRGBColorB(),
                                getDiscreteDashLength()));

					    break;
				    }
			    }
            }

            return aRetval;
		}

		bool OverlayHelplineStripedPrimitive::operator==( const BasePrimitive2D& rPrimitive ) const
		{
			if(ViewportDependentPrimitive2D::operator==(rPrimitive))
			{
				const OverlayHelplineStripedPrimitive& rCompare = static_cast< const OverlayHelplineStripedPrimitive& >(rPrimitive);

				return (getBasePosition() == rCompare.getBasePosition()
                    && getStyle() == rCompare.getStyle()
					&& getRGBColorA() == rCompare.getRGBColorA()
					&& getRGBColorB() == rCompare.getRGBColorB()
                    && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
			}

			return false;
		}

		ImplPrimitrive2DIDBlock(OverlayHelplineStripedPrimitive, PRIMITIVE2D_ID_OVERLAYHELPLINESTRIPEDPRIMITIVE)

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

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

namespace drawinglayer
{
	namespace primitive2d
	{
        OverlayRollingRectanglePrimitive::OverlayRollingRectanglePrimitive(
            const basegfx::B2DRange& aRollingRectangle,
			const basegfx::BColor& rRGBColorA,
			const basegfx::BColor& rRGBColorB,
			double fDiscreteDashLength)
        :   ViewportDependentPrimitive2D(),
			maRollingRectangle(aRollingRectangle),
			maRGBColorA(rRGBColorA),
			maRGBColorB(rRGBColorB),
			mfDiscreteDashLength(fDiscreteDashLength)
        {}

		Primitive2DSequence OverlayRollingRectanglePrimitive::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
		{
            // use the prepared Viewport information accessible using getViewport()
            Primitive2DSequence aRetval;

            if(!getViewport().isEmpty())
            {
                basegfx::B2DPolygon aLine;
                aRetval.realloc(8);

				// Left lines
                aLine.append(basegfx::B2DPoint(getViewport().getMinX(), getRollingRectangle().getMinY()));
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMinY()));
                aRetval[0] = Primitive2DReference(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
                
                aLine.clear();
                aLine.append(basegfx::B2DPoint(getViewport().getMinX(), getRollingRectangle().getMaxY()));
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMaxY()));
                aRetval[1] = Primitive2DReference(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
				
				// Right lines
                aLine.clear();
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMinY()));
                aLine.append(basegfx::B2DPoint(getViewport().getMaxX(), getRollingRectangle().getMinY()));
                aRetval[2] = Primitive2DReference(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
                
                aLine.clear();
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMaxY()));
                aLine.append(basegfx::B2DPoint(getViewport().getMaxX(), getRollingRectangle().getMaxY()));
                aRetval[3] = Primitive2DReference(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
				
				// Top lines
                aLine.clear();
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getViewport().getMinY()));
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMinY()));
                aRetval[4] = Primitive2DReference(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
                
                aLine.clear();
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getViewport().getMinY()));
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMinY()));
                aRetval[5] = Primitive2DReference(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));

				// Bottom lines
                aLine.clear();
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMaxY()));
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getViewport().getMaxY()));
                aRetval[6] = Primitive2DReference(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
                
                aLine.clear();
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMaxY()));
                aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getViewport().getMaxY()));
                aRetval[7] = Primitive2DReference(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
            }

            return aRetval;
		}

		bool OverlayRollingRectanglePrimitive::operator==( const BasePrimitive2D& rPrimitive ) const
		{
			if(ViewportDependentPrimitive2D::operator==(rPrimitive))
			{
				const OverlayRollingRectanglePrimitive& rCompare = static_cast< const OverlayRollingRectanglePrimitive& >(rPrimitive);

				return (getRollingRectangle() == rCompare.getRollingRectangle()
					&& getRGBColorA() == rCompare.getRGBColorA()
					&& getRGBColorB() == rCompare.getRGBColorB()
                    && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
			}

			return false;
		}

		ImplPrimitrive2DIDBlock(OverlayRollingRectanglePrimitive, PRIMITIVE2D_ID_OVERLAYROLLINGRECTANGLEPRIMITIVE)

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

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