namespace _dali.widgets.weekview
{
	
	/**
	 */
	type type_entry = {
		calendar_id : _dali.type.calendar_id;
		calendar_name : string;
		access_level : _dali.type.enum_access_level;
		event_id : (null | _dali.type.local_resource_event_id);
		event_object : _dali.type.event_object;
	};
	
	
	/**
	 */
	type type_get_entries = (
		(
			from_pit : lib_plankton.pit.type_pit,
			to_pit : lib_plankton.pit.type_pit,
			calendar_ids : Array<_dali.type.calendar_id>
		)
		=>
		Promise<Array<type_entry>>
	);
	
	
	/**
	 */
	export class class_widget_weekview extends _dali.class_widget
	{
		
		/**
		 */
		private get_entries : type_get_entries;
		
		
		/**
		 */
		private container : (null | Element);
		
		
		/**
		 */
		private action_select_event : (
			(
				calendar_id : _dali.type.calendar_id,
				access_level : _dali.type.enum_access_level,
				event_id : _dali.type.local_resource_event_id
			)
			=>
			void
		);
		
		
		/**
		 */
		private action_select_day : (
			(
				date : lib_plankton.pit.type_date
			)
			=>
			void
		);
		
		
		/**
		 */
		public constructor(
			get_entries : type_get_entries,
			options : {
				action_select_event ?: (
					(
						calendar_id : _dali.type.calendar_id,
						access_level : _dali.type.enum_access_level,
						event_id : _dali.type.local_resource_event_id
					)
					=>
					void
				);
				action_select_day ?: (
					(
						date : lib_plankton.pit.type_date
					)
					=>
					void
				);
			} = {}
		)
		{
			options = Object.assign(
				{
					"action_select_day": (date) => {},
					"action_select_event": (calendar_id, access_level, event_id) => {},
				},
				options
			);
			super();
			this.get_entries = get_entries;
			this.container = null;
			this.action_select_day = options.action_select_day;
			this.action_select_event = options.action_select_event;
		}
		
		
		/**
		 */
		private static event_generate_tooltip(
			calendar_name : string,
			event_object : _dali.type.event_object
		) : string
		{
			return (
				lib_plankton.string.coin(
					"[{{calendar_name}}] {{event_name}}\n",
					{
						"calendar_name": calendar_name,
						"event_name": event_object.name,
					}
				)
				+
				"--\n"
				+
				(
					(event_object.begin.time !== null)
					?
					lib_plankton.string.coin(
						"{{label}}: {{value}}\n",
						{
							"label": lib_plankton.translate.get("event.when"),
							"value": lib_plankton.pit.timespan_format(
								event_object.begin,
								event_object.end,
								{
									"timezone_indicator": lib_plankton.translate.get("common.timezone_indicator"),
									"adjust_to_ce": true,
									"show_timezone": true,
									"omit_date": true,
								}
							),
						}
					)
					:
					""
				)
				+
				(
					(event_object.location !== null)
					?
					(
						lib_plankton.string.coin(
							"{{label}}: {{value}}\n",
							{
								"label": lib_plankton.translate.get("event.location"),
								"value": event_object.location,
							}
						)
					)
					:
					""
				)
				+
				(
					(event_object.link !== null)
					?
					(
						lib_plankton.string.coin(
							"{{label}}: {{value}}\n",
							{
								"label": lib_plankton.translate.get("event.link"),
								"value": event_object.link,
							}
						)
					)
					:
					""
				)
				/*
				+
				(
					(event_object.description !== null)
					?
					(
						"--\n"
						+
						lib_plankton.string.coin(
							"{{description}}\n",
							{
								"description": event_object.description,
							}
						)
					)
					:
					""
				)
				 */
			);
		}
		
		
		/**
		 * @todo kein "while"
		 */
		private async calendar_view_table_data(
			calendar_ids : ( 
				null
				|
				Array<_dali.type.calendar_id>
			),
			from : {
				year : int;
				week : int;
			},
			to : {
				year : int;
				week : int;
			},
			timezone_shift : int
		) : Promise<
			{
				sources : lib_plankton.map.type_map<
					_dali.type.calendar_id,
					{
						name : string;
						access_level : _dali.type.enum_access_level;
					}
				>;
				rows : Array<
					{
						week : int;
						data : Array<
							{
								pit : lib_plankton.pit.type_pit;
								entries : Array<
									{
										calendar_id : _dali.type.calendar_id;
										event_id : (null | _dali.type.local_resource_event_id);
										event_object : _dali.type.event_object;
									}
								>;
								today : boolean;
							}
						>;
					}
				>
			}
		>
		{
			const now_pit : lib_plankton.pit.type_pit = lib_plankton.pit.now();
			const from_pit : lib_plankton.pit.type_pit = lib_plankton.pit.from_ywd(
				{
					"year": (from as {year : int; week : int}).year,
					"week": (from as {year : int; week : int}).week,
					"day": 1,
				},
				{
					"timezone_shift": (timezone_shift as int),
				}
			);
			const to_pit : lib_plankton.pit.type_pit = lib_plankton.pit.from_ywd(
				{
					"year": (to as {year : int; week : int}).year,
					"week": (to as {year : int; week : int}).week,
					"day": 1,
				},
				{
					"timezone_shift": (timezone_shift as int),
				}
			);
			
			// prepare
			const entries : Array<type_entry> = await this.get_entries(
				from_pit,
				to_pit,
				calendar_ids
			);
			let result : {
				sources : lib_plankton.map.type_map<
					_dali.type.calendar_id,
					{
						name : string;
						access_level : _dali.type.enum_access_level;
					}
				>;
				rows : Array<
					{
						week : int;
						data : Array<
							{
								pit : lib_plankton.pit.type_pit;
								entries : Array<
									{
										calendar_id : _dali.type.calendar_id;
										event_id : (null | _dali.type.local_resource_event_id);
										event_object : _dali.type.event_object;
									}
								>;
								today : boolean;
							}
						>;
					}
				>;
			} = {
				"sources": lib_plankton.map.hashmap.implementation_map(
					lib_plankton.map.hashmap.make(
						x => x.toFixed(0),
						{
							"pairs": (
								entries
								.map(
									(entry) => (
										{
											"key": entry.calendar_id,
											"value": {
												"name": entry.calendar_name,
												"access_level": entry.access_level,
											}
										}
									)
								)
							)
						}
					)
				),
				"rows": [],
			};
			let row : Array<
				{
					pit : lib_plankton.pit.type_pit;
					entries : Array<
						{
							calendar_id : _dali.type.calendar_id;
							event_id : (null | _dali.type.local_resource_event_id);
							event_object : _dali.type.event_object;
						}
					>;
					today : boolean;
				}
			> = [];
			let day : int = 0;
			while (true) {
				const pit_current : lib_plankton.pit.type_pit = lib_plankton.pit.shift_day(
					from_pit,
					day
				);
				if (
					lib_plankton.pit.is_before(
						pit_current,
						to_pit
					)
				) {
					day += 1;
					row.push(
						{
							"pit": pit_current,
							"entries": [],
							"today": false, // TODO
						}
					);
					if (day % 7 === 0) {
						result.rows.push(
							{
								"week": (
									(from as {year : int; week : int}).week
									+
									Math.floor(day / 7)
									-
									1 // TODO
								),
								"data": row
							}
						);
						row = [];
					}
					else {
						// do nothing
					}
				}
				else {
					break;
				}
			}
			
			// fill
			{
				// events
				(
					entries
					.forEach(
						(entry) => {
							const distance_seconds : int = (
								lib_plankton.pit.from_datetime(
									/**
									 * so that events without a start time will be put in the correct box
									 */
									{
										"timezone_shift": entry.event_object.begin.timezone_shift,
										"date": entry.event_object.begin.date,
										"time": (
											entry.event_object.begin.time
											??
											{
												"hour": 12,
												"minute": 0,
												"second": 0
											}
										),
									}
								)
								-
								from_pit
							);
							const distance_days : int = (distance_seconds / (60 * 60 * 24));
							
							const week : int = Math.floor(Math.floor(distance_days) / 7);
							const day : int = (Math.floor(distance_days) % 7);
							
							if ((week >= 0) && (week < result.rows.length)) {
								result.rows[week].data[day].entries.push(entry);
							}
							else {
								// do nothing
							}
						}
					)
				);
				// today
				{
					const distance_seconds : int = (
						now_pit
						-
						from_pit
					);
					const distance_days : int = (distance_seconds / (60 * 60 * 24));
					
					const week : int = Math.floor(Math.floor(distance_days) / 7);
					const day : int = (Math.floor(distance_days) % 7);
					
					if ((week >= 0) && (week < result.rows.length)) {
						result.rows[week].data[day].today = true;
					}
					else {
						// do nothing
					}
				}
			}
			
			return Promise.resolve(result);
		}
		
		
		/**
		 */
		private async table_rows(
			options : {
				calendar_ids ?: (
					null
					|
					Array<_dali.type.calendar_id>
				);
				from ?: {
					year : int;
					week : int;
				};
				to ?: {
					year : int;
					week : int;
				};
				timezone_shift ?: int;
				action_select ?: (
					(
						calendar_id : _dali.type.calendar_id,
						event_id : _dali.type.local_resource_event_id
					)
					=>
					void
				)
			} = {}
		) : Promise<string>
		{
			const now_pit : lib_plankton.pit.type_pit = lib_plankton.pit.now();
			options = Object.assign(
				{
					"calendar_ids": null,
					"from": lib_plankton.call.convey(
						now_pit,
						[
							(x : lib_plankton.pit.type_pit) => lib_plankton.pit.shift_week(x, -1),
							lib_plankton.pit.to_ywd,
							x => ({"year": x.year, "week": x.week}),
						]
					),
					"to": lib_plankton.call.convey(
						now_pit,
						[
							(x : lib_plankton.pit.type_pit) => lib_plankton.pit.shift_week(x, +4),
							lib_plankton.pit.to_ywd,
							x => ({"year": x.year, "week": x.week}),
						]
					),
					"timezone_shift": 0,
				},
				options
			);
			const stuff : {
				sources : lib_plankton.map.type_map<
					_dali.type.calendar_id,
					{
						name : string;
						access_level : _dali.type.enum_access_level;
					}
				>;
				rows : Array<
					{
						week : int;
						data : Array<
							{
								pit : lib_plankton.pit.type_pit;
								entries : Array<
									{
										calendar_id : _dali.type.calendar_id;
										event_id : (null | _dali.type.local_resource_event_id);
										event_object : _dali.type.event_object;
									}
								>;
								today : boolean;
							}
						>;
					}
				>;
			} = await this.calendar_view_table_data(
				options.calendar_ids,
				options.from,
				options.to,
				options.timezone_shift
			);
			const sources : lib_plankton.map.type_map<
				_dali.type.calendar_id,
				{
					name : string;
					access_level : _dali.type.enum_access_level;
					color : lib_plankton.color.type_color;
				}
			> = lib_plankton.map.hashmap.implementation_map(
				lib_plankton.map.hashmap.make(
					(x => x.toFixed(0)),
					{
						"pairs": (
							lib_plankton.map.dump(
								stuff.sources
							)
							.map(
								(pair) => ({
									"key": pair.key,
									"value": {
										"name": pair.value.name,
										"access_level": pair.value.access_level,
										"color": lib_plankton.color.give_generic(
											(pair.key - 1),
											{
												"saturation": 0.375,
												"value": 0.375,
											}
										),
									}
								})
							)
						)
					}
				)
			);
			return (
				await _dali.helpers.promise_row<string>(
					stuff.rows
					.map(
						(row) => async () => _dali.helpers.template_coin(
							"widget-weekview",
							"tableview-row",
							{
								"week": row.week.toFixed(0).padStart(2, "0"),
								"cells": (
									await _dali.helpers.promise_row<string>(
										row.data
										.map(
											(cell) => async () => _dali.helpers.template_coin(
												"widget-weekview",
												"tableview-cell",
												{
													"extra_classes": (
														[""]
														.concat(cell.today ? ["weekview-cell-today"] : [])
														.join(" ")
													),
													"title": lib_plankton.call.convey(
														cell.pit,
														[
															lib_plankton.pit.to_datetime_ce,
															(x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin(
																"{{year}}-{{month}}-{{day}}",
																{
																	"year": x.date.year.toFixed(0).padStart(4, "0"),
																	"month": x.date.month.toFixed(0).padStart(2, "0"),
																	"day": x.date.day.toFixed(0).padStart(2, "0"),
																}
															),
														]
													),
													"day": lib_plankton.call.convey(
														cell.pit,
														[
															lib_plankton.pit.to_datetime_ce,
															(x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin(
																"{{day}}",
																{
																	"year": x.date.year.toFixed(0).padStart(4, "0"),
																	"month": x.date.month.toFixed(0).padStart(2, "0"),
																	"day": x.date.day.toFixed(0).padStart(2, "0"),
																}
															),
														]
													),
													"rel": lib_plankton.call.convey(
														cell.pit,
														[
															lib_plankton.pit.to_datetime_ce,
															(x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin(
																"{{year}}-{{month}}-{{day}}",
																{
																	"year": x.date.year.toFixed(0).padStart(4, "0"),
																	"month": x.date.month.toFixed(0).padStart(2, "0"),
																	"day": x.date.day.toFixed(0).padStart(2, "0"),
																}
															)
														]
													),
													"entries": (
														await _dali.helpers.promise_row<string>(
															cell.entries
															.map(
																(entry) => () => _dali.helpers.template_coin(
																	"widget-weekview",
																	"tableview-cell-entry",
																	{
																		"color": lib_plankton.color.output_hex(
																			sources.get(
																				entry.calendar_id
																			).color
																		),
																		"title": class_widget_weekview.event_generate_tooltip(
																			sources.get(
																				entry.calendar_id
																			).name,
																			entry.event_object
																		),
																		"name": entry.event_object.name,
																		"rel": lib_plankton.string.coin(
																			"{{calendar_id}}/{{event_id}}/{{access_level}}",
																			{
																				"calendar_id": entry.calendar_id.toFixed(0),
																				"event_id": (
																					(entry.event_id === null)
																					?
																					"-"
																					:
																					entry.event_id.toFixed(0)
																				),
																				"access_level": (() => {
																					const access_level : _dali.type.enum_access_level = sources.get(entry.calendar_id).access_level;
																					switch (access_level) {
																						case _dali.type.enum_access_level.none: return "none";
																						case _dali.type.enum_access_level.view: return "view";
																						case _dali.type.enum_access_level.edit: return "edit";
																						case _dali.type.enum_access_level.admin: return "admin";
																					}
																				}) (),
																			}
																		),
																		"additional_classes": lib_plankton.string.coin(
																			" access_level-{{access_level}}",
																			{
																				"access_level": (() => {
																					const access_level : _dali.type.enum_access_level = sources.get(entry.calendar_id).access_level;
																					switch (access_level) {
																						case _dali.type.enum_access_level.none: return "none";
																						case _dali.type.enum_access_level.view: return "view";
																						case _dali.type.enum_access_level.edit: return "edit";
																						case _dali.type.enum_access_level.admin: return "admin";
																					}
																				}) (),
																			}
																		),
																	}
																)
															)
														)
													).join(""),
												}
											)
										)
									)
								).join(""),
							}
						)
					)
				)
			).join("");
		}
		
		
		/**
		 */
		private async update(
			year : int,
			week : int,
			count : int,
			options : {
				update_controls ?: boolean;
			} = {}
		) : Promise<void>
		{
			options = Object.assign(
				{
					"update_controls": true,
				},
				options
			);
			const context : Element = this.container;
			// controls
			{
				if (! options.update_controls) {
					// do nothing
				}
				else {
					(context.querySelector(".weekview-control-year > input") as HTMLInputElement).value = year.toFixed(0);
					(context.querySelector(".weekview-control-week > input") as HTMLInputElement).value = week.toFixed(0);
					(context.querySelector(".weekview-control-count > input") as HTMLInputElement).value = count.toFixed(0);
				}
			}
			// table
			{
				context.querySelector(".weekview-table tbody").innerHTML = await this.table_rows(
					{
						"calendar_ids": null,
						// TODO
						"from": {
							"year": year,
							"week": week
						},
						// TODO
						"to": {
							"year": year,
							"week": (week + count)
						},
						"timezone_shift": /*conf.timezone_shift*/0,
					}
				);
				// cells
				{
					if (! await _dali.backend.is_logged_in()) {
						// do nothing
					}
					else {
						context.querySelectorAll(".weekview-cell-regular").forEach(
							(element) => {
								element.addEventListener(
									"click",
									(event) => {
										if (! (element === event.target)) {
											// do nothing
										}
										else {
											const rel : string = element.getAttribute("rel");
											const parts : Array<string> = rel.split("-");
											const date : lib_plankton.pit.type_date = {
												"year": parseInt(parts[0]),
												"month": parseInt(parts[1]),
												"day": parseInt(parts[2]),
											};
											this.action_select_day(date);
										}
									}
								);
							}
						);
					}
				}
				// events
				{
					if (! await _dali.backend.is_logged_in()) {
						// do nothing
					}
					else {
						context.querySelectorAll(".weekview-event_entry").forEach(
							(element) => {
								element.addEventListener(
									"click",
									() => {
										const rel : string = element.getAttribute("rel");
										const parts : Array<string> = rel.split("/");
										const calendar_id : _dali.type.calendar_id = parseInt(parts[0]);
										const event_id : (null | _dali.type.local_resource_event_id) = (
											(parts[1] === "-")
											?
											null
											:
											parseInt(parts[1])
										);
										const access_level : _dali.type.enum_access_level = (() => {
											switch (parts[2]) {
												case "none": return _dali.type.enum_access_level.none;
												case "view": return _dali.type.enum_access_level.view;
												case "edit": return _dali.type.enum_access_level.edit;
												case "admin": return _dali.type.enum_access_level.admin;
											}
										}) ();
										this.action_select_event(
											calendar_id,
											access_level,
											event_id
										);
									}
								);
							}
						);
					}
				}
			}
			return Promise.resolve<void>(undefined);
		}
		
		
		/**
		 */
		public toggle_visibility(
			calendar_id : _dali.type.calendar_id
		) : void
		{
			this.container.querySelectorAll(".weekview-event_entry").forEach(
				(element) => {
					const rel : string = element.getAttribute("rel");
					const parts : Array<string> = rel.split("/");
					const calendar_id_ : _dali.type.calendar_id = parseInt(parts[0]);
					if (! (calendar_id === calendar_id_)) {
						// do nothing
					}
					else {
						element.classList.toggle("weekview-cell-hidden");
					}
				}
			);
		}
		
		
		/**
		 * [implementation]
		 */
		public async load(
			target_element : Element
		) : Promise<void>
		{
			target_element.innerHTML = await _dali.helpers.template_coin(
				"widget-weekview",
				"main",
				{
					"label_control_year": lib_plankton.translate.get("widget.weekview.controls.year"),
					"label_control_week": lib_plankton.translate.get("widget.weekview.controls.week"),
					"label_control_count": lib_plankton.translate.get("widget.weekview.controls.count"),
					"label_control_apply": lib_plankton.translate.get("widget.weekview.controls.apply"),
					"label_weekday_monday": lib_plankton.translate.get("common.weekday.monday"),
					"label_weekday_tuesday": lib_plankton.translate.get("common.weekday.tuesday"),
					"label_weekday_wednesday": lib_plankton.translate.get("common.weekday.wednesday"),
					"label_weekday_thursday": lib_plankton.translate.get("common.weekday.thursday"),
					"label_weekday_friday": lib_plankton.translate.get("common.weekday.friday"),
					"label_weekday_saturday": lib_plankton.translate.get("common.weekday.saturday"),
					"label_weekday_sunday": lib_plankton.translate.get("common.weekday.sunday"),
				}
			);
			this.container = target_element.querySelector(".weekview");
			// controls
			{
				target_element.querySelector(".weekview-control-apply").addEventListener(
					"click",
					(event) => {
						event.preventDefault();
						const year : int = parseInt((target_element.querySelector(".weekview-control-year > input") as HTMLInputElement).value);
						const week : int = parseInt((target_element.querySelector(".weekview-control-week > input") as HTMLInputElement).value);
						const count : int = parseInt((target_element.querySelector(".weekview-control-count > input") as HTMLInputElement).value);
						this.update(
							year,
							week,
							count,
							{
								"update_controls": false,
							}
						);
					}
				);
			}
			// table
			{
				const ywd_now : lib_plankton.pit.type_ywd = lib_plankton.pit.to_ywd(lib_plankton.pit.now());
				let year : int = ywd_now.year;
				let week : int = Math.max(0, (ywd_now.week - 1));
				let count : int = 5;
				await this.update(
					year,
					week,
					count,
					{
						"update_controls": true,
					}
				);
			}
			return Promise.resolve<void>(undefined);
		}
			
	}
	
}
