--------
----	"Private" (Implementation-Specific) Utility Functions
--------

-- Fetch the language ID for a given language code
CREATE FUNCTION i18n.lang_id(in_language_code text) RETURNS integer
AS $$
	Declare
		result int;
	Begin
		If ( in_language_code Is Null )
		Then
			Raise Exception 'Function i18n.lang_id() does not accept NULL input.';
		End If;
	
		Select Into result
			id
		From	i18n.language
		Where	code = in_language_code;
	
		If ( result Is Null )
		Then
			Raise Exception 'Invalid language code "%".', in_language_code;
		End If;
	
		Return result;
	End;
$$ LANGUAGE plpgsql STABLE;

-- Fetch the language code for a given language ID
CREATE FUNCTION i18n.lang_code(in_language_id integer) RETURNS text
AS $$
	Declare
		result text;
	Begin
		If ( in_language_id Is Null )
		Then
			Raise Exception 'Function i18n.lang_code() does not accept NULL input.';
		End If;
	
		Select Into result
			code
		From	i18n.language
		Where	id = in_language_id;
	
		If ( result Is Null )
		Then
			Raise Exception 'Invalid language ID "%".', in_language_id::text;
		End If;
	
		Return result;
	End;
$$ LANGUAGE plpgsql STABLE;

--------
----	Functions for Retrieving a Particular Translation
--------

-- Basic translation: translate(column, language_code)
CREATE FUNCTION i18n.translate(in_trans_array i18n.translated_text, in_language_code text) RETURNS text
AS $$
	Declare
		test_lang_id int;
		result text;
	Begin
		-- Allow a nullable translated column to show as null, not error
		If ( in_trans_array Is Null )
		Then
			return Null;
		End If;
	
		test_lang_id := i18n.lang_id(in_language_code);	
	
		Loop
			result := in_trans_array[test_lang_id];
			
			If ( 
				result Is Not Null
				And
				result <> ''
			)
			Then
				Return result;
			End If;
	
			If ( 
				Select (fallback_id = id OR fallback_id Is Null)
				From i18n.language
				Where id = test_lang_id
			)
			Then
				Raise Warning 'Translation chain terminated without selecting a value from input.';
				Return Null;
			Else
				Select Into test_lang_id
					fallback_id
				From i18n.language
				Where id = test_lang_id;
			End If;	
		End Loop;
	End;
$$ LANGUAGE plpgsql IMMUTABLE;

-- Set the default language for the current session, using env.set()
CREATE FUNCTION i18n.set_lang(in_language_code text) RETURNS void
AS $$
	Begin
		-- Check value is sane
		If Not Exists (
			Select *
			From	i18n.language
			Where	code = in_language_code
		)
		Then
			Raise Exception 'Invalid language code "%".', in_language_code;
		End If;
	
		Perform env.set('i18n_lang', in_language_code);
	End;
$$ LANGUAGE plpgsql;

-- Simplified translation using session default language retrieved from env.get()
CREATE FUNCTION i18n.translate(in_trans_array i18n.translated_text) RETURNS text
AS
$$
	Begin
		Return i18n.translate( in_trans_array, env.get('i18n_lang') );
	End;
$$ LANGUAGE plpgsql STABLE;

--------
----	Basic Functions for Altering Translated Text
--------

Create Function i18n.set_translation(in_trans_array i18n.translated_text, in_language_code text, in_translation text)
	Returns i18n.translated_text
AS
$$
	Declare
		target_index Int;
		highest_index Int;
		out_trans_array i18n.translated_text;
	Begin
		target_index := i18n.lang_id(in_language_code);
		If ( target_index Is Null )
		Then
			Raise Exception 'Invalid language code "%".', in_language_code;
		End If;
		
		out_trans_array := in_trans_array;
		
		-- Ensure the array is long enough, by adding one element at a time
		Loop
			highest_index := coalesce( array_upper(out_trans_array, 1), 0 );
			Exit When highest_index >= target_index;
		
			out_trans_array[highest_index + 1] := '';
		End Loop;
		
		out_trans_array[ target_index ] := in_translation;
		
		Return out_trans_array;
	End;
