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

#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <drawinglayer/attribute/strokeattribute.hxx>
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <drawinglayer/primitive2d/texteffectprimitive2d.hxx>
#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/primitive2d/textlineprimitive2d.hxx>
#include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx>
#include <drawinglayer/primitive2d/textbreakuphelper.hxx>

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

namespace drawinglayer
{
	namespace primitive2d
	{
        void TextDecoratedPortionPrimitive2D::impCreateGeometryContent(
            std::vector< Primitive2DReference >& rTarget,
            basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose& rDecTrans,
            const String& rText,
			xub_StrLen aTextPosition,
			xub_StrLen aTextLength,
            const ::std::vector< double >& rDXArray,
            const attribute::FontAttribute& rFontAttribute) const
        {
            // create the SimpleTextPrimitive needed in any case
	        rTarget.push_back(Primitive2DReference(
                new TextSimplePortionPrimitive2D(
                    rDecTrans.getB2DHomMatrix(),
                    rText,
				    aTextPosition,
				    aTextLength,
                    rDXArray,
                    rFontAttribute,
                    getLocale(),
                    getFontColor())));

			// see if something else needs to be done
            const bool bOverlineUsed(TEXT_LINE_NONE != getFontOverline());
            const bool bUnderlineUsed(TEXT_LINE_NONE != getFontUnderline());
			const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE != getTextStrikeout());

            if(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed)
			{
				// common preparations
			    TextLayouterDevice aTextLayouter;

                // TextLayouterDevice is needed to get metrics for text decorations like
                // underline/strikeout/emphasis marks from it. For setup, the font size is needed
			    aTextLayouter.setFontAttribute(
                    getFontAttribute(), 
                    rDecTrans.getScale().getX(), 
                    rDecTrans.getScale().getY(),
                    getLocale());

				// get text width
				double fTextWidth(0.0);

				if(rDXArray.empty())
				{
					fTextWidth = aTextLayouter.getTextWidth(rText, aTextPosition, aTextLength);
				}
				else
				{
					fTextWidth = rDXArray.back() * rDecTrans.getScale().getX();
                    const double fFontScaleX(rDecTrans.getScale().getX());
                    
                    if(!basegfx::fTools::equal(fFontScaleX, 1.0) 
                        && !basegfx::fTools::equalZero(fFontScaleX))
                    {
                        // need to take FontScaling out of the DXArray
                        fTextWidth /= fFontScaleX;
                    }
				}

                if(bOverlineUsed)
                {
                    // create primitive geometry for overline
				    rTarget.push_back(Primitive2DReference(
                        new TextLinePrimitive2D(
                            rDecTrans.getB2DHomMatrix(),
                            fTextWidth,
                            aTextLayouter.getOverlineOffset(),
                            aTextLayouter.getOverlineHeight(),
                            getFontOverline(),
                            getOverlineColor())));
                }

				if(bUnderlineUsed)
				{
					// create primitive geometry for underline
				    rTarget.push_back(Primitive2DReference(
                        new TextLinePrimitive2D(
                            rDecTrans.getB2DHomMatrix(),
                            fTextWidth,
                            aTextLayouter.getUnderlineOffset(),
                            aTextLayouter.getUnderlineHeight(),
                            getFontUnderline(),
                            getTextlineColor())));
                }

				if(bStrikeoutUsed)
				{
					// create primitive geometry for strikeout
                    if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || TEXT_STRIKEOUT_X == getTextStrikeout())
                    {
                        // strikeout with character
                        const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X');

                        rTarget.push_back(Primitive2DReference(
                            new TextCharacterStrikeoutPrimitive2D(
                                rDecTrans.getB2DHomMatrix(),
                                fTextWidth,
                                getFontColor(),
                                aStrikeoutChar,
                                getFontAttribute(),
                                getLocale())));
                    }
                    else
                    {
                        // strikeout with geometry
                        rTarget.push_back(Primitive2DReference(
                            new TextGeometryStrikeoutPrimitive2D(
                                rDecTrans.getB2DHomMatrix(),
                                fTextWidth,
                                getFontColor(),
                                aTextLayouter.getUnderlineHeight(),
                                aTextLayouter.getStrikeoutOffset(),
                                getTextStrikeout())));
                    }
				}
			}

