/*
 * Copyright 2010-2014, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Oliver Tappe <zooey@hirschkaefer.de>
 */

#ifndef HoG_GENODE
#include <unicode/uversion.h>
#endif  // ~HoG_GENODE
#include <DateTimeFormat.h>

#include <AutoDeleter.h>
#include <Autolock.h>
#include <FormattingConventionsPrivate.h>
#include <LanguagePrivate.h>
#include <TimeZone.h>

#ifndef HoG_GENODE
#include <ICUWrapper.h>

#include <unicode/datefmt.h>
#include <unicode/dtptngen.h>
#include <unicode/smpdtfmt.h>
#endif  // ~HoG_GENODE


BDateTimeFormat::BDateTimeFormat(const BLocale* locale)
	: BFormat(locale)
{
}


#ifndef HoG_GENODE

BDateTimeFormat::BDateTimeFormat(const BLanguage& language,
	const BFormattingConventions& conventions)
	: BFormat(language, conventions)
{
}


BDateTimeFormat::BDateTimeFormat(const BDateTimeFormat &other)
	: BFormat(other)
{
}

#endif  // ~HoG_GENODE


BDateTimeFormat::~BDateTimeFormat()
{
}


void
BDateTimeFormat::SetDateTimeFormat(BDateFormatStyle dateStyle,
	BTimeFormatStyle timeStyle, int32 elements) {
#ifndef HoG_GENODE
	UErrorCode error = U_ZERO_ERROR;
	DateTimePatternGenerator* generator
		= DateTimePatternGenerator::createInstance(
			*BLanguage::Private(&fLanguage).ICULocale(), error);

///maybe-later: date format: do create the skeleton, and find out how to use it
	BString skeleton;
	if (elements & B_DATE_ELEMENT_YEAR)
		skeleton << "yyyy";
	if (elements & B_DATE_ELEMENT_MONTH)
		skeleton << "MM";
	if (elements & B_DATE_ELEMENT_WEEKDAY)
		skeleton << "eee";
	if (elements & B_DATE_ELEMENT_DAY)
		skeleton << "dd";
	if (elements & B_DATE_ELEMENT_AM_PM)
		skeleton << "a";
	if (elements & B_DATE_ELEMENT_HOUR) {
		if (fConventions.Use24HourClock())
			skeleton << "k";
		else
			skeleton << "K";
	}
	if (elements & B_DATE_ELEMENT_MINUTE)
		skeleton << "mm";
	if (elements & B_DATE_ELEMENT_SECOND)
		skeleton << "ss";
	if (elements & B_DATE_ELEMENT_TIMEZONE)
		skeleton << "z";

	UnicodeString pattern = generator->getBestPattern(
		UnicodeString::fromUTF8(skeleton.String()), error);

	BString buffer;
	BStringByteSink stringConverter(&buffer);
	pattern.toUTF8(stringConverter);

	fConventions.SetExplicitDateTimeFormat(dateStyle, timeStyle, buffer);

	delete generator;
#endif  // ~HoG_GENODE
}


// #pragma mark - Formatting


ssize_t
BDateTimeFormat::Format(char* target, size_t maxSize, time_t time,
	BDateFormatStyle dateStyle, BTimeFormatStyle timeStyle) const
{
#ifndef HoG_GENODE
	BString format;
	fConventions.GetDateTimeFormat(dateStyle, timeStyle, format);
	ObjectDeleter<DateFormat> dateFormatter(_CreateDateTimeFormatter(format));
	if (dateFormatter.Get() == NULL)
		return B_NO_MEMORY;

	UnicodeString icuString;
	dateFormatter->format((UDate)time * 1000, icuString);

	CheckedArrayByteSink stringConverter(target, maxSize);
	icuString.toUTF8(stringConverter);

	if (stringConverter.Overflowed())
		return B_BAD_VALUE;

	return stringConverter.NumberOfBytesWritten();

#else

///later: localize hour/minutes (this instance is used by the Deskbar clock)
	char buffer [80] = {};
	{
		struct tm timeinfo = {};
		localtime_r( &time, &timeinfo);
		
		strftime( buffer, sizeof(buffer), "%H:%M:%S", &timeinfo );
	}
	strlcpy( target, buffer, maxSize );
	
	return strlen( buffer );
#endif  // ~HoG_GENODE
}


status_t
BDateTimeFormat::Format(BString& target, const time_t time,
	BDateFormatStyle dateStyle, BTimeFormatStyle timeStyle,
	const BTimeZone* timeZone) const
{
#ifndef HoG_GENODE
	BString format;
	fConventions.GetDateTimeFormat(dateStyle, timeStyle, format);
	ObjectDeleter<DateFormat> dateFormatter(_CreateDateTimeFormatter(format));
	if (dateFormatter.Get() == NULL)
		return B_NO_MEMORY;

	if (timeZone != NULL) {
		ObjectDeleter<TimeZone> icuTimeZone(
			TimeZone::createTimeZone(timeZone->ID().String()));
		if (icuTimeZone.Get() == NULL)
			return B_NO_MEMORY;
		dateFormatter->setTimeZone(*icuTimeZone.Get());
	}

	UnicodeString icuString;
	dateFormatter->format((UDate)time * 1000, icuString);

	target.Truncate(0);
	BStringByteSink stringConverter(&target);
	icuString.toUTF8(stringConverter);

#else

///later: don't hardcode date format (this instance is used by Tracker for folder entry details)
	char buffer [80] = {};
	{
		struct tm timeinfo = {};
		localtime_r( &time, &timeinfo);
		
		strftime( buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &timeinfo );
	}
	target = buffer;
#endif  // ~HoG_GENODE

	return B_OK;
}


#ifndef HoG_GENODE

DateFormat*
BDateTimeFormat::_CreateDateTimeFormatter(const BString& format) const
{
	Locale* icuLocale
		= fConventions.UseStringsFromPreferredLanguage()
			? BLanguage::Private(&fLanguage).ICULocale()
			: BFormattingConventions::Private(&fConventions).ICULocale();

	icu::DateFormat* dateFormatter = icu::DateFormat::createDateTimeInstance(
		DateFormat::kDefault, DateFormat::kDefault, *icuLocale);
	if (dateFormatter == NULL)
		return NULL;

	SimpleDateFormat* dateFormatterImpl
		= static_cast<SimpleDateFormat*>(dateFormatter);

	UnicodeString pattern(format.String());
	dateFormatterImpl->applyPattern(pattern);

	return dateFormatter;
}

#endif  // ~HoG_GENODE
