
/**
 */
namespace _zeitbild.frontend_web.helpers
{
	
	/**
	 */
	var _template_cache : Record<string, string> = {};

	
	/**
	 * @todo caching
	 */
	export async function template_coin(
		name : string,
		data : Record<string, string>
	) : Promise<string>
	{
		let content : string;
		if (! (name in _template_cache)) {
			content = (
				(
					await lib_plankton.file.read(
						lib_plankton.string.coin(
							"templates/{{name}}.html.tpl",
							{
								"name": name,
							}
						)
					)
				)
				.toString()
			);
			_template_cache[name] = content;
		}
		else {
			content = _template_cache[name];
		}
		return Promise.resolve<string>(
			lib_plankton.string.coin(
				content,
				data
			)
		);
	}
	
	
	/**
	 * @todo outsource
	 */
	export async function promise_row<type_result>(
		members : Array<
			() => Promise<type_result>
		>
	) : Promise<
		Array<
			type_result
		>
	>
	{
		let results : Array<type_result> = [];
		for await (const member of members) {
			results.push(await member());
		}
		return Promise.resolve<Array<type_result>>(results);
	}


	/**
	 */
	export function date_object_get_week_of_year(
		date : Date
	) : int
	{
		let date_ : Date = new Date(date.getTime());
		date_.setHours(0, 0, 0, 0);
		// Thursday in current week decides the year.
		date_.setDate(date_.getDate() + 3 - (date_.getDay() + 6) % 7);
		// January 4 is always in week 1.
		let week1 : Date = new Date(date_.getFullYear(), 0, 4);
		// Adjust to Thursday in week 1 and count number of weeks from date to week1.
		return (
			1
			+
			Math.round(
				(
					((date_.getTime() - week1.getTime()) / 86400000)
					-
					3
					+
					(week1.getDay() + 6) % 7
				)
				/
				7
			)
		);
	}


	/**
	 * @todo unite with type_datetimeobject?
	 */
	export type type_datetime = {
		timezone_shift : int;
		date : type_date;
		time : (
			null
			|
			type_time
		);
	};


	/**
	 * @todo timezone_shift
	 */
	function datetime_from_date_object(
		date : Date,
		options : {
			timezone_shift ?: int;
		} = {}
	) : type_datetime
	{
		options = Object.assign(
			{
				"timezone_shift": 0,
			},
			options
		);
		const date_ : Date = lib_plankton.call.convey(
			date,
			[
				(x : Date) => x.getTime(),
				(x : int) => (x + (((options.timezone_shift as int) * (60 * 60)) * 1000)),
				(x : int) => new Date(x),
			]
		);
		const iso_string : string = date_.toISOString();
		return {
			"timezone_shift": (options.timezone_shift as int),
			"date": {
				"year": parseInt(iso_string.slice(0, 4)),
				"month": parseInt(iso_string.slice(5, 7)),
				"day": parseInt(iso_string.slice(8, 10)),
			},
			"time": {
				"hour": parseInt(iso_string.slice(11, 13)),
				"minute": parseInt(iso_string.slice(14, 16)),
				"second": parseInt(iso_string.slice(17, 19)),
			},
		};
	}



	/**
	 * @todo negative shift?
	 */
	function datetime_to_date_object(
		datetime : type_datetime
	) : Date
	{
		const iso_string : string = lib_plankton.string.coin(
			"{{year}}-{{month}}-{{day}}T{{hour}}:{{minute}}:{{second}}.000+{{shift}}",
			{
				"year": datetime.date.year.toFixed(0).padStart(4, "0"),
				"month": datetime.date.month.toFixed(0).padStart(2, "0"),
				"day": datetime.date.day.toFixed(0).padStart(2, "0"),
				"hour": ((datetime.time !== null) ? datetime.time.hour : 0).toFixed(0).padStart(2, "0"),
				"minute": ((datetime.time !== null) ? datetime.time.minute : 0).toFixed(0).padStart(2, "0"),
				"second": ((datetime.time !== null) ? datetime.time.second : 0).toFixed(0).padStart(2, "0"),
				"shift": (datetime.timezone_shift.toFixed(0).padStart(2, "0") + ":00"),
			}
		);
		return (new Date(iso_string));
	}


	/**
	 */
	export type type_pit = int;


	/**
	 */
	export function pit_to_date_object(
		pit : type_pit
	) : Date
	{
		return (new Date(pit * 1000));
	}


	/**
	 */
	function pit_from_date_object(
		date_object : Date
	) : type_pit
	{
		return Math.round(date_object.getTime() / 1000);
	}


	/**
	 */
	export function pit_now(
	) : type_pit
	{
		return pit_from_date_object(new Date(Date.now()));
	}


	/**
	 * @todo timezone
	 */
	export function pit_to_datetime(
		pit : type_pit,
		options : {
			timezone_shift ?: int
		} = {}
	) : type_datetime
	{
		options = Object.assign(
			{
				"timezone_shift": 0,
			},
			options
		);
		const date_object : Date = pit_to_date_object(pit);
		return datetime_from_date_object(
			date_object,
			{
				"timezone_shift": (options.timezone_shift as int),
			}
		);
	}


	/**
	 */
	export function pit_from_datetime(
		datetime : type_datetime
	) : type_pit
	{
		return lib_plankton.call.convey(
			datetime,
			[
				datetime_to_date_object,
				pit_from_date_object,
			]
		);
	}


