/*
 *  oFono - Open Source Telephony - RIL-based devices
 *
 *  Copyright (C) 2015-2021 Jolla Ltd.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

#include "ril_util.h"
#include "ril_log.h"

#include <grilio_channel.h>

#include <gutil_misc.h>

#include <ofono/netreg.h>
#include <ofono/misc.h>
#include <ctype.h>

#define RIL_PROTO_IP_STR "IP"
#define RIL_PROTO_IPV6_STR "IPV6"
#define RIL_PROTO_IPV4V6_STR "IPV4V6"

const char *ril_error_to_string(int error)
{
#define RIL_E_(name) case RIL_E_##name: return #name
#define GRILIO_E_(name) case GRILIO_STATUS_##name: return "GRILIO_" #name
	static char unknown[12];
	switch (error) {
	case RIL_E_SUCCESS: return "OK";
	GRILIO_E_(TIMEOUT);
	GRILIO_E_(CANCELLED);
	RIL_E_(RADIO_NOT_AVAILABLE);
	RIL_E_(GENERIC_FAILURE);
	RIL_E_(PASSWORD_INCORRECT);
	RIL_E_(SIM_PIN2);
	RIL_E_(SIM_PUK2);
	RIL_E_(REQUEST_NOT_SUPPORTED);
	RIL_E_(CANCELLED);
	RIL_E_(OP_NOT_ALLOWED_DURING_VOICE_CALL);
	RIL_E_(OP_NOT_ALLOWED_BEFORE_REG_TO_NW);
	RIL_E_(SMS_SEND_FAIL_RETRY);
	RIL_E_(SIM_ABSENT);
	RIL_E_(SUBSCRIPTION_NOT_AVAILABLE);
	RIL_E_(MODE_NOT_SUPPORTED);
	RIL_E_(FDN_CHECK_FAILURE);
	RIL_E_(ILLEGAL_SIM_OR_ME);
	RIL_E_(MISSING_RESOURCE);
	RIL_E_(NO_SUCH_ELEMENT);
	RIL_E_(DIAL_MODIFIED_TO_USSD);
	RIL_E_(DIAL_MODIFIED_TO_SS);
	RIL_E_(DIAL_MODIFIED_TO_DIAL);
	RIL_E_(USSD_MODIFIED_TO_DIAL);
	RIL_E_(USSD_MODIFIED_TO_SS);
	RIL_E_(USSD_MODIFIED_TO_USSD);
	RIL_E_(SS_MODIFIED_TO_DIAL);
	RIL_E_(SS_MODIFIED_TO_USSD);
	RIL_E_(SUBSCRIPTION_NOT_SUPPORTED);
	RIL_E_(SS_MODIFIED_TO_SS);
	RIL_E_(LCE_NOT_SUPPORTED);
	RIL_E_(NO_MEMORY);
	RIL_E_(INTERNAL_ERR);
	RIL_E_(SYSTEM_ERR);
	RIL_E_(MODEM_ERR);
	RIL_E_(INVALID_STATE);
	RIL_E_(NO_RESOURCES);
	RIL_E_(SIM_ERR);
	RIL_E_(INVALID_ARGUMENTS);
	RIL_E_(INVALID_SIM_STATE);
	RIL_E_(INVALID_MODEM_STATE);
	RIL_E_(INVALID_CALL_ID);
	RIL_E_(NO_SMS_TO_ACK);
	RIL_E_(NETWORK_ERR);
	RIL_E_(REQUEST_RATE_LIMITED);
	RIL_E_(SIM_BUSY);
	RIL_E_(SIM_FULL);
	RIL_E_(NETWORK_REJECT);
	RIL_E_(OPERATION_NOT_ALLOWED);
	RIL_E_(EMPTY_RECORD);
	RIL_E_(INVALID_SMS_FORMAT);
	RIL_E_(ENCODING_ERR);
	RIL_E_(INVALID_SMSC_ADDRESS);
	RIL_E_(NO_SUCH_ENTRY);
	RIL_E_(NETWORK_NOT_READY);
	RIL_E_(NOT_PROVISIONED);
	RIL_E_(NO_SUBSCRIPTION);
	RIL_E_(NO_NETWORK_FOUND);
	RIL_E_(DEVICE_IN_USE);
	RIL_E_(ABORTED);
	RIL_E_(INVALID_RESPONSE);
	default:
		snprintf(unknown, sizeof(unknown), "%d", error);
		return unknown;
	}
}

const char *ril_request_to_string(guint request)
{
#define RIL_REQUEST_(name) case RIL_REQUEST_##name: return #name
	static char unknown[24];
	switch (request) {
	RIL_REQUEST_(GET_SIM_STATUS);
	RIL_REQUEST_(ENTER_SIM_PIN);
	RIL_REQUEST_(ENTER_SIM_PUK);
	RIL_REQUEST_(ENTER_SIM_PIN2);
	RIL_REQUEST_(ENTER_SIM_PUK2);
	RIL_REQUEST_(CHANGE_SIM_PIN);
	RIL_REQUEST_(CHANGE_SIM_PIN2);
	RIL_REQUEST_(ENTER_NETWORK_DEPERSONALIZATION);
	RIL_REQUEST_(GET_CURRENT_CALLS);
	RIL_REQUEST_(DIAL);
	RIL_REQUEST_(GET_IMSI);
	RIL_REQUEST_(HANGUP);
	RIL_REQUEST_(HANGUP_WAITING_OR_BACKGROUND);
	RIL_REQUEST_(HANGUP_FOREGROUND_RESUME_BACKGROUND);
	RIL_REQUEST_(SWITCH_HOLDING_AND_ACTIVE);
	RIL_REQUEST_(CONFERENCE);
	RIL_REQUEST_(UDUB);
	RIL_REQUEST_(LAST_CALL_FAIL_CAUSE);
	RIL_REQUEST_(SIGNAL_STRENGTH);
	RIL_REQUEST_(VOICE_REGISTRATION_STATE);
	RIL_REQUEST_(DATA_REGISTRATION_STATE);
	RIL_REQUEST_(OPERATOR);
	RIL_REQUEST_(RADIO_POWER);
	RIL_REQUEST_(DTMF);
	RIL_REQUEST_(SEND_SMS);
	RIL_REQUEST_(SEND_SMS_EXPECT_MORE);
	RIL_REQUEST_(SETUP_DATA_CALL);
	RIL_REQUEST_(SIM_IO);
	RIL_REQUEST_(SEND_USSD);
	RIL_REQUEST_(CANCEL_USSD);
	RIL_REQUEST_(GET_CLIR);
	RIL_REQUEST_(SET_CLIR);
	RIL_REQUEST_(QUERY_CALL_FORWARD_STATUS);
	RIL_REQUEST_(SET_CALL_FORWARD);
	RIL_REQUEST_(QUERY_CALL_WAITING);
	RIL_REQUEST_(SET_CALL_WAITING);
	RIL_REQUEST_(SMS_ACKNOWLEDGE);
	RIL_REQUEST_(GET_IMEI);
	RIL_REQUEST_(GET_IMEISV);
	RIL_REQUEST_(ANSWER);
	RIL_REQUEST_(DEACTIVATE_DATA_CALL);
	RIL_REQUEST_(QUERY_FACILITY_LOCK);
	RIL_REQUEST_(SET_FACILITY_LOCK);
	RIL_REQUEST_(CHANGE_BARRING_PASSWORD);
	RIL_REQUEST_(QUERY_NETWORK_SELECTION_MODE);
	RIL_REQUEST_(SET_NETWORK_SELECTION_AUTOMATIC);
	RIL_REQUEST_(SET_NETWORK_SELECTION_MANUAL);
	RIL_REQUEST_(QUERY_AVAILABLE_NETWORKS);
	RIL_REQUEST_(DTMF_START);
	RIL_REQUEST_(DTMF_STOP);
	RIL_REQUEST_(BASEBAND_VERSION);
	RIL_REQUEST_(SEPARATE_CONNECTION);
	RIL_REQUEST_(SET_MUTE);
	RIL_REQUEST_(GET_MUTE);
	RIL_REQUEST_(QUERY_CLIP);
	RIL_REQUEST_(LAST_DATA_CALL_FAIL_CAUSE);
	RIL_REQUEST_(DATA_CALL_LIST);
	RIL_REQUEST_(RESET_RADIO);
	RIL_REQUEST_(OEM_HOOK_RAW);
	RIL_REQUEST_(OEM_HOOK_STRINGS);
	RIL_REQUEST_(SCREEN_STATE);
	RIL_REQUEST_(SET_SUPP_SVC_NOTIFICATION);
	RIL_REQUEST_(WRITE_SMS_TO_SIM);
	RIL_REQUEST_(DELETE_SMS_ON_SIM);
	RIL_REQUEST_(SET_BAND_MODE);
	RIL_REQUEST_(QUERY_AVAILABLE_BAND_MODE);
	RIL_REQUEST_(STK_GET_PROFILE);
	RIL_REQUEST_(STK_SET_PROFILE);
	RIL_REQUEST_(STK_SEND_ENVELOPE_COMMAND);
	RIL_REQUEST_(STK_SEND_TERMINAL_RESPONSE);
	RIL_REQUEST_(STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM);
	RIL_REQUEST_(EXPLICIT_CALL_TRANSFER);
	RIL_REQUEST_(SET_PREFERRED_NETWORK_TYPE);
	RIL_REQUEST_(GET_PREFERRED_NETWORK_TYPE);
	RIL_REQUEST_(GET_NEIGHBORING_CELL_IDS);
	RIL_REQUEST_(SET_LOCATION_UPDATES);
	RIL_REQUEST_(CDMA_SET_SUBSCRIPTION_SOURCE);
	RIL_REQUEST_(CDMA_SET_ROAMING_PREFERENCE);
	RIL_REQUEST_(CDMA_QUERY_ROAMING_PREFERENCE);
	RIL_REQUEST_(SET_TTY_MODE);
	RIL_REQUEST_(QUERY_TTY_MODE);
	RIL_REQUEST_(CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE);
	RIL_REQUEST_(CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE);
	RIL_REQUEST_(CDMA_FLASH);
	RIL_REQUEST_(CDMA_BURST_DTMF);
	RIL_REQUEST_(CDMA_VALIDATE_AND_WRITE_AKEY);
	RIL_REQUEST_(CDMA_SEND_SMS);
	RIL_REQUEST_(CDMA_SMS_ACKNOWLEDGE);
	RIL_REQUEST_(GSM_GET_BROADCAST_SMS_CONFIG);
	RIL_REQUEST_(GSM_SET_BROADCAST_SMS_CONFIG);
	RIL_REQUEST_(GSM_SMS_BROADCAST_ACTIVATION);
	RIL_REQUEST_(CDMA_GET_BROADCAST_SMS_CONFIG);
	RIL_REQUEST_(CDMA_SET_BROADCAST_SMS_CONFIG);
	RIL_REQUEST_(CDMA_SMS_BROADCAST_ACTIVATION);
	RIL_REQUEST_(CDMA_SUBSCRIPTION);
	RIL_REQUEST_(CDMA_WRITE_SMS_TO_RUIM);
	RIL_REQUEST_(CDMA_DELETE_SMS_ON_RUIM);
	RIL_REQUEST_(DEVICE_IDENTITY);
	RIL_REQUEST_(EXIT_EMERGENCY_CALLBACK_MODE);
	RIL_REQUEST_(GET_SMSC_ADDRESS);
	RIL_REQUEST_(SET_SMSC_ADDRESS);
	RIL_REQUEST_(REPORT_SMS_MEMORY_STATUS);
	RIL_REQUEST_(REPORT_STK_SERVICE_IS_RUNNING);
	RIL_REQUEST_(CDMA_GET_SUBSCRIPTION_SOURCE);
	RIL_REQUEST_(ISIM_AUTHENTICATION);
	RIL_REQUEST_(ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU);
	RIL_REQUEST_(STK_SEND_ENVELOPE_WITH_STATUS);
	RIL_REQUEST_(VOICE_RADIO_TECH);
	RIL_REQUEST_(GET_CELL_INFO_LIST);
	RIL_REQUEST_(SET_UNSOL_CELL_INFO_LIST_RATE);
	RIL_REQUEST_(SET_INITIAL_ATTACH_APN);
	RIL_REQUEST_(IMS_REGISTRATION_STATE);
	RIL_REQUEST_(IMS_SEND_SMS);
	RIL_REQUEST_(SIM_TRANSMIT_APDU_BASIC);
	RIL_REQUEST_(SIM_OPEN_CHANNEL);
	RIL_REQUEST_(SIM_CLOSE_CHANNEL);
	RIL_REQUEST_(SIM_TRANSMIT_APDU_CHANNEL);
	RIL_REQUEST_(NV_READ_ITEM);
	RIL_REQUEST_(NV_WRITE_ITEM);
	RIL_REQUEST_(NV_WRITE_CDMA_PRL);
	RIL_REQUEST_(NV_RESET_CONFIG);
	RIL_REQUEST_(SET_UICC_SUBSCRIPTION);
	RIL_REQUEST_(ALLOW_DATA);
	RIL_REQUEST_(GET_HARDWARE_CONFIG);
	RIL_REQUEST_(SIM_AUTHENTICATION);
	RIL_REQUEST_(GET_DC_RT_INFO);
	RIL_REQUEST_(SET_DC_RT_INFO_RATE);
	RIL_REQUEST_(SET_DATA_PROFILE);
	RIL_REQUEST_(SHUTDOWN);
	RIL_REQUEST_(GET_RADIO_CAPABILITY);
	RIL_REQUEST_(SET_RADIO_CAPABILITY);
	RIL_REQUEST_(START_LCE);
	RIL_REQUEST_(STOP_LCE);
	RIL_REQUEST_(GET_ACTIVITY_INFO);
	RIL_REQUEST_(GET_CARRIER_RESTRICTIONS);
	RIL_REQUEST_(SEND_DEVICE_STATE);
	RIL_REQUEST_(SET_UNSOLICITED_RESPONSE_FILTER);
	RIL_REQUEST_(SET_SIM_CARD_POWER);
	RIL_REQUEST_(SET_CARRIER_INFO_IMSI_ENCRYPTION);
	RIL_REQUEST_(START_NETWORK_SCAN);
	RIL_REQUEST_(STOP_NETWORK_SCAN);
	RIL_REQUEST_(START_KEEPALIVE);
	RIL_REQUEST_(STOP_KEEPALIVE);
	case RIL_RESPONSE_ACKNOWLEDGEMENT:
		return "RESPONSE_ACK";
	default:
		snprintf(unknown, sizeof(unknown), "RIL_REQUEST_%d", request);
		return unknown;
	}
}

const char *ril_unsol_event_to_string(guint event)
{
#define RIL_UNSOL_(name) case RIL_UNSOL_##name: return #name
	static char unknown[24];
	switch (event) {
	RIL_UNSOL_(RESPONSE_RADIO_STATE_CHANGED);
	RIL_UNSOL_(RESPONSE_CALL_STATE_CHANGED);
	RIL_UNSOL_(RESPONSE_VOICE_NETWORK_STATE_CHANGED);
	RIL_UNSOL_(RESPONSE_NEW_SMS);
	RIL_UNSOL_(RESPONSE_NEW_SMS_STATUS_REPORT);
	RIL_UNSOL_(RESPONSE_NEW_SMS_ON_SIM);
	RIL_UNSOL_(ON_USSD);
	RIL_UNSOL_(ON_USSD_REQUEST);
	RIL_UNSOL_(NITZ_TIME_RECEIVED);
	RIL_UNSOL_(SIGNAL_STRENGTH);
	RIL_UNSOL_(DATA_CALL_LIST_CHANGED);
	RIL_UNSOL_(SUPP_SVC_NOTIFICATION);
	RIL_UNSOL_(STK_SESSION_END);
	RIL_UNSOL_(STK_PROACTIVE_COMMAND);
	RIL_UNSOL_(STK_EVENT_NOTIFY);
	RIL_UNSOL_(STK_CALL_SETUP);
	RIL_UNSOL_(SIM_SMS_STORAGE_FULL);
	RIL_UNSOL_(SIM_REFRESH);
	RIL_UNSOL_(CALL_RING);
	RIL_UNSOL_(RESPONSE_SIM_STATUS_CHANGED);
	RIL_UNSOL_(RESPONSE_CDMA_NEW_SMS);
	RIL_UNSOL_(RESPONSE_NEW_BROADCAST_SMS);
	RIL_UNSOL_(CDMA_RUIM_SMS_STORAGE_FULL);
	RIL_UNSOL_(RESTRICTED_STATE_CHANGED);
	RIL_UNSOL_(ENTER_EMERGENCY_CALLBACK_MODE);
	RIL_UNSOL_(CDMA_CALL_WAITING);
	RIL_UNSOL_(CDMA_OTA_PROVISION_STATUS);
	RIL_UNSOL_(CDMA_INFO_REC);
	RIL_UNSOL_(OEM_HOOK_RAW);
	RIL_UNSOL_(RINGBACK_TONE);
	RIL_UNSOL_(RESEND_INCALL_MUTE);
	RIL_UNSOL_(CDMA_SUBSCRIPTION_SOURCE_CHANGED);
	RIL_UNSOL_(CDMA_PRL_CHANGED);
	RIL_UNSOL_(EXIT_EMERGENCY_CALLBACK_MODE);
	RIL_UNSOL_(RIL_CONNECTED);
	RIL_UNSOL_(VOICE_RADIO_TECH_CHANGED);
	RIL_UNSOL_(CELL_INFO_LIST);
	RIL_UNSOL_(RESPONSE_IMS_NETWORK_STATE_CHANGED);
	RIL_UNSOL_(UICC_SUBSCRIPTION_STATUS_CHANGED);
	RIL_UNSOL_(SRVCC_STATE_NOTIFY);
	RIL_UNSOL_(HARDWARE_CONFIG_CHANGED);
	RIL_UNSOL_(DC_RT_INFO_CHANGED);
	RIL_UNSOL_(RADIO_CAPABILITY);
	RIL_UNSOL_(ON_SS);
	RIL_UNSOL_(STK_CC_ALPHA_NOTIFY);
	default:
		snprintf(unknown, sizeof(unknown), "RIL_UNSOL_%d", event);
		return unknown;
	}
}

const char *ril_radio_state_to_string(int radio_state)
{
#define RADIO_STATE_(name) case RADIO_STATE_##name: return #name
	static char unknown[16];
	switch (radio_state) {
	RADIO_STATE_(OFF);
	RADIO_STATE_(UNAVAILABLE);
	RADIO_STATE_(SIM_NOT_READY);
	RADIO_STATE_(SIM_LOCKED_OR_ABSENT);
	RADIO_STATE_(SIM_READY);
	RADIO_STATE_(RUIM_NOT_READY);
	RADIO_STATE_(RUIM_READY);
	RADIO_STATE_(RUIM_LOCKED_OR_ABSENT);
	RADIO_STATE_(NV_NOT_READY);
	RADIO_STATE_(NV_READY);
	RADIO_STATE_(ON);
	default:
		snprintf(unknown, sizeof(unknown), "%d (?)", radio_state);
		return unknown;
	}
}

const char *ril_protocol_from_ofono(enum ofono_gprs_proto proto)
{
	switch (proto) {
	case OFONO_GPRS_PROTO_IPV6:
		return RIL_PROTO_IPV6_STR;
	case OFONO_GPRS_PROTO_IPV4V6:
		return RIL_PROTO_IPV4V6_STR;
	case OFONO_GPRS_PROTO_IP:
		return RIL_PROTO_IP_STR;
	}
	return NULL;
}

int ril_protocol_to_ofono(const gchar *str)
{
	if (str) {
		if (!strcmp(str, RIL_PROTO_IPV6_STR)) {
			return OFONO_GPRS_PROTO_IPV6;
		} else if (!strcmp(str, RIL_PROTO_IPV4V6_STR)) {
			return OFONO_GPRS_PROTO_IPV4V6;
		} else if (!strcmp(str, RIL_PROTO_IP_STR)) {
			return OFONO_GPRS_PROTO_IP;
		}
	}
	return -1;
}

enum ril_auth ril_auth_method_from_ofono(enum ofono_gprs_auth_method auth)
{
	switch (auth) {
	case OFONO_GPRS_AUTH_METHOD_NONE:
		return RIL_AUTH_NONE;
	case OFONO_GPRS_AUTH_METHOD_CHAP:
		return RIL_AUTH_CHAP;
	case OFONO_GPRS_AUTH_METHOD_PAP:
		return RIL_AUTH_PAP;
	case OFONO_GPRS_AUTH_METHOD_ANY:
		/* Use default */
		break;
	}
	/* Default */
	return RIL_AUTH_BOTH;
}

