/*
 *
 *  Visual Voicemail Daemon
 *
 *  Copyright (C) 2012, 2013 Intel Corporation
 *                2021, Chris Talbot
 *
 * This library is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation.
 *
 * This library 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 Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: Mathias Hasselmann <mathias@openismus.com>
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif


#include <phonenumbers/logger.h>
#include <phonenumbers/phonenumberutil.h>

#include "phone-utils.h"

using i18n::phonenumbers::PhoneNumber;
using i18n::phonenumbers::PhoneNumberUtil;


struct EPhoneNumber {
	PhoneNumber priv;
};

typedef enum {
	E_PHONE_NUMBER_FORMAT_E164,
	E_PHONE_NUMBER_FORMAT_INTERNATIONAL,
	E_PHONE_NUMBER_FORMAT_NATIONAL,
	E_PHONE_NUMBER_FORMAT_RFC3966
} EPhoneNumberFormat;

static PhoneNumberUtil *
e_phone_number_util_get_instance (void)
{
	static PhoneNumberUtil *instance = NULL;

	if (g_once_init_enter (&instance)) {
		/* FIXME: Ideally PhoneNumberUtil would not be a singleton,
		 * so that we could safely tweak its attributes without
		 * influencing other users of the library. */
		PhoneNumberUtil *new_instance = PhoneNumberUtil::GetInstance ();

		/* Disable all logging: libphonenumber is pretty verbose. */
		new_instance->SetLogger (new i18n::phonenumbers::NullLogger);
		g_once_init_leave (&instance, new_instance);
	}

	return instance;
}

/*
 * phone_utils_is_valid is a bit smarter on if a phone number is valid. Here
 * I just want to make sure that something like an email isn't coming through
 * like a phone number
 */
gboolean
phone_utils_simple_is_valid (const char *number)
{
  if (strspn (number, "+()- 0123456789") != strlen (number))
    return false;

  return true;
}

gboolean
phone_utils_is_valid (const char *number,
                      const char *country_code)
{
  PhoneNumberUtil *util = PhoneNumberUtil::GetInstance ();
  PhoneNumber phone_number;

  if (!number || !*number ||
      !country_code || strlen (country_code) != 2)
    return FALSE;

  if (util->Parse (number, country_code, &phone_number) == PhoneNumberUtil::NO_PARSING_ERROR)
    return util->IsValidNumber (phone_number);

  return FALSE;
}

static bool
_phone_utils_cxx_parse (const std::string &phone_number,
                           const std::string &region,
                           PhoneNumber *parsed_number)
{
	const PhoneNumberUtil::ErrorType err =
		e_phone_number_util_get_instance ()->Parse (
			phone_number, region, parsed_number);

	if (err != PhoneNumberUtil::NO_PARSING_ERROR) {
		return false;
	}

	return true;
}

static char *
_phone_utils_cxx_to_string (const EPhoneNumber *phone_number)
{
	g_return_val_if_fail (NULL != phone_number, NULL);

	std::string formatted_number;

	e_phone_number_util_get_instance ()->Format
		(phone_number->priv,
		 static_cast<PhoneNumberUtil::PhoneNumberFormat> (E_PHONE_NUMBER_FORMAT_E164),
		 &formatted_number);

	if (!formatted_number.empty ())
		return g_strdup (formatted_number.c_str ());

	return NULL;
}

static char *
phone_utils_format_e164 (const char *phone_number,
                         const char *region_code)
{
	char *returned_string;
	EPhoneNumber *intermediate;

	if (!phone_number || !*phone_number || !region_code || strlen (region_code) != 2)
		return NULL;

	std::unique_ptr<EPhoneNumber> parsed_number(new EPhoneNumber);

	if (!_phone_utils_cxx_parse (
		phone_number, region_code, &parsed_number->priv))
		return NULL;

	intermediate = parsed_number.release ();

	returned_string = _phone_utils_cxx_to_string (intermediate);
	delete intermediate;

	return returned_string;
}

/*
 * phone_utils_format_number_e164() does two things:
 *
 * - To see if a number is valid (this does this with
     return_original_number set to FALSE), and
 * - To make sure that the valid number is in the E.164 format.
 *
 * If you see that return_original_number set to FALSE, the caller is checking
 * if the number is valid. If the number is valid, the number is returned in
 * the e.164 format.
 *
 * If return_original_number is TRUE, then the caller does not care if the
 * number is a real number (there are cases like one can send an email to
 * an MMS, so the "number" could be "foo@bar.com", and I don't want this
 * function to mess that up) and just wants to make sure that if there
 * is a number, it is properly formatted.
 *
 * No matter what, the string is newly allocated.
 *
 */

char *phone_utils_format_number_e164(const char *number,
				     const char *country_code,
				     gboolean return_original_number)
{
	char *formatted_number;
	formatted_number = phone_utils_format_e164 (number, country_code);
	if (formatted_number == NULL) {
		if (return_original_number == FALSE) {
			return NULL;
		} else {
			return g_strdup(number);
		}
	}
	return formatted_number;
}
