/**
 */
namespace _dali.backend
{
	
	/**
	 */
	var _data_chest : (
		null
		|
		lib_plankton.storage.type_chest<string, string, void, string, string>
	) = null;
	
	
	/**
	 */
	function access_level_encode(
		access_level : _dali.type.enum_access_level
	) : ("none" | "view" | "edit" | "admin")
	{
		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";
		}
	}
	
	
	/**
	 */
	function access_level_decode(
		access_level_encoded : ("none" | "view" | "edit" | "admin")
	) : _dali.type.enum_access_level
	{
		switch (access_level_encoded) {
			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;
		}
	}
	
	
	/**
	 */
	export async function init(
	) : Promise<void>
	{
		_data_chest = lib_plankton.storage.localstorage.implementation_chest(
			{
				"corner": "zeitbild",
			}
		);
		return Promise.resolve<void>(undefined);
	}
	
	
	/**
	 */
	async function get_session_key(
	) : Promise<(null | string)>
	{
		try
		{
			return (await _data_chest.read("session_key"));
		}
		catch (error)
		{
			return null;
		}
	}
	
	
	/**
	 */
	async function call(
		method : lib_plankton.http.enum_method,
		action : string,
		input : (null | any)
	) : Promise<any>
	{
		const with_body : boolean = (
			[
				lib_plankton.http.enum_method.post,
				lib_plankton.http.enum_method.put,
				lib_plankton.http.enum_method.patch,
			].includes(method)
		);
		const session_key : (null | string) = await get_session_key();
		const http_request : lib_plankton.http.type_request = {
			"version": "HTTP/2",
			"scheme": (
				(_dali.conf.get()["backend"]["scheme"] === "http")
				?
				"http"
				:
				"https"
			),
			"host": lib_plankton.string.coin(
				"{{host}}:{{port}}",
				{
					"host": _dali.conf.get()["backend"]["host"],
					"port": _dali.conf.get()["backend"]["port"].toFixed(0),
				}
			),
			"path": lib_plankton.string.coin(
				"{{base}}{{action}}",
				{
					"base": _dali.conf.get()["backend"]["path"],
					"action": action,
				}
			),
			"method": method,
			"query": (
				(with_body || (input === null))
				?
				null
				:
				("?" + lib_plankton.www_form.encode(input))
			),
			"headers": Object.assign(
				{},
				(
					(! with_body)
					?
					{}
					:
					{"Content-Type": "application/json"}
				),
				(
					(session_key === null)
					?
					{}
					:
					{"X-Session-Key": session_key}
				)
			),
			"body": (
				((! with_body) || (input === null))
				?
				null
				:
				/*Buffer.from*/(lib_plankton.json.encode(input))
			),
		};
		const http_response : lib_plankton.http.type_response = await lib_plankton.http.call(http_request);
		if (
			! (
				(http_response.status_code >= 200)
				&&
				(http_response.status_code < 300)
			)
		) {
			return Promise.reject<any>(http_response.body.toString());
		}
		else {
			const output : any = lib_plankton.json.decode(http_response.body.toString());
			return Promise.resolve<any>(output);
		}
	}

	
	/**
	 */
	export async function is_logged_in(
	) : Promise<boolean>
	{
		// return ((await get_session_key()) !== null);
		const result : {
			logged_in : boolean;
		} = await call(
			lib_plankton.http.enum_method.get,
			"/session/status",
			null
		);
		return result.logged_in;
	}
	
	
	/**
	 */
	export async function session_prepare(
		input : any
	) : Promise<{kind : string; data : any;}>
	{
		return call(
			lib_plankton.http.enum_method.post,
			"/session/prepare",
			input
		);
	}
	
	
	/**
	 */
	export function set_session_key(
		session_key : string
	) : Promise<void>
	{
		return (
			_data_chest.write("session_key", session_key)
			.then<void>(() => Promise.resolve<void>(undefined))
		);
	}
	
	/**
	 */
	export async function session_begin(
		name : string,
		password : string
	) : Promise<void>
	{
		const session_key : string = await call(
			lib_plankton.http.enum_method.post,
			"/session/begin",
			{
				"name": name,
				"password": password,
			}
		);
		await _data_chest.write("session_key", session_key);
		return Promise.resolve<void>(undefined);
	}
	
	
	/**
	 */
	export async function session_end(
	) : Promise<void>
	{
		await call(
			lib_plankton.http.enum_method.delete,
			"/session/end",
			null
		);
		await _data_chest.delete("session_key");
		return Promise.resolve<void>(undefined);
	}
	
	
	/**
	 */
	export function user_list(
	) : Promise<
		Array<
			{
				id : int;
				name : string;
			}
		>
	>
	{
		return call(
			lib_plankton.http.enum_method.get,
			"/users",
			null
		);
	}
	
	
	/**
	 */
	export function user_dav_conf(
	) : Promise<
		(
			null
			|
			{
				address : string;
				username : string;
				password : string;
				setup_hints : Array<
					{
						label : string;
						link : string;
						remark : (null | string);
					}
				>;
			}
		)
	>
	{
		return call(
			lib_plankton.http.enum_method.get,
			"/user_dav_conf",
			null
		);
	}
	
	
	/**
	 */
	export function user_dav_token(
	) : Promise<void>
	{
		return call(
			lib_plankton.http.enum_method.patch,
			"/user_dav_token",
			null
		);
	}
	
	
	/**
	 */
	export async function calendar_list(
	) : Promise<
		Array<
			{
				id : int;
				name : string;
				access_level : _dali.type.enum_access_level;
			}
		>
	>
	{
		return (
			call(
				lib_plankton.http.enum_method.get,
				"/calendar",
				null
			)
			.then(
				(entries) => Promise.resolve(
					entries
					.map(
						(entry) => ({
							"id": entry.id,
							"name": entry.name,
							"access_level": access_level_decode(entry.access_level),
						})
					)
				)
			)
		);
	}
	
	
	/**
	 */
	export async function calendar_get(
		calendar_id : _dali.type.calendar_id
	) : Promise<
		_dali.type.calendar_object
	>
	{
		return (
			call(
				lib_plankton.http.enum_method.get,
				lib_plankton.string.coin(
					"/calendar/{{calendar_id}}",
					{
						"calendar_id": calendar_id.toFixed(0),
					}
				),
				null
			)
			.then(
				(raw) => Promise.resolve(
					{
						"name": raw.name,
						"access": {
							"public": raw.access.public,
							"default_level": access_level_decode(raw.access.default_level),
							"attributed": lib_plankton.map.hashmap.implementation_map(
								lib_plankton.map.hashmap.make(
									x => x.toFixed(0),
									{
										"pairs": (
											raw.access.attributed
											.map(
												(entry) => ({
													"key": entry.user_id,
													"value": access_level_decode(entry.level),
												})
											)
										),
									}
								)
							),
						},
						// "resource_id": raw.resource_id
						// TODO
						"resource": {
							"kind": "local",
							"data": {
								"events": []
							}
						}
					}
				)
			)
		);
	}
	
	
	/**
	 */
	export async function calendar_add(
		calendar_object : _dali.type.calendar_object
	) : Promise<
		_dali.type.calendar_id
	>
	{
		return call(
			lib_plankton.http.enum_method.post,
			lib_plankton.string.coin(
				"/calendar",
				{
				}
			),
			{
				"name": calendar_object.name,
				"access": {
					"public": calendar_object.access.public,
					"default_level": access_level_encode(calendar_object.access.default_level),
					"attributed": (
						lib_plankton.map.dump(calendar_object.access.attributed)
						.map(
							(pair) => ({
								"user_id": pair.key,
								"level": access_level_encode(pair.value),
							})
						)
					)
				},
				"resource": calendar_object.resource,
			}
		);
	}
	
	
	/**
	 */
	export async function calendar_change(
		calendar_id : _dali.type.calendar_id,
		calendar_object : _dali.type.calendar_object
	) : Promise<
		void
	>
	{
		return call(
			lib_plankton.http.enum_method.put,
			lib_plankton.string.coin(
				"/calendar/{{calendar_id}}",
				{
					"calendar_id": calendar_id.toFixed(0),
				}
			),
			{
				"name": calendar_object.name,
				"access": {
					"public": calendar_object.access.public,
					"default_level": access_level_encode(calendar_object.access.default_level),
					"attributed": (
						lib_plankton.map.dump(calendar_object.access.attributed)
						.map(
							(pair) => ({
								"user_id": pair.key,
								"level": access_level_encode(pair.value),
							})
						)
					)
				},
				"resource": calendar_object.resource,
			}
		);
	}
	
	
	/**
	 */
	export async function calendar_remove(
		calendar_id : _dali.type.calendar_id
	) : Promise<
		void
	>
	{
		return call(
			lib_plankton.http.enum_method.delete,
			lib_plankton.string.coin(
				"/calendar/{{calendar_id}}",
				{
					"calendar_id": calendar_id.toFixed(0),
				}
			),
			null
		);
	}
	
	
	/**
	 */
	export async function calendar_event_get(
		calendar_id : _dali.type.calendar_id,
		event_id : _dali.type.local_resource_event_id
	) : Promise<_dali.type.event_object>
	{
		return call(
			lib_plankton.http.enum_method.get,
			lib_plankton.string.coin(
				"/calendar/{{calendar_id}}/event/{{event_id}}",
				{
					"calendar_id": calendar_id.toFixed(0),
					"event_id": event_id.toFixed(0),
				}
			),
			null
		);
	}
	
	
	/**
	 */
	export async function calendar_event_add(
		calendar_id : _dali.type.calendar_id,
		event_object : _dali.type.event_object
	) : Promise<void>
	{
		return call(
			lib_plankton.http.enum_method.post,
			lib_plankton.string.coin(
				"/calendar/{{calendar_id}}/event",
				{
					"calendar_id": calendar_id.toFixed(0),
				}
			),
			event_object
		);
	}
	
	
	/**
	 */
	export async function calendar_event_change(
		calendar_id : _dali.type.calendar_id,
		event_id : _dali.type.local_resource_event_id,
		event_object : _dali.type.event_object
	) : Promise<void>
	{
		return call(
			lib_plankton.http.enum_method.put,
			lib_plankton.string.coin(
				"/calendar/{{calendar_id}}/event/{{event_id}}",
				{
					"calendar_id": calendar_id.toFixed(0),
					"event_id": event_id.toFixed(0),
				}
			),
			event_object
		);
	}
	
	
	/**
	 */
	export async function calendar_event_remove(
		calendar_id : _dali.type.calendar_id,
		event_id : _dali.type.local_resource_event_id
	) : Promise<void>
	{
		return call(
			lib_plankton.http.enum_method.delete,
			lib_plankton.string.coin(
				"/calendar/{{calendar_id}}/event/{{event_id}}",
				{
					"calendar_id": calendar_id.toFixed(0),
					"event_id": event_id.toFixed(0),
				}
			),
			null
		);
	}
	
	
	/**
	 * @todo prevent loops
	 */
	export async function events(
		from_pit : lib_plankton.pit.type_pit,
		to_pit : lib_plankton.pit.type_pit,
		options : {
			calendar_ids ?: (null | Array<_dali.type.calendar_id>);
		} = {}
	) : Promise<
		Array<
			{
				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;
			}
		>
	>
	{
		options = Object.assign(
			{
				"calendar_ids": null,
			},
			options
		);
		
		return (
			call(
				lib_plankton.http.enum_method.get,
				"/events",
				Object.assign(
					{
						"from": from_pit,
						"to": to_pit,
					},
					(
						(options.calendar_ids === null)
						?
						{}
						:
						{"calendar_ids": options.calendar_ids.join(",")}
					)
				)
			)
			.then(
				(data) => Promise.resolve(
					data
					.map(
						(entry) => ({
							"calendar_id": entry.calendar_id,
							"calendar_name": entry.calendar_name,
							"access_level": access_level_decode(entry.access_level),
							"event_id": entry.event_id,
							"event_object": entry.event_object,
						})
					)
				)
			)
		);
	}

}
