//
//  Example
//  man
//
//  Created by man 11/11/2018.
//  Copyright © 2020 man. All rights reserved.
//

import Foundation

extension Dictionary {
    ///JSON/Form format conversion
    func dictionaryToFormString() -> String? {
        var array = [String]()
        
        for (key, value) in self {
            array.append(String(describing: key) + "=" + String(describing: value))
        }
        if array.count > 0 {
            return array.joined(separator: "&")
        }
        return nil
    }
}

extension String {
    ///JSON/Form format conversion
    func formStringToDictionary() -> [String: Any]? {
        var dictionary = [String: Any]()
        let array = self.components(separatedBy: "&")
        
        for str in array {
            let arr = str.components(separatedBy: "=")
            if arr.count == 2 {
                dictionary.updateValue(arr[1], forKey: arr[0])
            } else {
                return nil
            }
        }
        if dictionary.count > 0 {
            return dictionary
        }
        return nil
    }
}

//MARK: - *********************************************************************

extension Data {
    func dataToDictionary() -> [String: Any]? {
        do {
            return try JSONSerialization.jsonObject(with: self, options: []) as? [String : Any]
        } catch {
        }
        return nil
    }
}

extension Dictionary {
    func dictionaryToData() -> Data? {
        do {
            return try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
        } catch {
        }
        return nil
    }
}

extension Data {
    func dataToString() -> String? {
        return String(bytes: self, encoding: .utf8)
    }
}

extension String {
    func stringToData() -> Data? {
        return self.data(using: .utf8)
    }
}

//MARK: - *********************************************************************

extension String {
    func stringToDictionary() -> [String: Any]? {
        return self.stringToData()?.dataToDictionary()
    }
}

extension Dictionary {
    func dictionaryToString() -> String? {
        return self.dictionaryToData()?.dataToString()
    }
}

extension String {
    func jsonStringToPrettyJsonString() -> String? {
        return self.stringToDictionary()?.dictionaryToString()
    }
}

extension String {
    func isValidURL() -> Bool {
        if let url = URL(string: self) {
            return UIApplication.shared.canOpenURL(url)
        }
        return false
    }
}

extension String {
    func isValidJsonString() -> Bool {
        if let _ = self.stringToDictionary() {
            return true
        }
        return false
    }
}

extension String {
    func isValidFormString() -> Bool {
        if let _ = self.formStringToDictionary() {
            return true
        }
        return false
    }
}

extension String {
    func jsonStringToFormString() -> String? {
        return self.stringToDictionary()?.dictionaryToFormString()
    }
}

extension String {
    func formStringToJsonString() -> String? {
        return self.formStringToDictionary()?.dictionaryToString()
    }
}

extension String {
    func formStringToData() -> Data? {
        return self.formStringToDictionary()?.dictionaryToData()
    }
}

extension Data {
    func formDataToDictionary() -> [String: Any]? {
        return self.dataToString()?.formStringToDictionary()
    }
}

extension Data {
    func dataToPrettyPrintString() -> String? {
        //1.pretty json
        if let str = self.dataToDictionary()?.dictionaryToString() {
            return str
        } else {
            //2.protobuf
            //            if let message = try? GPBMessage.parse(from: self) {
            //                if message.serializedSize() > 0 {
            //                    return message.description
            //                } else {
            //                    //3.utf-8 string
            //                    return String(data: self, encoding: .utf8)
            //                }
            //            } else {
            //3.utf-8 string
            return String(data: self, encoding: .utf8)
            //            }
        }
    }
}

//MARK: - *********************************************************************

//https://gist.github.com/arshad/de147c42d7b3063ef7bc
///Color
extension String {
    var hexColor: UIColor {
        let hex = trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
        var int = UInt32()
        Scanner(string: hex).scanHexInt32(&int)
        var a, r, g, b: UInt32
        switch hex.count {
        case 3: // RGB (12-bit)
            (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
        case 6: // RGB (24-bit)
            (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
        case 8: // ARGB (32-bit)
            (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
        default:
            return .clear
        }
        return UIColor(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
    }
}

extension UIView {
    func addCorner(roundingCorners: UIRectCorner, cornerSize: CGSize) {
        let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: roundingCorners, cornerRadii: cornerSize)
        let cornerLayer = CAShapeLayer()
        cornerLayer.frame = bounds
        cornerLayer.path = path.cgPath
        self.layer.mask = cornerLayer
    }
}

//extension NSObject {
//    func dispatch_main_async_safe(callback: @escaping ()->Void ) {
//        if Thread.isMainThread {
//            callback()
//        } else {
//            DispatchQueue.main.async( execute: {
//                callback()
//            })
//        }
//    }
//}

//https://stackoverflow.com/questions/26244293/scrolltorowatindexpath-with-uitableview-does-not-work
///tableView
extension UITableView {
    func tableViewScrollToBottom(animated: Bool) {
        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1)) {
            let numberOfSections = self.numberOfSections
            let numberOfRows = self.numberOfRows(inSection: numberOfSections-1)
            
            if numberOfRows > 0 {
                let indexPath = IndexPath(row: numberOfRows-1, section: (numberOfSections-1))
                self.scrollToRow(at: indexPath, at: .bottom, animated: animated)
            }
        }
    }
    
    func tableViewScrollToIndex(index: Int, animated: Bool) {
        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1)) {
            self.scrollToRow(at: IndexPath(row: index, section: 0), at: .middle, animated: animated)
        }
    }
    
    func tableViewScrollToHeader(animated: Bool) {
        self.scrollRectToVisible(CGRect(x: 0, y: 0, width: 1, height: 1), animated: animated)
    }
    
    func reloadData(completion: @escaping () -> ()) {
        UIView.animate(withDuration: 0, animations: { self.reloadData()})
            {_ in completion() }
    }
}