	/**
	 */
	export function pit_is_before(
		pit : type_pit,
		reference : type_pit
	) : boolean
	{
		return (pit < reference);
	}


	/**
	 */
	function pit_is_after(
		pit : type_pit,
		reference : type_pit
	) : boolean
	{
		return (pit > reference);
	}


	/**
	 */
	export function pit_is_between(
		pit : type_pit,
		reference_left : type_pit,
		reference_right : type_pit
	) : boolean
	{
		return (
			pit_is_after(pit, reference_left)
			&&
			pit_is_before(pit, reference_right)
		);
	}


	/**
	 */
	function pit_shift_hour(
		pit : type_pit,
		increment : int
	) : type_pit
	{
		return (pit + (60 * 60 * increment));
	}


	/**
	 */
	export function pit_shift_day(
		pit : type_pit,
		increment : int
	) : type_pit
	{
		return (pit + (60 * 60 * 24 * increment));
	}


	/**
	 */
	export function pit_shift_week(
		pit : type_pit,
		increment : int
	) : type_pit
	{
		return (pit + (60 * 60 * 24 * 7 * increment));
	}


	/**
	 */
	function pit_shift_year(
		pit : type_pit,
		increment : int
	) : type_pit
	{
		return (pit + (60 * 60 * 24 * 365 * increment));
	}


	/**
	 */
	function pit_trunc_minute(
		pit : type_pit
	) : type_pit
	{
		const datetime_input : type_datetime = pit_to_datetime(pit);
		const datetime_output : type_datetime = {
			"timezone_shift": 0,
			"date": {
				"year": datetime_input.date.year,
				"month": datetime_input.date.month,
				"day": datetime_input.date.day,
			},
			"time": {
				"hour": (
					(datetime_input.time === null)
					?
					0
					:
					datetime_input.time.hour
				),
				"minute": (
					(datetime_input.time === null)
					?
					0
					:
					datetime_input.time.minute
				),
				"second": 0,
			},
		};
		return pit_from_datetime(datetime_output);
	}


	/**
	 */
	function pit_trunc_hour(
		pit : type_pit
	) : type_pit
	{
		const datetime_input : type_datetime = pit_to_datetime(pit);
		const datetime_output : type_datetime = {
			"timezone_shift": 0,
			"date": {
				"year": datetime_input.date.year,
				"month": datetime_input.date.month,
				"day": datetime_input.date.day,
			},
			"time": {
				"hour": (
					(datetime_input.time === null)
					?
					0
					:
					datetime_input.time.hour
				),
				"minute": 0,
				"second": 0,
			},
		};
		return pit_from_datetime(datetime_output);
	}


	/**
	 */
	function pit_trunc_day(
		pit : type_pit
	) : type_pit
	{
		const datetime_input : type_datetime = pit_to_datetime(pit);
		const datetime_output : type_datetime = {
			"timezone_shift": 0,
			"date": {
				"year": datetime_input.date.year,
				"month": datetime_input.date.month,
				"day": datetime_input.date.day,
			},
			"time": {
				"hour": 0,
				"minute": 0,
				"second": 0,
			},
		};
		return pit_from_datetime(datetime_output);
	}


	/**
	 */
	export function pit_trunc_week(
		pit : type_pit
	) : type_pit
	{
		const date_object : Date = pit_to_date_object(pit);
		return lib_plankton.call.convey(
			date_object.getDay(),
			[
				(x : int) => ((x === 0) ? 7 : x),
				(x : int) => (x - 1),
				(x : int) => pit_shift_day(pit, (-x)),
				pit_trunc_day
			]
		);
	}


	/**
	 */
	function pit_trunc_month(
		pit : type_pit
	) : type_pit
	{
		const datetime_input : type_datetime = pit_to_datetime(pit);
		const datetime_output : type_datetime = {
			"timezone_shift": 0,
			"date": {
				"year": datetime_input.date.year,
				"month": datetime_input.date.month,
				"day": 1,
			},
			"time": {
				"hour": 0,
				"minute": 0,
				"second": 0,
			},
		};
		return pit_from_datetime(datetime_output);
	}


	/**
	 */
	function pit_trunc_year(
		pit : type_pit
	) : type_pit
	{
		const datetime_input : type_datetime = pit_to_datetime(pit);
		const datetime_output : type_datetime = {
			"timezone_shift": 0,
			"date": {
				"year": datetime_input.date.year,
				"month": 1,
				"day": 1,
			},
			"time": {
				"hour": 0,
				"minute": 0,
				"second": 0,
			},
		};
		return pit_from_datetime(datetime_output);
	}


	/**
	 * @param year year according to specified timezone shift
	 * @param week week according to specified timezone shift
	 * @return the begin of the week (monday, 00:00)
	 */
	export function pit_from_year_and_week(
		year : int,
		week : int,
		options : {
			timezone_shift ?: int;
		} = {}
	) : type_pit
	{
		options = Object.assign(
			{
				"timezone_shift": 0,
			},
			options
		);
		return lib_plankton.call.convey(
			{
				"timezone_shift": (options.timezone_shift as int),
				"date": {
					"year": year,
					"month": 1,
					"day": 1,
				},
				"time": {
					"hour": 0,
					"minute": 0,
					"second": 0
				}
			},
			[
				pit_from_datetime,
				(x : type_pit) => pit_shift_week(x, (week - 1)),
				pit_trunc_week,
			]
		);
	}
	
}
