import Postbox

public enum ProxyServerConnection: Equatable, Hashable, PostboxCoding {
    case socks5(username: String?, password: String?)
    case mtp(secret: Data)
    
    public init(decoder: PostboxDecoder) {
        switch decoder.decodeInt32ForKey("_t", orElse: 0) {
            case 0:
                self = .socks5(username: decoder.decodeOptionalStringForKey("username"), password: decoder.decodeOptionalStringForKey("password"))
            case 1:
                self = .mtp(secret: decoder.decodeBytesForKey("secret")?.makeData() ?? Data())
            default:
                self = .socks5(username: nil, password: nil)
        }
    }
    
    public func encode(_ encoder: PostboxEncoder) {
        switch self {
            case let .socks5(username, password):
                encoder.encodeInt32(0, forKey: "_t")
                if let username = username {
                    encoder.encodeString(username, forKey: "username")
                } else {
                    encoder.encodeNil(forKey: "username")
                }
                if let password = password {
                    encoder.encodeString(password, forKey: "password")
                } else {
                    encoder.encodeNil(forKey: "password")
                }
            case let .mtp(secret):
                encoder.encodeInt32(1, forKey: "_t")
                encoder.encodeBytes(MemoryBuffer(data: secret), forKey: "secret")
        }
    }
}

public struct ProxyServerSettings: PostboxCoding, Equatable, Hashable {
    public let host: String
    public let port: Int32
    public let connection: ProxyServerConnection
    
    public init(host: String, port: Int32, connection: ProxyServerConnection) {
        self.host = host
        self.port = port
        self.connection = connection
    }
    
    public init(decoder: PostboxDecoder) {
        self.host = decoder.decodeStringForKey("host", orElse: "")
        self.port = decoder.decodeInt32ForKey("port", orElse: 0)
        if let username = decoder.decodeOptionalStringForKey("username") {
            self.connection = .socks5(username: username, password: decoder.decodeOptionalStringForKey("password"))
        } else {
            self.connection = decoder.decodeObjectForKey("connection", decoder: ProxyServerConnection.init(decoder:)) as? ProxyServerConnection ?? ProxyServerConnection.socks5(username: nil, password: nil)
        }
    }
    
    public func encode(_ encoder: PostboxEncoder) {
        encoder.encodeString(self.host, forKey: "host")
        encoder.encodeInt32(self.port, forKey: "port")
        encoder.encodeObject(self.connection, forKey: "connection")
    }
    
    public var hashValue: Int {
        var hash = self.host.hashValue
        hash = hash &* 31 &+ self.port.hashValue
        hash = hash &* 31 &+ self.connection.hashValue
        return hash
    }
}

public struct ProxySettings: PreferencesEntry, Equatable {
    public var enabled: Bool
    public var servers: [ProxyServerSettings]
    public var activeServer: ProxyServerSettings?
    public var useForCalls: Bool
    
    public static var defaultSettings: ProxySettings {
        return ProxySettings(enabled: false, servers: [], activeServer: nil, useForCalls: false)
    }
    
    public init(enabled: Bool, servers: [ProxyServerSettings], activeServer: ProxyServerSettings?, useForCalls: Bool) {
        self.enabled = enabled
        self.servers = servers
        self.activeServer = activeServer
        self.useForCalls = useForCalls
    }
    
    public init(decoder: PostboxDecoder) {
        if let _ = decoder.decodeOptionalStringForKey("host") {
            let legacyServer = ProxyServerSettings(decoder: decoder)
            if !legacyServer.host.isEmpty && legacyServer.port != 0 {
                self.enabled = true
                self.servers = [legacyServer]
            } else {
                self.enabled = false
                self.servers = []
            }
        } else {
            self.enabled = decoder.decodeInt32ForKey("enabled", orElse: 0) != 0
            self.servers = decoder.decodeObjectArrayWithDecoderForKey("servers")
        }
        self.activeServer = decoder.decodeObjectForKey("activeServer", decoder: ProxyServerSettings.init(decoder:)) as? ProxyServerSettings
        self.useForCalls = decoder.decodeInt32ForKey("useForCalls", orElse: 0) != 0
    }
    
    public func encode(_ encoder: PostboxEncoder) {
        encoder.encodeInt32(self.enabled ? 1 : 0, forKey: "enabled")
        encoder.encodeObjectArray(self.servers, forKey: "servers")
        if let activeServer = self.activeServer {
            encoder.encodeObject(activeServer, forKey: "activeServer")
        } else {
            encoder.encodeNil(forKey: "activeServer")
        }
        encoder.encodeInt32(self.useForCalls ? 1 : 0, forKey: "useForCalls")
    }
    
    public func isEqual(to: PreferencesEntry) -> Bool {
        guard let to = to as? ProxySettings else {
            return false
        }
        
        return self == to
    }
    
    public var effectiveActiveServer: ProxyServerSettings? {
        if self.enabled, let activeServer = self.activeServer {
            return activeServer
        } else {
            return nil
        }
    }
}