$$ LANGUAGE plpgsql STABLE;

Create Function i18n.delete(in_trans_array i18n.translated_text, in_language_code text)
	Returns i18n.translated_text
AS
$$
	Declare
		out_trans_array i18n.translated_text;
		target_index Int;
	Begin
		target_index := i18n.lang_id(in_language_code);
		
		-- Language clearly isn't set in input array, since the array's not that long!
		If ( target_index > array_upper(in_trans_array, 1) )
		Then
			Return in_trans_array;
		End If;
		
		out_trans_array := in_trans_array;
		out_trans_array[ target_index ] := '';
		
		Return out_trans_array;
	End;
$$ LANGUAGE plpgsql STABLE;

-- Adds any non-empty values from the second translated text value to the first
Create Function i18n.combine(in_trans_array_1 i18n.translated_text, in_trans_array_2 i18n.translated_text)
	Returns i18n.translated_text
AS
$$
	Declare
		out_trans_array i18n.translated_text;
		language_id Int;
	Begin
		out_trans_array := in_trans_array_1;
	
		For language_id In 1 .. coalesce( array_upper(in_trans_array_2, 1), 0 )
		Loop
			If ( 
				in_trans_array_2[language_id] Is Not Null
				And
				in_trans_array_2[language_id] <> ''
			)
			Then
				out_trans_array := i18n.set_translation(
					out_trans_array,
					i18n.lang_code(language_id),
					in_trans_array_2[language_id]
				);
			End If;
		End Loop;
	
		Return out_trans_array;
	End;
$$ LANGUAGE plpgsql STABLE;

--------
----	Functions for Creating Translated Text Values
--------

-- The simplest kind of translated text: one language 
Create Function i18n.translation_pair(in_language_code text, in_translation text)
	Returns i18n.translated_text
AS
$$
	Begin
		Return i18n.set_translation('{}'::i18n.translated_text, in_language_code, in_translation);
	End;
$$ LANGUAGE plpgsql STABLE;

-- Create a translated text value from a list of (language_code, translation) pairs
-- e.g. Select i18n.list_to_translation( ARRAY[ ['en','Hello'], ['fr','Bonjour'] ] );
Create Function i18n.list_to_translation(in_list_of_pairs text[][])
	Returns i18n.translated_text
AS
$$
	Declare
		out_trans_array i18n.translated_text;
		loop_index Int;
	Begin
		-- Check value is sane
		If (
			array_upper(in_list_of_pairs, 2) Is Null
			Or
			array_upper(in_list_of_pairs, 2) <> 2
		)
		Then
			Raise Exception 'Function list_to_translation() expects an N by 2 array; N by % given.', 
				coalesce(array_upper(in_list_of_pairs, 2)::text, 'NULL');
		End If;
	
		out_trans_array := '{}'::i18n.translated_text;
	
		For loop_index In 1 .. coalesce( array_upper(in_list_of_pairs, 1), 0 )
		Loop
			out_trans_array := i18n.set_translation(
				out_trans_array,
				in_list_of_pairs[loop_index][1],
				in_list_of_pairs[loop_index][2]
			);
		End Loop;
	
		Return out_trans_array;
	End;
$$ LANGUAGE plpgsql STABLE;

-- Output a translated text value as a list of (language_code, translation) pairs
-- Useful for selecting all current translations of a string for editting in an application
Create Function i18n.translation_to_list(in_trans_array i18n.translated_text)
	Returns text[][]
AS
$$
	Declare
		out_list_of_pairs text[][];
		language_id Int;
	Begin
		out_list_of_pairs := '{}'::text[][];
	
		For language_id In 1 .. coalesce( array_upper(in_trans_array, 1), 0 )
		Loop
			If (
				in_trans_array[language_id] Is Not Null
				And
				in_trans_array[language_id] <> ''
			)
			Then
				out_list_of_pairs := out_list_of_pairs 
					|| 
					ARRAY[ 
						[ i18n.lang_code(language_id), in_trans_array[language_id] ] 
					];
			End If;
		End Loop;
	
		Return out_list_of_pairs;
	End;
$$ LANGUAGE plpgsql STABLE;
