//
//  SwiftUI_Prefix.swift
//  PersonalHealthDiary
//
//  Created by Sean Kim on 2/20/24.
//
import SwiftUI

// MARK: - TYPEALIAS
typealias VOID_TO_VOID = () -> ()

// MARK: - VARIABLE
public var APPSTORE_URL = "https://itunes.apple.com/app/"
public var KEYBOARD_UP_HEIGHT: CGFloat = 46.0

enum Compare: Int{
    case bigger = 0
    case smaller
    case equal
    case error
}

// MARK: - FUNCTION
/// print 를 조금 더 자세하게 표기해주는 함수
public func printLog<T>(_ object: T, _ file: String = #file, _ function: String = #function, _ line: Int = #line){
#if DEBUG
    let dateString = Date().convertString("yyyy/MM/dd HH:mm:ss:SSS")
    Swift.print(
//        """
//        ＿＿＿＿＿＿＿＿＿＿ ＿＿＿＿＿＿＿＿＿＿
//        |*   TIME = [\(dateString)] || FILE = [\(file.lastPathComponent)]
//        |    NAME = [\(function)] || LINE = [\(line)]
//        |>>> PRINT = \(object)
//        ￣￣￣￣￣￣￣￣￣￣ ￣￣￣￣￣￣￣￣￣￣
//        """
        """
         ＿＿＿＿＿＿＿＿＿＿ ＿＿＿＿＿＿＿＿＿＿ ＿＿＿＿＿＿＿＿＿＿ ＿＿＿＿＿＿＿＿＿＿
        *   LOCATION   : [\(file.lastPathComponent) : \(line)] - \(function)
        |     TIME     : [\(dateString)]
        >     NOTE     : \(object)
         ￣￣￣￣￣￣￣￣￣￣ ￣￣￣￣￣￣￣￣￣￣ ￣￣￣￣￣￣￣￣￣￣ ￣￣￣￣￣￣￣￣￣￣
        """
    )
#else
    
#endif
}

public func getDeviceWidth() -> CGFloat { UIScreen.main.bounds.size.width }
public func getDeviceHeight() -> CGFloat { UIScreen.main.bounds.size.height }


/// 탈옥 여부 파악 함수
/// false 떠야 정상적인것
public func isIllegalDevice() -> Bool {
    func canOpen(path: String) -> Bool {
        let file = fopen(path, "r")
        guard file != nil else { return false }
        fclose(file)
        return true
    }
    
    guard let cydiaUrlScheme = NSURL(string: "cydia://package/com.example.package") else { return false }
    if UIApplication.shared.canOpenURL(cydiaUrlScheme as URL) {
        return true
    }
    
#if arch(i386) || arch(x86_64)
    return false
#endif
    
    let fileManager = FileManager.default
    if fileManager.fileExists(atPath: "/Applications/Cydia.app") ||
        fileManager.fileExists(atPath: "/Library/MobileSubstrate/MobileSubstrate.dylib") ||
        fileManager.fileExists(atPath: "/bin/bash") ||
        fileManager.fileExists(atPath: "/usr/sbin/sshd") ||
        fileManager.fileExists(atPath: "/etc/apt") ||
        fileManager.fileExists(atPath: "/usr/bin/ssh") ||
        fileManager.fileExists(atPath: "/private/var/lib/apt") {
        return true
    }
    if canOpen(path: "/Applications/Cydia.app") ||
        canOpen(path: "/Library/MobileSubstrate/MobileSubstrate.dylib") ||
        canOpen(path: "/bin/bash") ||
        canOpen(path: "/usr/sbin/sshd") ||
        canOpen(path: "/etc/apt") ||
        canOpen(path: "/usr/bin/ssh") {
        return true
    }
    let path = "/private/" + NSUUID().uuidString
    do {
        try "anyString".write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
        try fileManager.removeItem(atPath: path)
        return true
    } catch let error { // 이 부분
        printLog("Jail ERROR: \(error))")
        return false
    }
}

public func fontNameCheck() {
    for family: String in UIFont.familyNames {
        print(family)
        for names : String in UIFont.fontNames(forFamilyName: family){
            printLog("\(names)")
        }
    }
}

///  a 가 b 보다 클 경우 true 반환 ( 그외 전부  false)
public func isBigger (_ a: Int, _ b: Int) -> Bool {
    if a > b { return true } else { return false }
}

