/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* librvngabw
 * Version: MPL 2.0 / LGPLv2.1+
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Alternatively, the contents of this file may be used under the terms
 * of the GNU Lesser General Public License Version 2.1 or later
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
 * applicable instead of those above.
 *
 * For further information visit http://libwpd.sourceforge.net
 */

#include <stdio.h>

#include <cstdarg>
#include <cstdio>
#include <locale>
#include <sstream>

#include "DocumentElement.hxx"

#include "FilterInternal.hxx"

namespace librvngabw
{
void appendUnicode(uint32_t val, librevenge::RVNGString &buffer)
{
	uint8_t first;
	int len;
	if (val < 0x80)
	{
		first = 0;
		len = 1;
	}
	else if (val < 0x800)
	{
		first = 0xc0;
		len = 2;
	}
	else if (val < 0x10000)
	{
		first = 0xe0;
		len = 3;
	}
	else if (val < 0x200000)
	{
		first = 0xf0;
		len = 4;
	}
	else if (val < 0x4000000)
	{
		first = 0xf8;
		len = 5;
	}
	else
	{
		first = 0xfc;
		len = 6;
	}

	char outbuf[7];
	int i;
	for (i = len - 1; i > 0; --i)
	{
		outbuf[i] = char((val & 0x3f) | 0x80);
		val >>= 6;
	}
	outbuf[0] = char(val | first);
	outbuf[len] = 0;
	buffer.append(outbuf);
}

librevenge::RVNGString doubleToString(const double value)
{
	auto *prop = librevenge::RVNGPropertyFactory::newDoubleProp(value);
	auto retVal = prop->getStr();
	delete prop;
	return retVal;
}

double getInchValue(librevenge::RVNGProperty const &prop)
{
	double res;
	if (!getInchValue(prop, res))
		return 0;
	return res;
}

bool getInchValue(librevenge::RVNGProperty const &prop, double &value)
{
	value=prop.getDouble();
	switch (prop.getUnit())
	{
	case librevenge::RVNG_GENERIC: // assume inch
	case librevenge::RVNG_INCH:
		return true;
	case librevenge::RVNG_POINT:
		value /= 72.;
		return true;
	case librevenge::RVNG_TWIP:
		value /= 1440.;
		return true;
	case librevenge::RVNG_PERCENT:
	case librevenge::RVNG_UNIT_ERROR:
	default:
	{
		static bool first=true;
		if (first)
		{
			RVNGABW_DEBUG_MSG(("librvngabw::getInchValue: call with no double value\n"));
			first=false;
		}
		break;
	}
	}
	return false;
}

/** try to append border properties where which=(1:Left)|(2:Right)|(4:Top)|(5:Bottom) */
bool appendBorderProperties(librevenge::RVNGString const &prop, int wh, librevenge::RVNGPropertyList &propPropList,
                            bool toParagraph)
{
	if (prop.empty())
		return true;
	if (prop.size()>100)
	{
		RVNGABW_DEBUG_MSG(("librvngabw::appendBorderProperties: property size seems bad\n"));
		return false;
	}

	std::string str(prop.cstr()), type("solid"), color("#000000");
	// first use ' ' to cut the string in str, type and color
	for (int i=0; i<3; ++i)
	{
		auto pos = str.find_last_of(' ');
		if ((i!=2 && (pos==std::string::npos || pos==0)) || (i==2 && pos!=std::string::npos))
		{
			if (i==0 && pos==std::string::npos) // ok let assume solid and black color
				break;
			RVNGABW_DEBUG_MSG(("librvngabw::appendBorderProperties: can not decode %s\n", prop.cstr()));
			return false;
		}
		if (i==2)
			break;
		if (i==0)
			color = str.substr(pos+1);
		else
			type = str.substr(pos+1);
		str=str.substr(0, pos);
	}
	// now decompose str in width[unit]*
	auto unitPos=str.length();
	while (unitPos>0)
	{
		char c=str[unitPos-1];
		if ((c>='a' && c<='z')||(c=='*'))
		{
			--unitPos;
			continue;
		}
		break;
	}
	std::string unit("");
	if (unitPos!=str.length()) unit=str.substr(unitPos);
	std::istringstream iss(str.substr(0,unitPos));
	iss.imbue(std::locale("C")); // be sure that we use standart double
	double width = 0.0;
	iss >> width;
	if (iss.fail())
	{
		RVNGABW_DEBUG_MSG(("librvngabw::appendBorderProperties: can not find the width %s\n", str.c_str()));
		return false;
	}
	if (unit.empty() || unit=="pt") width/=72;
	else if (unit== "in")
	{
	}
	else if (unit== "cm") width*=0.3937;
	else if (unit== "mm") width*=0.03937;
	else if (unit== "*") width/=1440;
	else
	{
		RVNGABW_DEBUG_MSG(("librvngabw::appendBorderProperties: can not decode unit %s\n", unit.c_str()));
		return false;
	}

	int style=0;
	if (type=="none") // ok no border
		return true;
	if (type=="dotted")
		style=2;
	else if (type=="dashed")
		style=3;
	else if (type=="solid")
		style=1;
	else if (type=="double")
	{
		style=1;
		width *= 2;
	}
	else
	{
		RVNGABW_DEBUG_MSG(("librvngabw::appendBorderProperties: can not decode type %s\n", type.c_str()));
		return false;
	}
	if (color.empty() || color[0]!='#' || width<0 || style<=0)
	{
		RVNGABW_DEBUG_MSG(("librvngabw::appendBorderProperties: something is bad\n"));
		return false;
	}
	if (width<=0) // ok no border
		return true;
	librevenge::RVNGString finalColor(&(color[1]));
	for (int i=0, depl=1; i<4; ++i, depl*=2)
	{
		if ((wh&depl)==0) continue;
		static char const *(what[])= {"left", "right", "top", "bot"};
		librevenge::RVNGString tmp;
		tmp.sprintf("%s-style", what[i]);
		propPropList.insert(tmp.cstr(), style);
		tmp.sprintf("%s-thickness", what[i]);
		if (toParagraph)
		{
			propPropList.insert(tmp.cstr(), width, librevenge::RVNG_INCH);
			tmp.sprintf("%s-space", what[i]);
		}
		propPropList.insert(tmp.cstr(), 2*width, librevenge::RVNG_INCH);
		tmp.sprintf("%s-color", what[i]);
		propPropList.insert(tmp.cstr(), librevenge::RVNGPropertyFactory::newStringProp(color.c_str()));
	}
	return true;
}

void appendBorderProperties(librevenge::RVNGPropertyList const &borderProp,
                            librevenge::RVNGPropertyList &propPropList,
                            bool toParagraph)
{
	if (!toParagraph)
	{
		// first set all border to none
		for (int i=0; i<4; ++i)
		{
			static char const *(what[])= {"left", "right", "top", "bot"};
			librevenge::RVNGString tmp;
			tmp.sprintf("%s-style", what[i]);
			propPropList.insert(tmp.cstr(), 0);
		}
	}
	for (int i=0; i<5; ++i)
	{
		static char const *(names[])=
		{
			"fo:border", "fo:border-left", "fo:border-right", "fo:border-top",  "fo:border-bottom"
		};
		if (!borderProp[names[i]])
			continue;
		static int const(which[])= {15,1,2,4,8};
		appendBorderProperties(borderProp[names[i]]->getStr(),which[i],propPropList,toParagraph);
	}
}

#ifdef DEBUG
void printDebugMsg(const char *format, ...)
{
	va_list args;
	va_start(args, format);
	std::vfprintf(stderr, format, args);
	va_end(args);
}
#endif


}
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