enum ofono_access_technology ril_parse_tech(const char *stech, int *ril_tech)
{
	int tech = -1;
	enum ofono_access_technology access_tech =
		OFONO_ACCESS_TECHNOLOGY_NONE;

	if (gutil_parse_int(stech, 0, &tech)) {
		switch (tech) {
		case RADIO_TECH_GPRS:
		case RADIO_TECH_GSM:
			access_tech = OFONO_ACCESS_TECHNOLOGY_GSM;
			break;
		case RADIO_TECH_EDGE:
			access_tech = OFONO_ACCESS_TECHNOLOGY_GSM_EGPRS;
			break;
		case RADIO_TECH_UMTS:
			access_tech = OFONO_ACCESS_TECHNOLOGY_UTRAN;
			break;
		case RADIO_TECH_HSDPA:
			access_tech = OFONO_ACCESS_TECHNOLOGY_UTRAN_HSDPA;
			break;
		case RADIO_TECH_HSUPA:
			access_tech = OFONO_ACCESS_TECHNOLOGY_UTRAN_HSUPA;
			break;
		case RADIO_TECH_HSPA:
		case RADIO_TECH_HSPAP:
			access_tech = OFONO_ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
			break;
		case RADIO_TECH_LTE:
		case RADIO_TECH_LTE_CA:
			access_tech = OFONO_ACCESS_TECHNOLOGY_EUTRAN;
			break;
		default:
			DBG("Unknown RIL tech %s", stech);
			/* no break */
		case RADIO_TECH_IWLAN:
		case RADIO_TECH_UNKNOWN:
			break;
		}

	}
	if (ril_tech) {
		*ril_tech = tech;
	}
	return access_tech;
}