            // TODO: Handle Font Emphasis Above/Below
        }

		Primitive2DSequence TextDecoratedPortionPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
        {
            if(getWordLineMode())
            {
                // support for single word mode; split to single word primitives
                // using TextBreakupHelper
                const TextBreakupHelper aTextBreakupHelper(*this);
                const Primitive2DSequence aBroken(aTextBreakupHelper.getResult(BreakupUnit_word));

                if(aBroken.hasElements())
                {
                    // was indeed split to several words, use as result
                    return aBroken;
                }
                else
                {
                    // no split, was already a single word. Continue to
                    // decompse local entity
                }
            }
            std::vector< Primitive2DReference > aNewPrimitives;
            basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform());
            Primitive2DSequence aRetval;

            // create basic geometry such as SimpleTextPrimitive, Overline, Underline,
            // Strikeout, etc...
            // prepare new font attributes WITHOUT outline
            const attribute::FontAttribute aNewFontAttribute(
                getFontAttribute().getFamilyName(),
                getFontAttribute().getStyleName(),
                getFontAttribute().getWeight(),
                getFontAttribute().getSymbol(),
                getFontAttribute().getVertical(),
                getFontAttribute().getItalic(),
                false,             // no outline anymore, handled locally
                getFontAttribute().getRTL(),
                getFontAttribute().getBiDiStrong());

			// handle as one word
            impCreateGeometryContent(aNewPrimitives, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute);

            // convert to Primitive2DSequence
            const sal_uInt32 nMemberCount(aNewPrimitives.size());

			if(nMemberCount)
            {
                aRetval.realloc(nMemberCount);

                for(sal_uInt32 a(0); a < nMemberCount; a++)
                {
                    aRetval[a] = aNewPrimitives[a];
                }
            }

            // Handle Shadow, Outline and TextRelief
            if(aRetval.hasElements())
            {
                // outline AND shadow depend on NO TextRelief (see dialog)
                const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief());
                const bool bHasShadow(!bHasTextRelief && getShadow());
                const bool bHasOutline(!bHasTextRelief && getFontAttribute().getOutline());

                if(bHasShadow || bHasTextRelief || bHasOutline)
                {
                    Primitive2DReference aShadow;

                    if(bHasShadow)
                    {
                        // create shadow with current content (in aRetval). Text shadow
                        // is constant, relative to font size, rotated with the text and has a
                        // constant color.
                        // shadow parameter values
                        static double fFactor(1.0 / 24.0);
                        const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor);
                        static basegfx::BColor aShadowColor(0.3, 0.3, 0.3);

                        // preapare shadow transform matrix
                        const basegfx::B2DHomMatrix aShadowTransform(basegfx::tools::createTranslateB2DHomMatrix(
                            fTextShadowOffset, fTextShadowOffset));

                        // create shadow primitive
                        aShadow = Primitive2DReference(new ShadowPrimitive2D(
                            aShadowTransform,
                            aShadowColor,
                            aRetval));
                    }

                    if(bHasTextRelief)
                    {
                        // create emboss using an own helper primitive since this will
                        // be view-dependent
						const basegfx::BColor aBBlack(0.0, 0.0, 0.0);
						const bool bDefaultTextColor(aBBlack == getFontColor());
						TextEffectStyle2D aTextEffectStyle2D(TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED);

						if(bDefaultTextColor)
						{
							if(TEXT_RELIEF_ENGRAVED == getTextRelief())
							{
								aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED_DEFAULT;
							}
							else
							{
								aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED_DEFAULT;
							}
						}
						else
						{
							if(TEXT_RELIEF_ENGRAVED == getTextRelief())
							{
								aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED;
							}
							else
							{
								aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED;
							}
						}

						Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
							aRetval,
							aDecTrans.getTranslate(),
							aDecTrans.getRotate(),
                            aTextEffectStyle2D));
                        aRetval = Primitive2DSequence(&aNewTextEffect, 1);
                    }
                    else if(bHasOutline)
                    {
                        // create outline using an own helper primitive since this will
                        // be view-dependent
                        Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
							aRetval,
							aDecTrans.getTranslate(),
							aDecTrans.getRotate(),
							TEXTEFFECTSTYLE2D_OUTLINE));
                        aRetval = Primitive2DSequence(&aNewTextEffect, 1);
                    }

                    if(aShadow.is())
                    {
                        // put shadow in front if there is one to paint timely before
                        // but placed behind content
                        const Primitive2DSequence aContent(aRetval);
                        aRetval = Primitive2DSequence(&aShadow, 1);
                        appendPrimitive2DSequenceToPrimitive2DSequence(aRetval, aContent);
                    }
                }
            }

            return aRetval;
        }

        TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D(

            // TextSimplePortionPrimitive2D parameters
			const basegfx::B2DHomMatrix& rNewTransform,
			const String& rText,
			xub_StrLen aTextPosition,
			xub_StrLen aTextLength,
			const ::std::vector< double >& rDXArray,
            const attribute::FontAttribute& rFontAttribute,
            const ::com::sun::star::lang::Locale& rLocale,
			const basegfx::BColor& rFontColor,

            // local parameters
            const basegfx::BColor& rOverlineColor,
            const basegfx::BColor& rTextlineColor,
            TextLine eFontOverline,
            TextLine eFontUnderline,
			bool bUnderlineAbove,
			TextStrikeout eTextStrikeout,
			bool bWordLineMode,
			TextEmphasisMark eTextEmphasisMark,
			bool bEmphasisMarkAbove,
			bool bEmphasisMarkBelow,
			TextRelief eTextRelief,
			bool bShadow)
		:	TextSimplePortionPrimitive2D(rNewTransform, rText, aTextPosition, aTextLength, rDXArray, rFontAttribute, rLocale, rFontColor),
            maOverlineColor(rOverlineColor),
            maTextlineColor(rTextlineColor),
            meFontOverline(eFontOverline),
            meFontUnderline(eFontUnderline),
			meTextStrikeout(eTextStrikeout),
			meTextEmphasisMark(eTextEmphasisMark),
			meTextRelief(eTextRelief),
			mbUnderlineAbove(bUnderlineAbove),
			mbWordLineMode(bWordLineMode),
			mbEmphasisMarkAbove(bEmphasisMarkAbove),
			mbEmphasisMarkBelow(bEmphasisMarkBelow),
			mbShadow(bShadow)
		{
		}

        bool TextDecoratedPortionPrimitive2D::decoratedIsNeeded() const
        {
            return (TEXT_LINE_NONE != getFontOverline()
                 || TEXT_LINE_NONE != getFontUnderline()
                 || TEXT_STRIKEOUT_NONE != getTextStrikeout()
                 || TEXT_EMPHASISMARK_NONE != getTextEmphasisMark()
                 || TEXT_RELIEF_NONE != getTextRelief()
                 || getShadow());
        }

		bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
		{
			if(TextSimplePortionPrimitive2D::operator==(rPrimitive))
			{
				const TextDecoratedPortionPrimitive2D& rCompare = (TextDecoratedPortionPrimitive2D&)rPrimitive;

                return (getOverlineColor() == rCompare.getOverlineColor()
                    && getTextlineColor() == rCompare.getTextlineColor()
                    && getFontOverline() == rCompare.getFontOverline()
                    && getFontUnderline() == rCompare.getFontUnderline()
					&& getTextStrikeout() == rCompare.getTextStrikeout()
					&& getTextEmphasisMark() == rCompare.getTextEmphasisMark()
					&& getTextRelief() == rCompare.getTextRelief()
					&& getUnderlineAbove() == rCompare.getUnderlineAbove()
					&& getWordLineMode() == rCompare.getWordLineMode()
					&& getEmphasisMarkAbove() == rCompare.getEmphasisMarkAbove()
					&& getEmphasisMarkBelow() == rCompare.getEmphasisMarkBelow()
					&& getShadow() == rCompare.getShadow());
			}

			return false;
		}

        // #i96475#
        // Added missing implementation. Decorations may (will) stick out of the text's
        // inking area, so add them if needed
		basegfx::B2DRange TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
		{
            if(decoratedIsNeeded())
            {
                // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses
                // the own local decomposition for computation and thus creates all necessary
                // geometric objects
                return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
            }
            else
            {
                // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
                return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation);
            }
        }

		// provide unique ID
		ImplPrimitrive2DIDBlock(TextDecoratedPortionPrimitive2D, PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D)

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

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