/**************************************************************
 * 
 * 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 <bordrhdl.hxx>
#include <xmloff/xmltoken.hxx>
#include <xmloff/xmluconv.hxx>
#include <rtl/ustrbuf.hxx>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/table/BorderLine.hpp>

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

using namespace ::com::sun::star;
using namespace ::xmloff::token;

// copied from svx/boxitem.hxx
#define DEF_LINE_WIDTH_0        1
#define DEF_LINE_WIDTH_1        35
#define DEF_LINE_WIDTH_2        88
#define DEF_LINE_WIDTH_3        141
#define DEF_LINE_WIDTH_4        176

#define DEF_MAX_LINE_WIDHT      DEF_LINE_WIDTH_4
#define DEF_MAX_LINE_DIST       DEF_LINE_WIDTH_2

#define DEF_DOUBLE_LINE0_OUT    DEF_LINE_WIDTH_0
#define DEF_DOUBLE_LINE0_IN     DEF_LINE_WIDTH_0
#define DEF_DOUBLE_LINE0_DIST   DEF_LINE_WIDTH_1

#define DEF_DOUBLE_LINE1_OUT    DEF_LINE_WIDTH_1
#define DEF_DOUBLE_LINE1_IN     DEF_LINE_WIDTH_1
#define DEF_DOUBLE_LINE1_DIST   DEF_LINE_WIDTH_1

#define DEF_DOUBLE_LINE2_OUT    DEF_LINE_WIDTH_2
#define DEF_DOUBLE_LINE2_IN     DEF_LINE_WIDTH_2
#define DEF_DOUBLE_LINE2_DIST   DEF_LINE_WIDTH_2

#define DEF_DOUBLE_LINE3_OUT    DEF_LINE_WIDTH_2
#define DEF_DOUBLE_LINE3_IN     DEF_LINE_WIDTH_1
#define DEF_DOUBLE_LINE3_DIST   DEF_LINE_WIDTH_2

#define DEF_DOUBLE_LINE4_OUT    DEF_LINE_WIDTH_1
#define DEF_DOUBLE_LINE4_IN     DEF_LINE_WIDTH_2
#define DEF_DOUBLE_LINE4_DIST   DEF_LINE_WIDTH_1

#define DEF_DOUBLE_LINE5_OUT    DEF_LINE_WIDTH_3
#define DEF_DOUBLE_LINE5_IN     DEF_LINE_WIDTH_2
#define DEF_DOUBLE_LINE5_DIST   DEF_LINE_WIDTH_2

#define DEF_DOUBLE_LINE6_OUT    DEF_LINE_WIDTH_2
#define DEF_DOUBLE_LINE6_IN     DEF_LINE_WIDTH_3
#define DEF_DOUBLE_LINE6_DIST   DEF_LINE_WIDTH_2

#define DEF_DOUBLE_LINE7_OUT    DEF_LINE_WIDTH_0
#define DEF_DOUBLE_LINE7_IN     DEF_LINE_WIDTH_0
#define DEF_DOUBLE_LINE7_DIST   DEF_LINE_WIDTH_2

#define DEF_DOUBLE_LINE8_OUT    DEF_LINE_WIDTH_1
#define DEF_DOUBLE_LINE8_IN     DEF_LINE_WIDTH_0
#define DEF_DOUBLE_LINE8_DIST   DEF_LINE_WIDTH_2

#define DEF_DOUBLE_LINE9_OUT    DEF_LINE_WIDTH_2
#define DEF_DOUBLE_LINE9_IN     DEF_LINE_WIDTH_0
#define DEF_DOUBLE_LINE9_DIST   DEF_LINE_WIDTH_2

#define DEF_DOUBLE_LINE10_OUT	DEF_LINE_WIDTH_3
#define DEF_DOUBLE_LINE10_IN	DEF_LINE_WIDTH_0
#define DEF_DOUBLE_LINE10_DIST	DEF_LINE_WIDTH_2

// finished copy

#define SVX_XML_BORDER_STYLE_NONE 0
#define SVX_XML_BORDER_STYLE_SOLID 1
#define SVX_XML_BORDER_STYLE_DOUBLE 2

#define SVX_XML_BORDER_WIDTH_THIN 0
#define SVX_XML_BORDER_WIDTH_MIDDLE 1
#define SVX_XML_BORDER_WIDTH_THICK 2

SvXMLEnumMapEntry pXML_BorderStyles[] =
{
	{ XML_NONE,				    SVX_XML_BORDER_STYLE_NONE	},
	{ XML_HIDDEN, 				SVX_XML_BORDER_STYLE_NONE	},
	{ XML_SOLID, 				SVX_XML_BORDER_STYLE_SOLID	},
	{ XML_DOUBLE,				SVX_XML_BORDER_STYLE_DOUBLE	},
	{ XML_DOTTED, 				SVX_XML_BORDER_STYLE_SOLID  },
	{ XML_DASHED, 				SVX_XML_BORDER_STYLE_SOLID	},
	{ XML_GROOVE, 				SVX_XML_BORDER_STYLE_SOLID	},
	{ XML_RIDGE,  				SVX_XML_BORDER_STYLE_SOLID  },
	{ XML_INSET,  				SVX_XML_BORDER_STYLE_SOLID  },
	{ XML_OUTSET, 				SVX_XML_BORDER_STYLE_SOLID	},
	{ XML_TOKEN_INVALID,        0 }
};

SvXMLEnumMapEntry pXML_NamedBorderWidths[] =
{
	{ XML_THIN,	            SVX_XML_BORDER_WIDTH_THIN	},
	{ XML_MIDDLE,	        SVX_XML_BORDER_WIDTH_MIDDLE	},
	{ XML_THICK,	        SVX_XML_BORDER_WIDTH_THICK	},
	{ XML_TOKEN_INVALID,    0 }
};
// mapping tables to map external xml input to intarnal box line widths

// Ein Eintrag besteht aus vier USHORTs. Der erste ist die Gesamtbreite,
// die anderen sind die 3 Einzelbreiten

#define SBORDER_ENTRY( n ) \
	DEF_LINE_WIDTH_##n, DEF_LINE_WIDTH_##n, 0, 0

#define DBORDER_ENTRY( n ) \
	DEF_DOUBLE_LINE##n##_OUT + DEF_DOUBLE_LINE##n##_IN + \
	DEF_DOUBLE_LINE##n##_DIST, \
	DEF_DOUBLE_LINE##n##_OUT, \
	DEF_DOUBLE_LINE##n##_IN, \
	DEF_DOUBLE_LINE##n##_DIST

#define TDBORDER_ENTRY( n ) \
	DEF_DOUBLE_LINE##n##_OUT, \
	DEF_DOUBLE_LINE##n##_OUT, \
	DEF_DOUBLE_LINE##n##_IN, \
	DEF_DOUBLE_LINE##n##_DIST


static sal_uInt16 __READONLY_DATA aSBorderWidths[] =
{
	SBORDER_ENTRY( 0 ), SBORDER_ENTRY( 1 ), SBORDER_ENTRY( 2 ),
	SBORDER_ENTRY( 3 ), SBORDER_ENTRY( 4 )
};

static sal_uInt16 __READONLY_DATA aDBorderWidths[] =
{
	DBORDER_ENTRY( 0 ),
	DBORDER_ENTRY( 7 ),
	DBORDER_ENTRY( 1 ),
	DBORDER_ENTRY( 8 ),
	DBORDER_ENTRY( 4 ),
	DBORDER_ENTRY( 9 ),
	DBORDER_ENTRY( 3 ),
	DBORDER_ENTRY( 10 ),
	DBORDER_ENTRY( 2 ),
	DBORDER_ENTRY( 6 ),
	DBORDER_ENTRY( 5 )
};

void lcl_frmitems_setXMLBorderWidth( table::BorderLine &rBorderLine,
									 sal_uInt16 nWidth, sal_Bool bDouble )
{
#ifdef XML_CHECK_UI_CONTSTRAINS
	const sal_uInt16 *aWidths;
	sal_uInt16 nSize;
	if( !bDouble )
	{
		aWidths = aSBorderWidths;
		nSize = sizeof( aSBorderWidths );
	}
	else
	{
		aWidths = aDBorderWidths;
		nSize = sizeof( aDBorderWidths );
	}

	sal_uInt16 i = (nSize / sizeof(sal_uInt16)) - 4;
	while( i>0 &&
		   nWidth <= ((aWidths[i] + aWidths[i-4]) / 2)	)
	{
		i -= 4;
	}

	rBorderLine.OuterLineWidth = aWidths[i+1];
	rBorderLine.InnerLineWidth = aWidths[i+2];
	rBorderLine.LineDistance = aWidths[i+3];
#else
	if( bDouble )
	{
		const sal_uInt16 *aWidths = aDBorderWidths;
		sal_uInt16 nSize = sizeof( aDBorderWidths );
		sal_uInt16 i = (nSize / sizeof(sal_uInt16)) - 4;
		while( i>0 &&
			   nWidth <= ((aWidths[i] + aWidths[i-4]) / 2)	)
		{
			i -= 4;
		}

		rBorderLine.OuterLineWidth = aWidths[i+1];
		rBorderLine.InnerLineWidth = aWidths[i+2];
		rBorderLine.LineDistance = aWidths[i+3];
	}
	else
	{
		rBorderLine.OuterLineWidth = 0 == nWidth ? DEF_LINE_WIDTH_0 : nWidth; 
		rBorderLine.InnerLineWidth = 0;
		rBorderLine.LineDistance = 0;

	}
#endif
}


///////////////////////////////////////////////////////////////////////////////
//
// class XMLEscapementPropHdl
//

XMLBorderWidthHdl::~XMLBorderWidthHdl()
{
	// nothing to do
}

sal_Bool XMLBorderWidthHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const
{ 
	SvXMLTokenEnumerator aTokenEnum( rStrImpValue );

	sal_Int32 nInWidth, nDistance, nOutWidth;

	OUString aToken;
	if( !aTokenEnum.getNextToken( aToken ) )
		return sal_False;

	if( !rUnitConverter.convertMeasure( nInWidth, aToken, 0, 500 ) )
		return sal_False;

	if( !aTokenEnum.getNextToken( aToken ) )
		return sal_False;

	if( !rUnitConverter.convertMeasure( nDistance, aToken, 0, 500 ) )
		return sal_False;

	if( !aTokenEnum.getNextToken( aToken ) )
		return sal_False;

	if( !rUnitConverter.convertMeasure( nOutWidth, aToken, 0, 500 ) )
		return sal_False;

#ifdef XML_CHECK_UI_CONSTRAINS
	sal_uInt16 nSize = sizeof( aDBorderWidths );
	for( sal_uInt16 i=0; i < nSize; i += 4 )
	{
		if( aDBorderWidths[i+1] == nOutWidth &&
			aDBorderWidths[i+2] == nInWidth &&
			aDBorderWidths[i+3] == nDistance )
			break;
	}

	sal_uInt16 nWidth = i < nSize ? 0 : nOutWidth + nInWidth + nDistance;
#endif

	table::BorderLine aBorderLine;
	if(!(rValue >>= aBorderLine))
		aBorderLine.Color = 0;

	aBorderLine.InnerLineWidth = sal::static_int_cast< sal_Int16 >(nInWidth);
	aBorderLine.OuterLineWidth = sal::static_int_cast< sal_Int16 >(nOutWidth);
	aBorderLine.LineDistance   = sal::static_int_cast< sal_Int16 >(nDistance);

	rValue <<= aBorderLine;
	return sal_True; 
}

sal_Bool XMLBorderWidthHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const
{ 
	OUStringBuffer aOut;

	table::BorderLine aBorderLine;
	if(!(rValue >>= aBorderLine))
		return sal_False;

	if( aBorderLine.LineDistance == 0 && aBorderLine.InnerLineWidth == 0)
		return sal_False;

	rUnitConverter.convertMeasure( aOut, aBorderLine.InnerLineWidth );
	aOut.append( sal_Unicode( ' ' ) );
	rUnitConverter.convertMeasure( aOut, aBorderLine.LineDistance );
	aOut.append( sal_Unicode( ' ' ) );
	rUnitConverter.convertMeasure( aOut, aBorderLine.OuterLineWidth );

	rStrExpValue = aOut.makeStringAndClear();
	return sal_True;
}

///////////////////////////////////////////////////////////////////////////////
//
// class XMLEscapementHeightPropHdl
//

XMLBorderHdl::~XMLBorderHdl()
{
	// nothing to do
}

sal_Bool XMLBorderHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const
{ 
	OUString aToken;
	SvXMLTokenEnumerator aTokens( rStrImpValue );

	sal_Bool bHasStyle = sal_False;
	sal_Bool bHasWidth = sal_False;
	sal_Bool bHasColor = sal_False;

	sal_uInt16 nStyle = USHRT_MAX;
	sal_uInt16 nWidth = 0;
	sal_uInt16 nNamedWidth = USHRT_MAX;
	Color aColor;

	sal_Int32 nTemp;
	while( aTokens.getNextToken( aToken ) && aToken.getLength() != 0 )
	{
		if( !bHasWidth &&
			rUnitConverter.convertEnum( nNamedWidth, aToken,
										pXML_NamedBorderWidths ) )
		{
			bHasWidth = sal_True;
		}
		else if( !bHasStyle &&
			     rUnitConverter.convertEnum( nStyle, aToken,
						 					 pXML_BorderStyles ) )
		{
			bHasStyle = sal_True;
		}
		else if( !bHasColor && rUnitConverter.convertColor( aColor, aToken ) )
		{
			bHasColor = sal_True;
		}
		else if( !bHasWidth &&
				 rUnitConverter.convertMeasure( nTemp, aToken, 0,
						 						USHRT_MAX ) )
		{
			nWidth = (sal_uInt16)nTemp;
			bHasWidth = sal_True;
		}
		else
		{
			// missformed
			return sal_False;
		}
	}

	// if there is no style or a different style than none but no width,
   	// then the declaration is not valid.
	if( !bHasStyle || (SVX_XML_BORDER_STYLE_NONE != nStyle && !bHasWidth) )
		return sal_False;

	table::BorderLine aBorderLine;
	if(!(rValue >>= aBorderLine))
	{
		aBorderLine.Color = 0;
		aBorderLine.InnerLineWidth = 0;
		aBorderLine.OuterLineWidth = 0;
		aBorderLine.LineDistance   = 0;
	}

	// first of all, delete an empty line
	sal_Bool bDouble = SVX_XML_BORDER_STYLE_DOUBLE == nStyle;
	if( (bHasStyle && SVX_XML_BORDER_STYLE_NONE == nStyle) ||
		(bHasWidth && USHRT_MAX == nNamedWidth && 0 == nWidth) )
	{
		aBorderLine.InnerLineWidth = 0;
		aBorderLine.OuterLineWidth = 0;
		aBorderLine.LineDistance   = 0;
	}
	else if( bHasWidth )
	{
		if( USHRT_MAX != nNamedWidth )
		{
			const sal_uInt16 *aWidths = bDouble ? aDBorderWidths
										    : aSBorderWidths;
			sal_uInt16 nNWidth = nNamedWidth * 4;
			aBorderLine.OuterLineWidth = aWidths[nNWidth+1];
			aBorderLine.InnerLineWidth = aWidths[nNWidth+2];
			aBorderLine.LineDistance = aWidths[nNWidth+3];
		}
		else
		{
			lcl_frmitems_setXMLBorderWidth( aBorderLine, nWidth, bDouble );
		}
	}
	else
	{
		lcl_frmitems_setXMLBorderWidth( aBorderLine, 0, bDouble );
	}

	// set color
	if( bHasColor )
		aBorderLine.Color = (sal_Int32)aColor.GetRGBColor();

	rValue <<= aBorderLine;
	return sal_True;
}

sal_Bool XMLBorderHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const
{ 
	OUStringBuffer aOut;

	table::BorderLine aBorderLine;
	if(!(rValue >>= aBorderLine))
		return sal_False;

	sal_Int32 nWidth = aBorderLine.OuterLineWidth;
	const sal_uInt16 nDistance = aBorderLine.LineDistance;
	if( 0 != nDistance )
	{
		nWidth += nDistance;
		nWidth += aBorderLine.InnerLineWidth;
	}

	if( nWidth == 0 )
	{
		aOut.append( GetXMLToken( XML_NONE ) );
	}
	else
	{
		rUnitConverter.convertMeasure( aOut, nWidth );

		aOut.append( sal_Unicode( ' ' ) );

		aOut.append( GetXMLToken((0 == nDistance) ? XML_SOLID : XML_DOUBLE) );

		aOut.append( sal_Unicode( ' ' ) );

		rUnitConverter.convertColor( aOut, aBorderLine.Color );
	}

	rStrExpValue = aOut.makeStringAndClear();

	return sal_True;
}