///shake
extension UIWindow {
    
    private static var _cocoadebugShakeProperty = [String:Bool]()
    
    var cocoadebugShakeProperty:Bool {
        get {
            let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
            return UIWindow._cocoadebugShakeProperty[tmpAddress] ?? false
        }
        set(newValue) {
            let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
            UIWindow._cocoadebugShakeProperty[tmpAddress] = newValue
        }
    }
    
    
    open override var canBecomeFirstResponder: Bool {
        return true
    }
    
    open override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
        super.motionBegan(motion, with: event)
        
        self.cocoadebugShakeProperty = true
        
        if CocoaDebugSettings.shared.responseShake == false {return}
        if motion == .motionShake {
            if CocoaDebugSettings.shared.visible == true { return }
            CocoaDebugSettings.shared.showBubbleAndWindow = !CocoaDebugSettings.shared.showBubbleAndWindow
        }
    }
    
    open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
        super.motionEnded(motion, with: event)
        
        if self.cocoadebugShakeProperty == true {
            self.cocoadebugShakeProperty = false
            return
        }
        
        if CocoaDebugSettings.shared.responseShake == false {return}
        if motion == .motionShake {
            if CocoaDebugSettings.shared.visible == true { return }
            CocoaDebugSettings.shared.showBubbleAndWindow = !CocoaDebugSettings.shared.showBubbleAndWindow
        }
    }
}


///CocoaDebug
extension CocoaDebug {
    
    ///init
    static func initializationMethod(serverURL: String? = nil, ignoredURLs: [String]? = nil, onlyURLs: [String]? = nil, ignoredPrefixLogs: [String]? = nil, onlyPrefixLogs: [String]? = nil, additionalViewController: UIViewController? = nil, emailToRecipients: [String]? = nil, emailCcRecipients: [String]? = nil, mainColor: String? = nil, protobufTransferMap: [String: [String]]? = nil)
    {
        if serverURL == nil {
            CocoaDebugSettings.shared.serverURL = ""
        } else {
            CocoaDebugSettings.shared.serverURL = serverURL
        }
        
        if ignoredURLs == nil {
            CocoaDebugSettings.shared.ignoredURLs = []
        } else {
            CocoaDebugSettings.shared.ignoredURLs = ignoredURLs
        }
        if onlyURLs == nil {
            CocoaDebugSettings.shared.onlyURLs = []
        } else {
            CocoaDebugSettings.shared.onlyURLs = onlyURLs
        }
        
        if ignoredPrefixLogs == nil {
            CocoaDebugSettings.shared.ignoredPrefixLogs = []
        } else {
            CocoaDebugSettings.shared.ignoredPrefixLogs = ignoredPrefixLogs
        }
        if onlyPrefixLogs == nil {
            CocoaDebugSettings.shared.onlyPrefixLogs = []
        } else {
            CocoaDebugSettings.shared.onlyPrefixLogs = onlyPrefixLogs
        }
        
        if CocoaDebugSettings.shared.firstIn == nil {//first launch
            CocoaDebugSettings.shared.firstIn = ""
            CocoaDebugSettings.shared.showBubbleAndWindow = true
        } else {//not first launch
            CocoaDebugSettings.shared.showBubbleAndWindow = CocoaDebugSettings.shared.showBubbleAndWindow
        }
        
        CocoaDebugSettings.shared.visible = false
        CocoaDebugSettings.shared.logSearchWordNormal = nil
        CocoaDebugSettings.shared.logSearchWordRN = nil
        CocoaDebugSettings.shared.logSearchWordWeb = nil
        CocoaDebugSettings.shared.networkSearchWord = nil
        CocoaDebugSettings.shared.protobufTransferMap = protobufTransferMap
        CocoaDebugSettings.shared.additionalViewController = additionalViewController
        
        var _ = _OCLogStoreManager.shared()
        CocoaDebugSettings.shared.responseShake = true
        
        //share via email
        CocoaDebugSettings.shared.emailToRecipients = emailToRecipients
        CocoaDebugSettings.shared.emailCcRecipients = emailCcRecipients
        
        //color
        CocoaDebugSettings.shared.mainColor = mainColor ?? "#42d459"
        
        //slow animations
        CocoaDebugSettings.shared.slowAnimations = false
        
        //log
        let enableLogMonitoring = UserDefaults.standard.bool(forKey: "enableLogMonitoring_CocoaDebug")
        if enableLogMonitoring == false {
            _SwiftLogHelper.shared.enable = false
//            _OCLogHelper.shared()?.enable = false
        } else {
            _SwiftLogHelper.shared.enable = true
//            _OCLogHelper.shared()?.enable = true
        }
        
        //network
        let disableNetworkMonitoring = UserDefaults.standard.bool(forKey: "disableNetworkMonitoring_CocoaDebug")
        if disableNetworkMonitoring == true {
            _NetworkHelper.shared().disable()
        } else {
            _NetworkHelper.shared().enable()
        }
    }
    
    ///deinit
    static func deinitializationMethod() {
        WindowHelper.shared.disable()
        _NetworkHelper.shared().disable()
        _SwiftLogHelper.shared.enable = false
//        _OCLogHelper.shared()?.enable = false
        CrashLogger.shared.enable = false
        CocoaDebugSettings.shared.responseShake = false
    }
}


