// Copyright (c) ZeroC, Inc.

import IceImpl

/// Implementations of SliceTraits describe a Slice interface.
public protocol SliceTraits {
    /// List of all an interface's type-ids.
    static var staticIds: [String] { get }

    /// The interface's most derived type-id.
    static var staticId: String { get }
}

/// The base protocol for servants.
public protocol Object {
    /// Returns the type ID of the most-derived Slice interface supported by this object.
    ///
    /// - Parameter current: The Current object for the dispatch.
    /// - Returns: The Slice type ID of the most-derived interface.
    func ice_id(current: Current) async throws -> String

    /// Returns the Slice interfaces supported by this object as a list of type IDs.
    ///
    /// - Parameter current: The Current object for the dispatch.
    /// - Returns: The Slice type IDs of the interfaces supported by this object, in alphabetical order.
    func ice_ids(current: Current) async throws -> [String]

    /// Tests whether this object supports a specific Slice interface.
    ///
    /// - Parameters:
    ///   - id: The type ID of the Slice interface to test against.
    ///   - current: The Current object for the dispatch.
    /// - Returns: `true` if this object implements the Slice interface specified by `id` or implements a derived
    ///   interface, `false` otherwise.
    func ice_isA(id: String, current: Current) async throws -> Bool

    /// Tests whether this object can be reached.
    ///
    /// - Parameter current: The Current object for the dispatch.
    func ice_ping(current: Current) async throws
}

extension Object {
    public func _iceD_ice_id(_ request: IncomingRequest) async throws -> OutgoingResponse {
        _ = try request.inputStream.skipEmptyEncapsulation()
        let returnValue = try await ice_id(current: request.current)
        return request.current.makeOutgoingResponse(returnValue, formatType: .compactFormat) {
            ostr, value in
            ostr.write(value)
        }
    }

    public func _iceD_ice_ids(_ request: IncomingRequest) async throws -> OutgoingResponse {
        _ = try request.inputStream.skipEmptyEncapsulation()
        let returnValue = try await ice_ids(current: request.current)
        return request.current.makeOutgoingResponse(returnValue, formatType: .compactFormat) {
            ostr, value in
            ostr.write(value)
        }
    }

    public func _iceD_ice_isA(_ request: IncomingRequest) async throws -> OutgoingResponse {
        let istr = request.inputStream
        _ = try istr.startEncapsulation()
        let identity: String = try istr.read()
        let returnValue = try await ice_isA(id: identity, current: request.current)
        return request.current.makeOutgoingResponse(returnValue, formatType: .compactFormat) {
            ostr, value in
            ostr.write(value)
        }
    }

    public func _iceD_ice_ping(_ request: IncomingRequest) async throws -> OutgoingResponse {
        _ = try request.inputStream.skipEmptyEncapsulation()
        try await ice_ping(current: request.current)
        return request.current.makeEmptyOutgoingResponse()
    }
}

/// Traits for Object.
public struct ObjectTraits: SliceTraits {
    public static let staticIds = ["::Ice::Object"]
    public static let staticId = "::Ice::Object"
}

/// `DefaultObject` provides a default implementation of the 4 ``Object`` operations
/// (`ice_id`, `ice_ids`, `ice_isA`, `ice_ping`) for a given Slice interface.
public struct DefaultObject<T: SliceTraits>: Object {
    public init() {}

    public func ice_id(current _: Current) -> String {
        return T.staticId
    }

    public func ice_ids(current _: Current) -> [String] {
        return T.staticIds
    }

    public func ice_isA(id: String, current _: Current) -> Bool {
        return T.staticIds.contains(id)
    }

    public func ice_ping(current _: Current) {
        // Do nothing
    }
}
