#nullable enable
using Cysharp.Threading.Tasks;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using UVC.Extention;
namespace UVC.Data.Core
{
///
/// JSON 데이터의 구조와 변환 규칙을 정의하는 마스크 클래스입니다.
/// Dictionary를 상속하여 JSON 데이터 구조를 표현하고,
/// 데이터 매핑, 필드 이름 변환, 그리고 타입 변환을 위한 메타데이터를 제공합니다.
///
///
/// DataMask는 주로 DataMapper 클래스와 함께 사용되어 서로 다른 JSON 형식 간의
/// 데이터 변환 및 매핑 규칙을 정의합니다. 기본 JSON 구조 외에도 추가 속성들을
/// 통해 매핑 과정에서 필요한 메타데이터를 제공합니다.
/// 또한 AppData 폴더(AppData/Company Name/Product Name)에서 JSON 파일을 로드하고 저장하는 기능을 제공하여,
/// 그 기능은 FactoryObject의 정보를 InfoWindow로 보여줄때 사용됩니다.
///
///
/// 기본 사용 예시:
///
/// // 마스크 객체 생성 및 설정
/// var mask = new DataMask();
/// mask["name"] = ""; // 문자열 타입 지정
/// mask["age"] = 0; // 정수 타입 지정
/// mask["isActive"] = false; // 불리언 타입 지정
/// mask["height"] = 0.0; // 실수 타입 지정
///
/// // 메타 속성 설정
/// mask.ObjectIdKey = "name"; // DataObject의 ID로 사용할 필드 지정
/// mask.ObjectName = "employees"; // 데이터 객체의 이름 지정
///
/// // 필드 이름 변환 규칙 설정
/// mask.NamesForReplace = new Dictionary
/// {
/// { "full_name", "name" }, // JSON의 full_name을 name으로 변환
/// { "employee_age", "age" } // JSON의 employee_age를 age로 변환
/// };
///
/// // 매핑 예시 (DataMapper 클래스와 함께 사용)
/// var sourceJson = JObject.Parse(@"{
/// ""full_name"": ""김철수"",
/// ""employee_age"": 30,
/// ""isActive"": true,
/// ""height"": 175.5
/// }");
///
/// var mapper = new DataMapper(mask);
/// DataObject result = mapper.Mapping(sourceJson);
/// // result는 변환된 필드 이름과 타입을 가진 DataObject 객체
///
///
public class DataMask : OrderedDictionary
{
private static Dictionary _dataMasks = new Dictionary();
public static IReadOnlyDictionary DataMasks => _dataMasks;
///
/// 컬렉션에 데이터 마스크를 추가하고 지정된 키와 연결합니다.
///
/// 컬렉션에 동일한 키를 가진 마스크가 이미 있는 경우, 새 마스크로 대체됩니다.
///
/// 데이터 마스크에 연결할 키입니다. 이거나 비어 있을 수 없습니다.
/// 추가할 입니다. 일 수 없습니다.
/// 사용자 데이터 마스크도 함께 추가할지 여부를 지정합니다. 기본값은 false입니다.
public static void AddMask(string key, DataMask mask)
{
if (string.IsNullOrEmpty(key) || mask == null) return;
_dataMasks[key] = mask;
}
///
/// 지정된 키와 연관된 데이터 마스크를 제거합니다.
///
/// 가 null이거나 비어 있으면 메서드는 아무 작업도 수행하지 않습니다.
/// 제거할 데이터 마스크를 식별하는 키입니다. null이거나 비어 있으면 안 됩니다.
/// 사용자 데이터 마스크도 함께 제거할지 여부를 지정합니다. 기본값은 false입니다.
public static void RemoveMask(string key)
{
if (string.IsNullOrEmpty(key)) return;
_dataMasks.Remove(key);
}
///
/// 지정된 키와 연관된 를 검색합니다.
///
/// 를 찾는 데 사용되는 키입니다. null이거나 비어 있을 수 없습니다.
/// 지정된 키와 연관된 또는 키를 찾을 수 없는 경우 을 반환합니다.
///
public static DataMask? Get(string key)
{
if (string.IsNullOrEmpty(key)) return null;
_dataMasks.TryGetValue(key, out var mask);
return mask;
}
///
/// 내부 컬렉션에서 모든 데이터 마스크를 지웁니다.
///
/// 이 메서드는 내부 데이터 마스크 컬렉션에서 모든 항목을 제거하고
/// 빈 상태로 재설정합니다. 이전에 추가된 모든 마스크를 지워야 할 때 이 메서드를 사용하세요.
public static void ClearMask()
{
_dataMasks.Clear();
}
///
/// DataObject의 Id에 해당하는 key 문자열입니다.
/// 이 속성은 매핑된 DataObject에서 고유 식별자로 사용될 필드를 지정합니다.
/// null인 경우 DataObject의 기본 동작을 따릅니다.
///
///
///
/// var mask = new DataMask();
/// mask.ObjectIdKey = "userId"; // DataObject에서 "userId" 필드가 Id로 사용됨
///
///
public string? ObjectIdKey { get; set; } = null;
///
/// DataObject의 이름을 나타내는 속성입니다. DataRepository에서 사용됩니다.
/// 이 이름은 매핑된 DataObject를 분류하거나 식별하는 데 사용될 수 있습니다.
///
///
///
/// var mask = new DataMask();
/// mask.ObjectName = "users"; // 매핑된 DataObject는 "users"라는 이름을 가짐
///
///
public string ObjectName { get; set; } = string.Empty;
///
/// 원본 JSON에서 매핑된 DataObject로 필드 이름을 변환하는 규칙을 정의하는 딕셔너리입니다.
/// 키는 원본 JSON의 필드 이름이고, 값은 변환될 대상 필드 이름입니다.
///
///
/// 이 속성을 통해 서로 다른 명명 규칙을 사용하는 JSON 구조 간의 매핑을 용이하게 할 수 있습니다.
/// 예를 들어, snake_case를 사용하는 API 응답을 camelCase로 변환하는 데 사용할 수 있습니다.
///
///
///
/// var mask = new DataMask();
/// mask.NamesForReplace = new Dictionary
/// {
/// { "first_name", "firstName" },
/// { "last_name", "lastName" },
/// { "birth_date", "birthDate" }
/// };
///
///
public Dictionary? NamesForReplace { get; set; }
public DataMask() { }
public DataMask(string jsonString)
{
if (string.IsNullOrEmpty(jsonString)) return;
JObject jObj = JObject.Parse(jsonString);
foreach (var property in jObj.Properties())
{
this[property.Name] = ConvertJTokenToObject(property);
}
}
public DataMask(JObject jObj)
{
if (jObj == null) return;
foreach (var property in jObj.Properties())
{
this[property.Name] = ConvertJTokenToObject(property.Value);
}
}
///
/// JToken을 실제 객체로 변환하는 헬퍼 메서드
///
/// 변환할 JToken 객체
/// 변환된 .NET 객체
///
/// 이 메서드는 JToken의 타입에 따라 적절한 .NET 타입의 객체로 변환합니다.
/// 객체는 DataObject로, 배열은 DataArray로 변환되며, 기본 타입은 해당하는 .NET 타입으로 변환됩니다.
///
private object ConvertJTokenToObject(JToken token)
{
if (token == null) return null;
switch (token.Type)
{
case JTokenType.Property:
JProperty prop = (JProperty)token;
return ConvertJTokenToObject(prop.Value);
case JTokenType.Object:
return new DataMask((JObject)token);
case JTokenType.Array:
JArray array = (JArray)token;
List dataMasks = new List();
if (array.All(item => item.Type == JTokenType.Object))
{
foreach (var item in array)
{
dataMasks.Add(new DataMask((JObject)item));
}
}
return dataMasks;
case JTokenType.Integer:
return token.ToObject();
case JTokenType.Float:
return token.ToObject();
case JTokenType.String:
return token.ToObject();
case JTokenType.Boolean:
return token.ToObject();
case JTokenType.Date:
return token.ToObject();
case JTokenType.Null:
return null;
default:
return token.ToObject