/** Typescript의 enum에 정확히 대응할 수 있는 아주 유용한 유틸 */
export class EnumUtil {
    // ========================================================================================
    /**  enum 정의로부터 key 목록을 배열로 추출
    */// ======================================================================================
    public static Keys(enumObj: Record<string, string | number>) {
        if(this.IsNumberEnum(enumObj)) {
            return Object.keys(enumObj).filter((k) => (Number.isNaN(Number(k))));
        }
        else {
            return Object.keys(enumObj);
        }
    }
    
    // ========================================================================================
    /**  enum 정의로부터 value 목록을 배열로 추출
    */// ======================================================================================
    public static Values(enumObj: Record<string, string | number>) {
        if(this.IsNumberEnum(enumObj)) {
            return this.Keys(enumObj).map((k) => enumObj[k]);
        }
        else {
            return Object.values(enumObj);
        }
    }
    
    // ========================================================================================
    /**  enum 정의로부터 제대로 된 { key:value } 꼴의 객체 추출
    */// ======================================================================================
    public static Build(enumObj: Record<string, string | number>) {
        const res = {} as any;
        const keys = this.Keys(enumObj);
        keys.forEach((k) => res[k] = enumObj[k]);
        return res;
    }
    
    // ========================================================================================
    /**  해당 값이 enum 내의 key인지 검사
    */// ======================================================================================
    public static IsKeyOf(enumObj: Record<string, string | number>, key: string) {
        if(typeof key !== "string") {
            return false;
        }
        return !!this.GetValue(enumObj, key);
    }
    
    // ========================================================================================
    /**  해당 값이 enum 내의 value인지 검사
    */// ======================================================================================
    public static IsValueOf(enumObj: Record<string, string | number>, value: string | number) {
        if(typeof value !== "string" && typeof value !== "number") {
            return false;
        }
        return !!this.GetKey(enumObj, value);
    }
    
    // ========================================================================================
    /**  enum으로부터 특정 value의 key를 추출
     *   @param ignoreValueType value가 숫자 또는 숫자 형식 문자열인 경우, 두 타입 모두 허용
    */// ======================================================================================
    public static GetKey(enumObj: Record<string, string | number>, value: string | number, ignoreValueType: boolean = false) {
        if(ignoreValueType) {
            return this.Keys(enumObj).find((k) => enumObj[k] == value);
        }
        else {
            return this.Keys(enumObj).find((k) => enumObj[k] === value);
        }
    }
    
    // ========================================================================================
    /**  enum으로부터 특정 key의 value를 추출
    */// ======================================================================================
    public static GetValue(enumObj: Record<string, string | number>, key: string) {
        if(typeof key !== "string") {
            return undefined;
        }
        return this.Values(enumObj).find((v) => enumObj[key] === v);
    }
    
    // ========================================================================================
    /**  enum으로부터 key -> value / value -> key 탐색
     *   @param ignoreValueType value가 숫자 또는 숫자 형식 문자열인 경우, 두 타입 모두 허용
    */// ======================================================================================
    public static SearchKeyOrValue(enumObj: Record<string, string | number>, keyOrValue: string | number, ignoreValueType: boolean = false) {
        const key = this.GetKey(enumObj, keyOrValue, ignoreValueType);
        if(key) {
            return key;
        }
        else {
            return this.GetValue(enumObj, keyOrValue as string);
        }
    }

    /** 숫자를 포함한 enum인지 검사 */
    private static IsNumberEnum(enumObj: Record<string, string | number>) {
        return Object.values(enumObj).filter((v) => typeof v === "number").length > 0;
    }
}