/*
 * 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.
 */

#ifndef _LOG4CXX_HELPERS_CACHED_DATE_FORMAT_H
#define _LOG4CXX_HELPERS_CACHED_DATE_FORMAT_H

#include <log4cxx/helpers/dateformat.h>

#if defined(_MSC_VER)
	#pragma warning ( push )
	#pragma warning ( disable: 4251 )
#endif

namespace log4cxx
{
namespace pattern
{
class LOG4CXX_EXPORT CachedDateFormat : public log4cxx::helpers::DateFormat
{
	public:
		enum
		{
			/*
			 *  Constant used to represent that there was no change
			 *  observed when changing the millisecond count.
			 */
			NO_MILLISECONDS = -2,
			/*
			 *  Constant used to represent that there was an
			 *  observed change, but was an expected change.
			 */
			UNRECOGNIZED_MILLISECONDS = -1
		};

	private:
		/**
		 *  Supported digit set.  If the wrapped DateFormat uses
		 *  a different unit set, the millisecond pattern
		 *  will not be recognized and duplicate requests
		 *  will use the cache.
		 */
		static const logchar digits[];


		/**
		 * First magic number (in microseconds) used to detect
		 * the millisecond position.
		 */
		static const int magic1;


		/**
		 *  Expected representation of first magic number in milliseconds.
		 */
		static const logchar magicString1[];


		/**
		 * Second magic number (in microseconds) used to detect
		 * the millisecond position.
		 */
		static const int magic2;


		/**
		 *  Expected representation of second magic number in milliseconds.
		 */
		static const logchar magicString2[];


		/**
		 *  Expected representation of 0 milliseconds.
		 */
		static const logchar zeroString[];

		/**
		 *   Wrapped formatter.
		 */
		log4cxx::helpers::DateFormatPtr formatter;

		/**
		 *  Index of initial digit of millisecond pattern or
		 *   UNRECOGNIZED_MILLISECONDS or NO_MILLISECONDS.
		 */
		mutable int millisecondStart;

		/**
		 *  Integral second preceding the previous convered Date.
		 */
		mutable log4cxx_time_t slotBegin;


		/**
		 *  Cache of previous conversion.
		 */
		mutable LogString cache;


		/**
		 *  Maximum validity period for the cache.
		 *  Typically 1, use cache for duplicate requests only, or
		 *  1000000, use cache for requests within the same integral second.
		 */
		const int expiration;

		/**
		 *  Date requested in previous conversion.
		 */
		mutable log4cxx_time_t previousTime;

	public:
		/**
		 *  Creates a new CachedDateFormat object.
		 *  @param dateFormat Date format, may not be null.
		 *  @param expiration maximum cached range in microseconds.
		 *    If the dateFormat is known to be incompatible with the
		 *      caching algorithm, use a value of 0 to totally disable
		 *      caching or 1 to only use cache for duplicate requests.
		 */
		CachedDateFormat(const log4cxx::helpers::DateFormatPtr& dateFormat, int expiration);

		/**
		 * Finds start of millisecond field in formatted time.
		 * @param time long time, must be integral number of seconds
		 * @param formatted String corresponding formatted string
		 * @param formatter DateFormat date format
		 * @param pool pool.
		 * @return int position in string of first digit of milliseconds,
		 *    -1 indicates no millisecond field, -2 indicates unrecognized
		 *    field (likely RelativeTimeDateFormat)
		 */
		static int findMillisecondStart(
			log4cxx_time_t time, const LogString& formatted,
			const log4cxx::helpers::DateFormatPtr& formatter,
			log4cxx::helpers::Pool& pool);

		/**
		 * Formats a Date into a date/time string.
		 *
		 *  @param date the date to format.
		 *  @param sbuf the string buffer to write to.
		 *  @param p memory pool.
		 */
		virtual void format(LogString& sbuf,
			log4cxx_time_t date,
			log4cxx::helpers::Pool& p) const;

	private:
		/**
		 *   Formats a count of milliseconds (0-999) into a numeric representation.
		 *   @param millis Millisecond coun between 0 and 999.
		 *   @buf String buffer, may not be null.
		 *   @offset Starting position in buffer, the length of the
		 *       buffer must be at least offset + 3.
		 */
		static void millisecondFormat(int millis,
			LogString& buf,
			int offset);


	public:
		/**
		 * Set timezone.
		 *
		 * @remarks Setting the timezone using getCalendar().setTimeZone()
		 * will likely cause caching to misbehave.
		 * @param zone TimeZone new timezone
		 */
		virtual void setTimeZone(const log4cxx::helpers::TimeZonePtr& zone);

		/**
		* Format an integer consistent with the format method.
		* @param s string to which the numeric string is appended.
		* @param n integer value.
		* @param p memory pool used during formatting.
		*/
		virtual void numberFormat(LogString& s,
			int n,
			log4cxx::helpers::Pool& p) const;

		/**
		 * Gets maximum cache validity for the specified SimpleDateTime
		 *    conversion pattern.
		 *  @param pattern conversion pattern, may not be null.
		 *  @returns Duration in microseconds from an integral second
		 *      that the cache will return consistent results.
		 */
		static int getMaximumCacheValidity(const LogString& pattern);

	private:
		CachedDateFormat(const CachedDateFormat&);
		CachedDateFormat& operator=(const CachedDateFormat&);

		/**
		* Tests if two string regions are equal.
		* @param target target string.
		* @param toffset character position in target to start comparison.
		* @param other other string.
		* @param ooffset character position in other to start comparison.
		* @param len length of region.
		* @return true if regions are equal.
		*/
		static bool regionMatches(
			const LogString& target,
			size_t toffset,
			const LogString& other,
			size_t ooffset,
			size_t len);

};



}  // namespace helpers
} // namespace log4cxx

#if defined(_MSC_VER)
	#pragma warning (pop)
#endif

#endif // _LOG4CXX_HELPERS_SIMPLE_DATE_FORMAT_H