/// JSON 형태의 String 을 받아서 Dictionary 의 형태로 내보냄
public func jsonToDict(_ input: String) -> [String: Any] {
    if let jsonData = input.data(using: .utf8){
        do {
            if let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
                return jsonObject
            }
        } catch let error { // 이 부분
            printLog("JSON ERROR: \(error))")
        }
    }
    return [:]
}

func currentVersion() -> String {
    guard let currentVer = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { return "" }
    return currentVer
    
}

func versionChange(ver: String) -> [Int] {
    return ver.components(separatedBy: ["."]).map {Int($0) ?? 0}
}


/// a 가 b 보다 OOO 하다.
func compareVersion(_ a: String, _ b: String) -> Compare {
    let aList = versionChange(ver: a)
    let bList = versionChange(ver: b)
    if aList.count != bList.count { return .error }
    else {
        for i in 0 ..< aList.count {
            if aList[i] > bList[i] { return .bigger }
            else if aList[i] < bList[i] { return .smaller }
        }
        return .equal
    }
}

func copyToClipboard(_ text: String){
    UIPasteboard.general.string = text
}



// MARK: - CUSTOM COMPONENTS


// MARK: - EXTENSION
extension String {
    /// 마지막 경로 구성 요소
    var lastPathComponent: String {
        get {
            return (self as NSString).lastPathComponent
        }
    }
    
    /// 원하는 길이 만큼 절단
    func cut(start: Int, end: Int) -> String {
        let startIndex = self.index(self.startIndex,offsetBy: start >= 0 ? start : 0)
        let endIndex = self.index(self.startIndex,offsetBy: end >= 0 ? end : 0)
        let result: String = "\(self[startIndex ..< endIndex])"
        return result
    }
    
    /// 글자 하나 단위로 구분
    func letter() -> [String] {
        return self.map { String($0) }
    }
    
    /// 단어 하나 단위로 구문 (띄어쓰기)
    func word() -> [String] {
        return self.components(separatedBy: " ")
    }
    
    /// Date() 로 변환 -> (Bool, Date) 출력
    func convertDate(_ dateFormat: String = "yyyyMMdd") -> (Bool, Date) {
        let dateFormatter = Date().setDateFormatter(dateFormat)
        if let convert = dateFormatter.date(from: self) {
            return (true, convert)
        } else {
            return (false, Date())
        }
    }
    
    /// 정규식 체크
    ///
    /// 이메일 정규식 예시: "[A-Z0-9a-z._%+-]+@[A-Z0-9a-z._%+-]+\\.[A-Za-z]{2,64}"
    /// 비밀번호 정규식 에시: "[A-Z0-9a-z._%+-]{6,12}"
    func checkFilter(_ filter: String) -> Bool {
        if self == "" { return false }
        return self.range(of: filter, options: .regularExpression) != nil
        
    }
    
    /// Bool 값에 맞게  DateFormat을 반환
    static func makeDateFormat(year: Bool, month: Bool, day: Bool, dayOfWeek: Bool = false, am_pm: Bool = false, hour: Bool = false,_ fullTime: Bool = true, minute: Bool = false, second: Bool = false, mSecond: Bool = false, mSDigit: Int = 1) -> String {
        
        var dateFormat = ""
        
        if year {
            dateFormat = dateFormat + "yyyy"
        }
        if month {
            dateFormat = dateFormat + "MM"
        }
        if day {
            dateFormat = dateFormat + "dd"
        }
        if dayOfWeek {
            dateFormat = dateFormat + "EEEEEE"
        }
        
        if am_pm {
            dateFormat = dateFormat + "a"
        }
        
        if fullTime { // 24시간
            if hour {
                dateFormat = dateFormat + "HH"
            }
        } else { // 12시간
            if hour {
                dateFormat = dateFormat + "hh"
            }
        }
        
        if minute {
            dateFormat = dateFormat + "mm"
        }
        
        if second {
            dateFormat = dateFormat + "ss"
        }
        
        if mSecond {
            for _ in 0 ..< mSDigit {
                dateFormat = dateFormat + "S"
            }
        }
        
        return dateFormat
    }
    
    /// 날짜를 합쳐서 하나의 String으로 반환
    static func combineDate(year: Int, month: Int, day: Int) -> String {
        return "\(year * 10000 + month * 100 + day)"
    }
    
