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

#include <tools/debug.hxx>
#include <xmloff/XMLShapeStyleContext.hxx>
#include "XMLShapePropertySetContext.hxx"
#include <xmloff/contextid.hxx>
#include <com/sun/star/drawing/XControlShape.hpp>
#include "com/sun/star/beans/XPropertySetInfo.hpp"
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <xmloff/xmlimp.hxx>
#include <xmloff/xmlnumi.hxx>
#include <xmloff/xmlnmspe.hxx>
#include <xmloff/xmltoken.hxx>
#include "xmloff/xmlerror.hxx"
#include <xmloff/maptype.hxx>

#include "sdpropls.hxx"

using ::rtl::OUString;
using ::rtl::OUStringBuffer;

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::drawing;
using ::xmloff::token::IsXMLToken;
using ::xmloff::token::XML_TEXT_PROPERTIES;
using ::xmloff::token::XML_GRAPHIC_PROPERTIES;
using ::xmloff::token::XML_PARAGRAPH_PROPERTIES;

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

TYPEINIT1( XMLShapeStyleContext, XMLPropStyleContext );

XMLShapeStyleContext::XMLShapeStyleContext(
	SvXMLImport& rImport, 
	sal_uInt16 nPrfx, 
	const OUString& rLName, 
	const uno::Reference< xml::sax::XAttributeList >& xAttrList,
	SvXMLStylesContext& rStyles,
	sal_uInt16 nFamily)
:	XMLPropStyleContext(rImport, nPrfx, rLName, xAttrList, rStyles, nFamily ),
	m_bIsNumRuleAlreadyConverted( sal_False ),
    m_bIsFillStyleAlreadyConverted( sal_False ) //UUUU
{
}

XMLShapeStyleContext::~XMLShapeStyleContext()
{
}

void XMLShapeStyleContext::SetAttribute( sal_uInt16 nPrefixKey, const ::rtl::OUString& rLocalName, const ::rtl::OUString& rValue )
{
	if ((0 == m_sControlDataStyleName.getLength()) && (::xmloff::token::GetXMLToken(::xmloff::token::XML_DATA_STYLE_NAME) == rLocalName))
	{
		m_sControlDataStyleName = rValue;
	}
	else if( (XML_NAMESPACE_STYLE == nPrefixKey) && IsXMLToken( rLocalName, ::xmloff::token::XML_LIST_STYLE_NAME ) )
	{
		m_sListStyleName = rValue;
	}
	else
	{
		XMLPropStyleContext::SetAttribute( nPrefixKey, rLocalName, rValue );

		if( (XML_NAMESPACE_STYLE == nPrefixKey) &&
			( IsXMLToken( rLocalName, ::xmloff::token::XML_NAME ) || IsXMLToken( rLocalName, ::xmloff::token::XML_DISPLAY_NAME ) ) )
		{
			if( GetName().getLength() && GetDisplayName().getLength() && GetName() != GetDisplayName() )
			{
				const_cast< SvXMLImport&>( GetImport() ).
					AddStyleDisplayName( GetFamily(), GetName(), GetDisplayName() );
			}
		}
	}
}

SvXMLImportContext *XMLShapeStyleContext::CreateChildContext(
		sal_uInt16 nPrefix,
		const OUString& rLocalName,
		const Reference< xml::sax::XAttributeList > & xAttrList )
{
	SvXMLImportContext *pContext = 0;

	if( XML_NAMESPACE_STYLE == nPrefix )
	{
		sal_uInt32 nFamily = 0;
		if( IsXMLToken( rLocalName, XML_TEXT_PROPERTIES ) )
			nFamily = XML_TYPE_PROP_TEXT;
		else if( IsXMLToken( rLocalName, XML_PARAGRAPH_PROPERTIES ) )
			nFamily = XML_TYPE_PROP_PARAGRAPH;
		else if( IsXMLToken( rLocalName, XML_GRAPHIC_PROPERTIES ) )
			nFamily = XML_TYPE_PROP_GRAPHIC;
		if( nFamily )
		{
			UniReference < SvXMLImportPropertyMapper > xImpPrMap =
				GetStyles()->GetImportPropertyMapper( GetFamily() );
			if( xImpPrMap.is() )
				pContext = new XMLShapePropertySetContext( GetImport(), nPrefix,
														rLocalName, xAttrList,
														nFamily,
														GetProperties(),
														xImpPrMap );
		}
	}
		
	if( !pContext )
		pContext = XMLPropStyleContext::CreateChildContext( nPrefix, rLocalName,
														  xAttrList );

	return pContext;
}

