using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
using UnityEngine;
using Ubisoft.Hotel.Localisation;
using Ubisoft.Hotel.Localisation.Editor;
namespace Ubisoft.Hotel.Package.Editor
{
///
/// Class responsible for storing an InfoPlist item.
///
/// It is used as a workaround to avoid the following warning message shown by Unity when a Serializable object contains instances of other
/// Serializable objects of the same type (recursivity):
/// "Serialization depth limit 10 exceeded at 'Ubisoft.Hotel.Package.Editor::InfoPlistProperty.m_valueAsList'. There may be an object composition cycle in one or more of your serialized classes."
///
[Serializable]
public class InfoPlistItem
{
private static string BoolToString(bool value)
{
return value ? "1" : "0";
}
private static bool StringToBool(string value)
{
return value == "1";
}
private static string IntToString(int value)
{
return value.ToString();
}
private static int StringToInt(string value)
{
return HtInt.Parse(value);
}
private static string FloatToString(float value)
{
return value.ToString();
}
private static float StringToFloat(string value)
{
return HtFloat.Parse(value);
}
private static string DoubleToString(double value)
{
return value.ToString();
}
private static double StringToDouble(string value)
{
return HtDouble.Parse(value);
}
internal enum EType
{
None,
String,
LocalisedString,
Boolean,
Number,
Array,
Dictionary
};
public const string ATTRIBUTE_KEY = "m_key";
public const string ATTRIBUTE_TYPE = "m_type";
public const string ATTRIBUTE_VALUE = "m_value";
public const string ATTRIBUTE_VALUE_AS_LOCALISED_STRING = "m_valueAsLocalisedString";
[SerializeField]
private string m_key;
[SerializeField]
private EType m_type;
[SerializeField]
private string m_value;
[SerializeField]
private List m_valueAsLocalisedString;
internal InfoPlistItem(string key, EType type, string value)
{
Key = key;
Type = type;
Value = value;
}
public InfoPlistItem(string key, string value) : this(key, EType.String, value)
{
}
public InfoPlistItem(string key, bool value) : this(key, EType.Boolean, BoolToString(value))
{
}
public InfoPlistItem(string key, int value) : this(key, EType.Number, IntToString(value))
{
}
public InfoPlistItem(string key, float value) : this(key, EType.Number, FloatToString(value))
{
}
public InfoPlistItem(string key, double value) : this(key, EType.Number, DoubleToString(value))
{
}
public InfoPlistItem(string key, List value) : this(key, EType.LocalisedString, null)
{
ValueAsLocalisedString = value;
}
public InfoPlistItem Clone()
{
InfoPlistItem returnValue = new InfoPlistItem(Key, Type, Value);
if (ValueAsLocalisedString != null)
{
returnValue.ValueAsLocalisedString = new List();
int count = ValueAsLocalisedString.Count;
for (int i = 0; i < count; i++)
{
returnValue.ValueAsLocalisedString.Add(ValueAsLocalisedString[i]);
}
}
return returnValue;
}
public string Key
{
get { return m_key; }
set { m_key = value; }
}
internal EType Type
{
get { return m_type; }
set { m_type = value; }
}
public string TypeAsString
{
get { return Type.ToString(); }
}
public string Value
{
get { return m_value; }
set { m_value = value; }
}
public bool ValueAsBool
{
get { return StringToBool(Value); }
set { Value = BoolToString(value); }
}
public int ValueAsInt
{
get { return StringToInt(Value); }
set { Value = IntToString(value); }
}
public float ValueAsFloat
{
get { return StringToFloat(Value); }
set { Value = FloatToString(value); }
}
public double ValueAsDouble
{
get { return StringToDouble(Value); }
set { Value = DoubleToString(value); }
}
public List ValueAsLocalisedString
{
get { return m_valueAsLocalisedString; }
set { m_valueAsLocalisedString = value; }
}
public bool Validate()
{
bool returnValue = true;
EType enumType = Type;
switch (enumType)
{
case EType.Boolean:
returnValue = Value == "0" || Value == "1";
if (!returnValue)
{
Value = "0";
}
break;
case EType.Number:
returnValue = float.TryParse(Value, NumberStyles.Any, CultureInfo.InvariantCulture, out _);
if (!returnValue)
{
Value = "0";
}
break;
case EType.LocalisedString:
// LocalisedString properties use ValueAsLocalisedString instead of Value. Value is set to empty to avoid misunderstandings
if (!string.IsNullOrEmpty(Value))
{
Value = "";
returnValue = false;
}
break;
}
if (enumType != EType.LocalisedString && ValueAsLocalisedString != null)
{
if (ValueAsLocalisedString != null)
{
ValueAsLocalisedString.Clear();
}
returnValue = false;
}
return returnValue;
}
public bool IsValid()
{
bool returnValue = !string.IsNullOrEmpty(Key) && IsTypeValid();
if (returnValue)
{
if (IsLocalisedStringType())
{
returnValue = IsValueAsLocalisedStringValid();
}
else if (!IsMultipleType())
{
returnValue = !string.IsNullOrEmpty(Value);
}
}
return returnValue;
}
public bool IsTypeValid()
{
return Type != EType.None;
}
public bool IsMultipleType()
{
return Type == EType.Array || Type == EType.Dictionary;
}
public bool IsLocalisedStringType()
{
return Type == EType.LocalisedString;
}
public bool IsValueAsLocalisedStringValid()
{
return ValueAsLocalisedString != null && ValueAsLocalisedString.Count > 0;
}
public XmlElement GetValueNode(XmlDocument xmlDocument)
{
XmlElement returnValue = null;
var enumType = Type;
switch (enumType)
{
case EType.String:
returnValue = xmlDocument.CreateElement("string");
returnValue.InnerText = Value;
break;
case EType.Boolean:
string valueDecorator = ValueAsBool ? "true" : "false";
returnValue = xmlDocument.CreateElement(valueDecorator);
break;
case EType.Number:
float floatValue;
bool isInt = int.TryParse(Value, NumberStyles.Any, CultureInfo.InvariantCulture, out _);
bool isFloat = float.TryParse(Value, NumberStyles.Any, CultureInfo.InvariantCulture, out floatValue);
if (isInt && !Value.Contains("."))
{
returnValue = xmlDocument.CreateElement("integer");
returnValue.InnerText = Value;
}
else if (isFloat)
{
returnValue = xmlDocument.CreateElement("real");
returnValue.InnerText = floatValue.ToString();
}
break;
case EType.LocalisedString:
returnValue = xmlDocument.CreateElement("string");
string value = "";
if (ValueAsLocalisedString != null)
{
int count = ValueAsLocalisedString.Count;
for (int i = 0; i < count && string.IsNullOrEmpty(value); i++)
{
if (ValueAsLocalisedString[i].LanguageId == LocalisationUtils.DEFAULT_LANGUAGE_ID)
{
value = ValueAsLocalisedString[i].Value;
}
}
}
returnValue.InnerText = value;
break;
}
return returnValue;
}
public string GetDebugIsValidDescription()
{
bool isKeyValid = !string.IsNullOrEmpty(Key);
string detail = null;
if (IsLocalisedStringType())
{
if (!IsValueAsLocalisedStringValid())
{
detail = $" {Debug.FormatTextInCodeContext("ValueAsLocalisedString")} mustn't be empty for a {Debug.FormatTextInCodeContext("LocalisedString")} property";
}
}
else if (!IsMultipleType() && string.IsNullOrEmpty(Value))
{
detail = " Value mustn't be empty for a non multiple type property";
}
return $"Key: {Debug.FormatTextInUserContext(Key)} IsKeyValid: {isKeyValid} | Type: {Type} IsTypeValid: {IsTypeValid()} | Detail : {detail}";
}
}
}