    func stringToInt() -> Int {
        if let intValue = Int(self) {
            return intValue
        } else {
            return 0
        }
    }
}

extension Date {
    /// 연도 표시
    ///
    /// 변환에 실패시 현재 날짜의 년 표시
    var year: Int {
        let dateFormatter = self.setDateFormatter("yyyy")
        if let year = Int(dateFormatter.string(from: self)){
            return year
        } else {
            return Calendar.current.component(.year, from: self)
        }
    }
    
    /// 월 표시
    ///
    /// 변환 실패시 현재 날짜의 월 표시
    var month: Int {
        let dateFormatter = self.setDateFormatter("MM")
        if let month = Int(dateFormatter.string(from: self)) {
            return month
        } else {
            return Calendar.current.component(.month, from: self)
        }
    }
    
    /// 일 표시
    ///
    /// 변환 실패시 현재 날짜의 월 표시
    var day: Int {
        let dateFormatter = self.setDateFormatter("dd")
        if let day = Int(dateFormatter.string(from: self)) {
            return day
        } else {
            return Calendar.current.component(.day, from: self)
        }
    }
    
    /// 요일 표시
    ///
    /// 일요일 부터 시작해 0~6까지 반환
    ///
    /// -1 반환시 오류 발생
    var dayOfWeek: Int {
        let dateFormatter = self.setDateFormatter("EEEEEE")
        let convert = dateFormatter.string(from: self)
        switch convert {
        case "일":
            return 0
        case "월":
            return 1
        case "화":
            return 2
        case "수":
            return 3
        case "목":
            return 4
        case "금":
            return 5
        case "토":
            return 6
        default:
            return -1
        }
    }
    
    func setDateFormatter(_ dateFormat: String) -> DateFormatter {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = dateFormat
        dateFormatter.timeZone = TimeZone(abbreviation: "KST")
        dateFormatter.locale = Locale(identifier: "ko_kr")
        return dateFormatter
    }
    
    /// String 값으로 변환
    func convertString(_ dateFormat: String = "yyyyMMdd") -> String {
        let dateFormatter = self.setDateFormatter(dateFormat)
        return dateFormatter.string(from: self)
    }
    
    
    /// 해당 년도가 윤년인지 확인
    ///
    /// 값이 정확하지 않으면 현재 날짜로 파악
    func checkLeapMonth() -> Bool {
        if self.year % 4 == 0 {
            if self.year % 100 == 0 {
                if self.year % 400 == 0 {
                    return true
                }
            } else {
                return true
            }
        }
        return false
    }
    
    /// 해당 월의 날짜 수 확인
    ///
    /// 값이 정확하지 않으면 현재 날짜로 파악
    func getLastDayOfMonth() -> Int {
        switch self.month {
        case 1,3,5,7,8,10,12 :
            return 31
        case 2:
            if self.checkLeapMonth() {
                return 29
            } else {
                return 28
            }
        case 4,6,9,11:
            return 30
        default:
            return -1
        }
    }
}


extension Font {
    enum NPS_Font : String {
        case regular
        case bold
        
        var value: String {
            switch self {
            case .regular:
                return "NPS-font-Regular"
            case .bold:
                return "NPS-font-Bold"
            }
        }
    }
    
    static func nps(font: NPS_Font = .regular, size: CGFloat = 12) -> Font {
        return .custom(font.value, size: size)
    }
    
    
}

extension View {
    func endTextEditing() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

// MARK: - ANNOTATION

/// 사용방법
/// @UserDefault (key: "keyName", defaultValue: "default") var 변수
/// print(변수)
///  - 위에처럼 하게 되면 UserDefaults에 저장된 key 값을 갖고있는걸 보여주든가 기본 값 보여줌
/// 변수 = 변경값
///  - 위에처럼 하게되면 UserDefaults에 해당 key 값에 변경값을 저장하고 들고 있음
@propertyWrapper
struct UserDefault<T> {
    private let ud: UserDefaults = .standard
    private let key: String
    private var defaultValue: T
    
    var wrappedValue: T {
        set { ud.set(newValue, forKey: key) }
        get { ud.object(forKey: key) as? T ?? defaultValue }
    }
    
    init(key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }
    
    ///사용법: _변수.removeData()
    /// - 중요사항 변수 앞에 _ 무조건 붙여야 함
    func removeData() {
        ud.removeObject(forKey: key)
    }
}