void XMLShapeStyleContext::FillPropertySet( const Reference< beans::XPropertySet > & rPropSet )
{
	if( !m_bIsNumRuleAlreadyConverted )
	{
		m_bIsNumRuleAlreadyConverted = sal_True;

		// for compatibility to beta files, search for CTF_SD_NUMBERINGRULES_NAME to
		// import numbering rules from the style:properties element
		const UniReference< XMLPropertySetMapper >&rMapper = GetStyles()->GetImportPropertyMapper( GetFamily() )->getPropertySetMapper();

		::std::vector< XMLPropertyState > &rProperties = GetProperties();
		::std::vector< XMLPropertyState >::iterator end( rProperties.end() );
		::std::vector< XMLPropertyState >::iterator property;

		// first, look for the old format, where we had a text:list-style-name
		// attribute in the style:properties element
		for( property = rProperties.begin(); property != end; property++ )
		{
			// find properties with context
			if( (property->mnIndex != -1) && (rMapper->GetEntryContextId( property->mnIndex ) == CTF_SD_NUMBERINGRULES_NAME) )
				break;
		}

		// if we did not find an old list-style-name in the properties, and we need one
		// because we got a style:list-style attribute in the style-style element
		// we generate one
		if( (property == end) && ( 0 != m_sListStyleName.getLength() ) )
		{
			sal_Int32 nIndex = rMapper->FindEntryIndex( CTF_SD_NUMBERINGRULES_NAME );
			DBG_ASSERT( -1 != nIndex, "can't find numbering rules property entry, can't set numbering rule!" );

			XMLPropertyState aNewState( nIndex );
			rProperties.push_back( aNewState );
			end = rProperties.end();
			property = end - 1;
		}

		// so, if we have an old or a new list style name, we set its value to
		// a numbering rule
		if( property != end )
		{
			if( 0 == m_sListStyleName.getLength() )
			{
				property->maValue >>= m_sListStyleName;
			}

			const SvxXMLListStyleContext *pListStyle = GetImport().GetTextImport()->FindAutoListStyle( m_sListStyleName );
			
			DBG_ASSERT( pListStyle, "list-style not found for shape style" );
			if( pListStyle )
			{
				uno::Reference< container::XIndexReplace > xNumRule( pListStyle->CreateNumRule( GetImport().GetModel() ) );
				pListStyle->FillUnoNumRule(xNumRule, NULL /* const SvI18NMap * ??? */ );
				property->maValue <<= xNumRule;
			}
			else
			{
				property->mnIndex = -1;
			}
		}
	}

    if(!m_bIsFillStyleAlreadyConverted && GetProperties().size())
    {
        const UniReference< XMLPropertySetMapper >&rMapper = GetStyles()->GetImportPropertyMapper(GetFamily())->getPropertySetMapper();
        ::std::vector< XMLPropertyState >& rProperties = GetProperties();
        ::std::vector< XMLPropertyState >::iterator a;
        FillStyle eFS(FillStyle_NONE);
        static ::rtl::OUString s_FillStyle(RTL_CONSTASCII_USTRINGPARAM("FillStyle"));

        // try to find a FillStyle entry and a value from it
        for(a = rProperties.begin(); a != rProperties.end(); a++)
        {
            if(a->mnIndex != -1)
            {
                const OUString& rPropName = rMapper->GetEntryAPIName(a->mnIndex);

                if(rPropName == s_FillStyle)
                {
                    if(a->maValue >>= eFS)
                    {
                        // okay, type was good, eFS is set
                    }
                    else
                    {
                        // also try an int (see XFillStyleItem::PutValue)
                        sal_Int32 nFS = 0;

                        if(a->maValue >>= nFS)
                        {
                            eFS = (FillStyle)nFS;
                        }
                    }

                    // exit loop, we found out what we needed to know
                    break;
                }
            }
        }

        if(FillStyle_NONE != eFS)
        {
            //UUUU a FillStyle was found, thus the new [XATTR_FILL_FIRST .. XATTR_FILL_LAST]
            // description for the Fill definitions is used. All formally used props based
            // on RES_BACKGROUND need to be deleted to get no conflicts between the two
            // sets of properties; old files will keep these and adapt accordingly
            static ::rtl::OUString s_BackColorRGB(RTL_CONSTASCII_USTRINGPARAM("BackColorRGB"));
            static ::rtl::OUString s_BackTransparent(RTL_CONSTASCII_USTRINGPARAM("BackTransparent"));
            static ::rtl::OUString s_BackColorTransparency(RTL_CONSTASCII_USTRINGPARAM("BackColorTransparency"));
            static ::rtl::OUString s_BackGraphicURL(RTL_CONSTASCII_USTRINGPARAM("BackGraphicURL"));
            static ::rtl::OUString s_BackGraphicFilter(RTL_CONSTASCII_USTRINGPARAM("BackGraphicFilter"));
            static ::rtl::OUString s_BackGraphicLocation(RTL_CONSTASCII_USTRINGPARAM("BackGraphicLocation"));
            static ::rtl::OUString s_BackGraphicTransparency(RTL_CONSTASCII_USTRINGPARAM("BackGraphicTransparency"));

            for(a = rProperties.begin(); a != rProperties.end(); a++)
            {
                if(a->mnIndex != -1)
                {
                    const OUString& rPropName = rMapper->GetEntryAPIName(a->mnIndex);

                    if(s_BackColorRGB == rPropName
                        || s_BackTransparent == rPropName
                        || s_BackColorTransparency == rPropName
                        || s_BackGraphicURL == rPropName
                        || s_BackGraphicFilter == rPropName
                        || s_BackGraphicLocation == rPropName
                        || s_BackGraphicTransparency== rPropName)
                    {
                        // mark entry as inactive
                        a->mnIndex = -1;
                    }
                }
            }
        }

        m_bIsFillStyleAlreadyConverted = sal_True;
    }

	struct _ContextID_Index_Pair aContextIDs[] =
	{
		{ CTF_DASHNAME , -1 },
		{ CTF_LINESTARTNAME , -1 },
		{ CTF_LINEENDNAME , -1 },
		{ CTF_FILLGRADIENTNAME, -1 },
		{ CTF_FILLTRANSNAME , -1 },
		{ CTF_FILLHATCHNAME , -1 },
		{ CTF_FILLBITMAPNAME , -1 },
		{ CTF_SD_OLE_VIS_AREA_IMPORT_LEFT, -1 },
		{ CTF_SD_OLE_VIS_AREA_IMPORT_TOP, -1 },
		{ CTF_SD_OLE_VIS_AREA_IMPORT_WIDTH, -1 },
		{ CTF_SD_OLE_VIS_AREA_IMPORT_HEIGHT, -1 },
		{ -1, -1 }
	};
	static sal_uInt16 aFamilies[] =
	{
		XML_STYLE_FAMILY_SD_STROKE_DASH_ID,
		XML_STYLE_FAMILY_SD_MARKER_ID,
		XML_STYLE_FAMILY_SD_MARKER_ID,
		XML_STYLE_FAMILY_SD_GRADIENT_ID,
		XML_STYLE_FAMILY_SD_GRADIENT_ID,
		XML_STYLE_FAMILY_SD_HATCH_ID,
		XML_STYLE_FAMILY_SD_FILL_IMAGE_ID
	};

	UniReference < SvXMLImportPropertyMapper > xImpPrMap =
		GetStyles()->GetImportPropertyMapper( GetFamily() );
	DBG_ASSERT( xImpPrMap.is(), "There is the import prop mapper" );
	if( xImpPrMap.is() )
		xImpPrMap->FillPropertySet( GetProperties(), rPropSet, aContextIDs );

	Reference< XPropertySetInfo > xInfo;
	// get property set mapper
	UniReference<XMLPropertySetMapper> xPropMapper(	xImpPrMap->getPropertySetMapper() );

	for( sal_uInt16 i=0; aContextIDs[i].nContextID != -1; i++ )
	{
		sal_Int32 nIndex = aContextIDs[i].nIndex;
		if( nIndex != -1 ) switch( aContextIDs[i].nContextID )
		{
		case CTF_DASHNAME:
		case CTF_LINESTARTNAME:
		case CTF_LINEENDNAME:
		case CTF_FILLGRADIENTNAME:
		case CTF_FILLTRANSNAME:
		case CTF_FILLHATCHNAME:
		case CTF_FILLBITMAPNAME:
		{
			struct XMLPropertyState& rState = GetProperties()[nIndex];
			OUString sStyleName;
			rState.maValue >>= sStyleName;
			sStyleName = GetImport().GetStyleDisplayName( aFamilies[i], sStyleName );
			try
			{

				// set property
				const OUString& rPropertyName =	xPropMapper->GetEntryAPIName(rState.mnIndex);
				if( !xInfo.is() )
					xInfo = rPropSet->getPropertySetInfo();
				if ( xInfo->hasPropertyByName( rPropertyName ) )
				{
					rPropSet->setPropertyValue( rPropertyName, Any( sStyleName ) );
				}
			}
			catch ( ::com::sun::star::lang::IllegalArgumentException& e )
			{
				Sequence<OUString> aSeq(1);
				aSeq[0] = sStyleName;
				GetImport().SetError(
					XMLERROR_STYLE_PROP_VALUE | XMLERROR_FLAG_WARNING,
					aSeq, e.Message, NULL );
			}
			break;
		}
		case CTF_SD_OLE_VIS_AREA_IMPORT_LEFT:
		case CTF_SD_OLE_VIS_AREA_IMPORT_TOP:
		case CTF_SD_OLE_VIS_AREA_IMPORT_WIDTH:
		case CTF_SD_OLE_VIS_AREA_IMPORT_HEIGHT:
		{
			struct XMLPropertyState& rState = GetProperties()[nIndex];
			const OUString& rPropertyName =	xPropMapper->GetEntryAPIName(rState.mnIndex);
			try
			{
				if( !xInfo.is() )
					xInfo = rPropSet->getPropertySetInfo();
				if ( xInfo->hasPropertyByName( rPropertyName ) )
				{
					rPropSet->setPropertyValue( rPropertyName, rState.maValue );
				}
			}
			catch ( ::com::sun::star::lang::IllegalArgumentException& e )
			{
				Sequence<OUString> aSeq;
				GetImport().SetError(
					XMLERROR_STYLE_PROP_VALUE | XMLERROR_FLAG_WARNING,
					aSeq, e.Message, NULL );
			}
			break;
		}
		}
	}

	if (m_sControlDataStyleName.getLength())
	{	// we had a data-style-name attribute

		// set the formatting on the control model of the control shape
		uno::Reference< drawing::XControlShape > xControlShape(rPropSet, uno::UNO_QUERY);
		DBG_ASSERT(xControlShape.is(), "XMLShapeStyleContext::FillPropertySet: data style for a non-control shape!");
		if (xControlShape.is())
		{
			uno::Reference< beans::XPropertySet > xControlModel(xControlShape->getControl(), uno::UNO_QUERY);
			DBG_ASSERT(xControlModel.is(), "XMLShapeStyleContext::FillPropertySet: no control model for the shape!");
			if (xControlModel.is())
			{
				GetImport().GetFormImport()->applyControlNumberStyle(xControlModel, m_sControlDataStyleName);
			}
		}
	}
}

void XMLShapeStyleContext::Finish( sal_Bool /*bOverwrite*/ )
{
}