gboolean ril_parse_mcc_mnc(const char *str, struct ofono_network_operator *op)
{
	if (str) {
		int i;
		const char *ptr = str;

		/* Three digit country code */
		for (i = 0;
		     i < OFONO_MAX_MCC_LENGTH && *ptr && isdigit(*ptr);
		     i++) {
			op->mcc[i] = *ptr++;
		}
		op->mcc[i] = 0;

		if (i == OFONO_MAX_MCC_LENGTH) {
			/* Usually 2 but sometimes 3 digit network code */
			for (i = 0;
			     i < OFONO_MAX_MNC_LENGTH && *ptr && isdigit(*ptr);
			     i++) {
				op->mnc[i] = *ptr++;
			}
			op->mnc[i] = 0;

			if (i > 0) {

				/*
				 * Sometimes MCC/MNC are followed by + and
				 * what looks like the technology code. This
				 * is of course completely undocumented.
				 */
				if (*ptr == '+') {
					int tech = ril_parse_tech(ptr+1, NULL);
					if (tech >= 0) {
						op->tech = tech;
					}
				}

				return TRUE;
			}
		}
	}
	return FALSE;
}

char* ril_encode_hex(const void *in, guint size)
{
	char *out = g_new(char, size * 2 + 1);

	ofono_encode_hex(in, size, out);
	return out;
}

void *ril_decode_hex(const char *hex, int len, guint *out_size)
{
	void *out = NULL;
	guint size = 0;

	if (hex) {
		if (len < 0) {
			len = (int) strlen(hex);
		}
		if (len > 0 && !(len & 1)) {
			size = len/2;
			out = g_malloc(size);
			if (!gutil_hex2bin(hex, len, out)) {
				g_free(out);
				out = NULL;
				size = 0;
			}
		}
	}
	if (out_size) {
		*out_size = size;
	}
	return out;
}

/*
 * Local Variables:
 * mode: C
 * c-basic-offset: 8
 * indent-tabs-mode: t
 * End:
 */
