
namespace _zeitbild.repository.resource
{
	
	/**
	 */
	type type_local_resource_event_stuff = {
		event : _zeitbild.type_event_object;
		local_resource_id : int;
	}
	
	
	/**
	 */
	var _local_resource_event_store : (
		null
		|
		lib_plankton.storage.type_store<
			int,
			Record<string, any>,
			{},
			lib_plankton.storage.type_sql_table_autokey_search_term,
			Record<string, any>
		>
	) = null;
	
	
	/**
	 */
	var _local_resource_core_store : (
		null
		|
		lib_plankton.storage.type_store<
			int,
			Record<string, any>,
			{},
			lib_plankton.storage.type_sql_table_autokey_search_term,
			Record<string, any>
		>
	) = null;
	
	
	/**
	 */
	var _caldav_resource_store : (
		null
		|
		lib_plankton.storage.type_store<
			int,
			Record<string, any>,
			{},
			lib_plankton.storage.type_sql_table_autokey_search_term,
			Record<string, any>
		>
	) = null;
	
	
	/**
	 */
	var _resource_core_store : (
		null
		|
		lib_plankton.storage.type_store<
			_zeitbild.type_resource_id,
			Record<string, any>,
			{},
			lib_plankton.storage.type_sql_table_autokey_search_term,
			Record<string, any>
		>
	) = null;
	
	
	/**
	 */
	function get_local_resource_event_store(
	) : lib_plankton.storage.type_store<
		int,
		Record<string, any>,
		{},
		lib_plankton.storage.type_sql_table_autokey_search_term,
		Record<string, any>
	>
	{
		if (_local_resource_event_store === null) {
			_local_resource_event_store = lib_plankton.storage.sql_table_autokey_store(
				{
					"database_implementation": _zeitbild.database.get_implementation(),
					"table_name": "local_resource_events",
					"key_name": "id",
				}
			);
		}
		else {
			// do nothing
		}	
		return _local_resource_event_store;
	}
	
	
	/**
	 */
	function get_local_resource_core_store(
	) : lib_plankton.storage.type_store<
		int,
		Record<string, any>,
		{},
		lib_plankton.storage.type_sql_table_autokey_search_term,
		Record<string, any>
	>
	{
		if (_local_resource_core_store === null) {
			_local_resource_core_store = lib_plankton.storage.sql_table_autokey_store(
				{
					"database_implementation": _zeitbild.database.get_implementation(),
					"table_name": "local_resources",
					"key_name": "id",
				}
			);
		}
		else {
			// do nothing
		}	
		return _local_resource_core_store;
	}
	
	
	/**
	 */
	function get_caldav_resource_store(
	) : lib_plankton.storage.type_store<
		int,
		Record<string, any>,
		{},
		lib_plankton.storage.type_sql_table_autokey_search_term,
		Record<string, any>
	>
	{
		if (_caldav_resource_store === null) {
			_caldav_resource_store = lib_plankton.storage.sql_table_autokey_store(
				{
					"database_implementation": _zeitbild.database.get_implementation(),
					"table_name": "caldav_resources",
					"key_name": "id",
				}
			);
		}
		else {
			// do nothing
		}	
		return _caldav_resource_store;
	}
	
	
	/**
	 */
	function get_resource_core_store(
	) : lib_plankton.storage.type_store<
		_zeitbild.type_resource_id,
		Record<string, any>,
		{},
		lib_plankton.storage.type_sql_table_autokey_search_term,
		Record<string, any>
	>
	{
		if (_resource_core_store === null) {
			_resource_core_store = lib_plankton.storage.sql_table_autokey_store(
				{
					"database_implementation": _zeitbild.database.get_implementation(),
					"table_name": "resources",
					"key_name": "id",
				}
			);
		}
		else {
			// do nothing
		}	
		return _resource_core_store;
	}
	
	
	/**
	 */
	function encode_local_resource_event(
		stuff : type_local_resource_event_stuff
	) : Record<string, any>
	{
		const encode_datetime : ((datetime : lib_plankton.pit.type_datetime) => string) = ((datetime) => {
			return lib_plankton.string.coin(
				"{{timezone_shift}}|{{date}}{{macro_time}}",
				{
					"timezone_shift": datetime.timezone_shift.toFixed(0).padStart(2, "0"),
					"date": lib_plankton.string.coin(
						"{{year}}-{{month}}-{{day}}",
						{
							"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"),
						}
					),
					"macro_time": (
						(datetime.time === null)
						?
						""
						:
						lib_plankton.string.coin(
							"T{{hour}}:{{minute}}:{{second}}",
							{
								"hour": datetime.time.hour.toFixed(0).padStart(2, "0"),
								"minute": datetime.time.minute.toFixed(0).padStart(2, "0"),
								"second": datetime.time.second.toFixed(0).padStart(2, "0"),
							}
						)
					),
				}
			);
		});
		return {
			"local_resource_id": stuff.local_resource_id,
			"name": stuff.event.name,
			"begin": encode_datetime(stuff.event.begin),
			"end": (
				(stuff.event.end === null)
				?
				null
				:
				encode_datetime(stuff.event.end)
			),
			"location": stuff.event.location,
			"link": stuff.event.link,
			"description": stuff.event.description,
		}
	}
	
	
	/**
	 */
	function decode_local_resource_event(
		row : Record<string, any>
	) : type_local_resource_event_stuff
	{
		const decode_datetime : ((datetime_raw : string) => lib_plankton.pit.type_datetime) = ((datetime_raw) => {
			const parts : Array<string> = datetime_raw.split("|");
			const timezone_shift : int = parseInt(parts[0]);
			if (parts[1].length <= 10) {
				return {
					"timezone_shift": timezone_shift,
					"date": {
						"year": parseInt(parts[1].slice(0, 4)),
						"month": parseInt(parts[1].slice(5, 7)),
						"day": parseInt(parts[1].slice(8, 10)),
					},
					"time": null
				};
			}
			else {
				return {
					"timezone_shift": timezone_shift,
					"date": {
						"year": parseInt(parts[1].slice(0, 4)),
						"month": parseInt(parts[1].slice(5, 7)),
						"day": parseInt(parts[1].slice(8, 10)),
					},
					"time": {
						"hour": parseInt(parts[1].slice(11, 13)),
						"minute": parseInt(parts[1].slice(14, 16)),
						"second": parseInt(parts[1].slice(17, 19)),
					}
				};
			}
		});
		return {
			"local_resource_id": row["local_resource_id"],
			"event": {
				"name": row["name"],
				"begin": decode_datetime(row["begin"]),
				"end": (
					(row["end"] === null)
					?
					null
					:
					decode_datetime(row["end"])
				),
				"location": row["location"],
				"link": row["link"],
				"description": row["description"],
			}
		};
	}
	
	
	/**
	 */
	/*
	function encode_resource_core(
		stuff : {
			object : _zeitbild.type_resource_object;
			sub_id : int;
		}
	) : Record<string, any>
	{
		return {
			"kind": stuff.object.kind,
			"sub_id": stuff.sub_id
		};
	}
	 */
	
	
	/**
	 * @todo data
	 */
	/*
	function decode_resource_core(
		row : Record<string, any>
	) : {
		object : _zeitbild.type_resource_object;
		sub_id : int;
	}
	{
		return {
			"object": {
				"kind": row["kind"],
				"data": null,
			},
			"sub_id": row["sub_id"],
		};
	}
	 */
	
	
	/**
	 */
	export async function read(
		resource_id : _zeitbild.type_resource_id
	) : Promise<_zeitbild.type_resource_object>
	{
		const dataset_core : Record<string, any> = await get_resource_core_store().read(resource_id);
		switch (dataset_core.kind) {
			case "local": {
				const dataset_extra_local_core : Record<string, any> = await get_local_resource_core_store().read(dataset_core.sub_id);
				const datasets_extra_local_event_ids : Array<Record<string, any>> = await get_local_resource_event_store().search(
					{
						"expression": "(local_resource_id = $local_resource_id)",
						"arguments": {
							"local_resource_id": dataset_core.sub_id,
						}
					}
				);
				return Promise.resolve<_zeitbild.type_resource_object>(
					{
						"kind": "local",
						"data": {
							"event_ids": (
								datasets_extra_local_event_ids
								.map((hit) => hit.preview["id"])
							)
						}
					}
				);
			}
			case "caldav": {
				const dataset_extra_caldav : Record<string, any> = await get_caldav_resource_store().read(dataset_core.sub_id);
				return Promise.resolve<_zeitbild.type_resource_object>(
					{
						"kind": "caldav",
						"data": {
							"url": dataset_extra_caldav["url"],
							"read_only": dataset_extra_caldav["read_only"],
							"from_fucked_up_wordpress": dataset_extra_caldav["from_fucked_up_wordpress"],
						}
					}
				);
				break;
			}
			default: {
				return Promise.reject<_zeitbild.type_resource_object>(
					new Error("invalid resource kind: " + dataset_core.kind)
				);
				break;
			}
		}
	}
	
	
	/**
	 */
	export async function create(
		resource_object : _zeitbild.type_resource_object
	) : Promise<_zeitbild.type_resource_id>
	{
		switch (resource_object.kind) {
			case "local": {
				const local_resource_id : int = await get_local_resource_core_store().create(
					{
						"_dummy": null,
					}
				);
				const resource_id : _zeitbild.type_resource_id = await get_resource_core_store().create(
					{
						"kind": "local",
						"sub_id": local_resource_id,
					}
				);
				await _zeitbild.cache.clear();
				return Promise.resolve<_zeitbild.type_resource_id>(resource_id);
				break;
			}
			case "caldav": {
				const caldav_resource_id : int = await get_caldav_resource_store().create(
					{
						"url": resource_object.data.url,
						"read_only": resource_object.data.read_only,
						"from_fucked_up_wordpress": resource_object.data.from_fucked_up_wordpress,
					}
				);
				const resource_id : _zeitbild.type_resource_id = await get_resource_core_store().create(
					{
						"kind": "caldav",
						"sub_id": caldav_resource_id,
					}
				);
				await _zeitbild.cache.clear();
				return Promise.resolve<_zeitbild.type_resource_id>(resource_id);
				break;
			}
			default: {
				// @ts-ignore
				throw (new Error("invalid resource kind: " + resource_object.kind));
				break;
			}
		}
	}
	
	
	/**
	 * @todo allow kind change?
	 */
	export async function update(
		resource_id : _zeitbild.type_resource_id,
		resource_object : _zeitbild.type_resource_object
	) : Promise<void>
	{
		const dataset_core : Record<string, any> = await get_resource_core_store().read(resource_id);
		if (dataset_core["kind"] !== resource_object.kind) {
			return Promise.reject(new Error("resource kind may not be altered"));
		}
		else {
			switch (resource_object.kind) {
				case "local": {
					// event_id list may not be altered directly
					/*
					const current_event_id_rows : {key : int; preview : Array<Record<string, any>>;} = await get_local_resource_event_chest().search(
						{
							"expression": "(local_resource_id = $local_resource_id)",
							"arguments": {
								"local_resource_id": dataset_core["sub_id"]
							}
						}
					);
					const contrast : {
						only_left : Array<{key : int; left : any;}>;
						both : Array<{key : int; left : any; right : any;}>;
						only_right : Array<{key : int; right : any;}>;
					} = lib_plankton.list.contrast<
						{key : Array<any>; preview : Record<string, any>;},
						_zeitbild.type_local_resource_event_id
					>(
						event_rows,
						(hit => hit.key),
						resource_object.data.event_ids,
						(x => x)
					);
					// TODO: single delete?
					for await (const entry of constrast.only_left) {
						await get_local_resource_event_store().delete(
							entry.key
						);
					}
					/*
					for await (const entry of contrast.both) {
						await get_local_resource_event_store().update(
							entry.left.key,
							encode_local_resource_event(entry.right.object)
						);
					}
					for await (const entry of contrast.only_right) {
						const event_id : type_local_resource_event_id = await get_local_resource_event_store().create(
							encode_local_resource_event(entry.right.object)
						);
					}
					 */
					break;
				}
				case "caldav": {
					await get_caldav_resource_store().update(
						dataset_core["sub_id"],
						{
							"url": resource_object.data.url,
							"read_only": resource_object.data.read_only,
						}
					);
					await _zeitbild.cache.clear();
					break;
				}
				default: {
					// @ts-ignore
					throw (new Error("invalid resource kind: " + resource_object.kind));
					break;
				}
			}
		}
	}
	
	
	/**
	 */
	export async function local_resource_event_read(
		resource_id : _zeitbild.type_resource_id,
		local_resource_event_id : _zeitbild.type_local_resource_event_id
	) : Promise<_zeitbild.type_event_object>
	{
		const dataset_core : Record<string, any> = await get_resource_core_store().read(resource_id);
		if (! (dataset_core.kind === "local")) {
			throw (new Error("not a local resource"));
		}
		else {
			return (
				get_local_resource_event_store().read(
					local_resource_event_id
				)
				.then(
					(row) => Promise.resolve(decode_local_resource_event(row))
				)
				.then(
					(local_resource_event_stuff : type_local_resource_event_stuff) => Promise.resolve(
						local_resource_event_stuff.event
					)
				)
			);
		}
	}
	
	
	/**
	 */
	export async function local_resource_event_create(
		resource_id : _zeitbild.type_resource_id,
		event_object : _zeitbild.type_event_object
	) : Promise<_zeitbild.type_local_resource_event_id>
	{
		const dataset_core : Record<string, any> = await get_resource_core_store().read(resource_id);
		if (! (dataset_core.kind === "local")) {
			throw (new Error("not a local resource"));
		}
		else {
			const local_resource_event_id: _zeitbild.type_local_resource_event_id = await get_local_resource_event_store().create(
				encode_local_resource_event(
					{
						"local_resource_id": dataset_core["sub_id"],
						"event": event_object,
					}
				)
			);
			await _zeitbild.cache.clear();
			return Promise.resolve(local_resource_event_id);
		}
	}
	
	
	/**
	 */
	export async function local_resource_event_update(
		resource_id : _zeitbild.type_resource_id,
		event_id : _zeitbild.type_local_resource_event_id,
		event_object : _zeitbild.type_event_object
	) : Promise<void>
	{
		const dataset_core : Record<string, any> = await get_resource_core_store().read(resource_id);
		if (! (dataset_core.kind === "local")) {
			throw (new Error("not a local resource"));
		}
		else {
			await get_local_resource_event_store().update(
				event_id,
				encode_local_resource_event(
					{
						"local_resource_id": dataset_core["sub_id"],
						"event": event_object,
					}
				)
			);
			await _zeitbild.cache.clear();
			return Promise.resolve<void>(undefined);
		}
	}
	
	
	/**
	 */
	export async function local_resource_event_delete(
		resource_id : _zeitbild.type_resource_id,
		local_resource_event_id : _zeitbild.type_local_resource_event_id
	) : Promise<void>
	{
		await _zeitbild.cache.clear();
		const dataset_core : Record<string, any> = await get_resource_core_store().read(resource_id);
		if (! (dataset_core.kind === "local")) {
			throw (new Error("not a local resource"));
		}
		else {
			return (
				get_local_resource_event_store().delete(
					local_resource_event_id
				)
				.then(
					() => Promise.resolve(undefined)
				)
			);
		}
	}
	
}
