// // ModelColorConfiguration.swift // VDS // // Created by Matt Bruce on 8/4/22. // import Foundation import UIKit public typealias ObjectColorable = Colorable & Initable & ObjectWithable /// Meant to be used in a Object that implements the following interfaces for 2 possible color combinations /// - Surfaceable (var surface: Surface) /// /// let model = TestModel() /// model.surface = .light /// /// let config = SurfaceColorConfiguration() /// config.lightColor = .black /// config.darkColor = .white /// /// let textColor = config.getColor(model) //returns .black open class SurfaceColorConfiguration: ObjectColorable { public typealias ObjectType = Surfaceable public var lightColor: UIColor = .clear public var darkColor: UIColor = .clear required public init(){} public init(_ lightColor: UIColor, _ darkColor: UIColor) { self.lightColor = lightColor self.darkColor = darkColor } public func getColor(_ surface: Surface) -> UIColor { return surface == .light ? lightColor : darkColor } public func getColor(_ object: any ObjectType) -> UIColor { return getColor(object.surface) } } /// Struct to Hold onto KeyType and SurfaceConfiguration public struct KeyColorConfiguration { var key: KeyType var surfaceConfig: SurfaceColorConfiguration } // Protocol to Hold onto the relationship of KeyType and SurfaceConfiguration public protocol KeyColorConfigurable: ObjectColorable { associatedtype KeyType: Equatable var keyColors: [KeyColorConfiguration] { get set } } extension KeyColorConfigurable { public func setSurfaceColors(_ lightColor: UIColor, _ darkColor: UIColor, forKey key: KeyType) { keyColors.append(.init(key: key, surfaceConfig: .init(lightColor, darkColor))) } public func reset() { keyColors.removeAll() } } public class ControlColorConfiguration: KeyColorConfigurable { public typealias KeyType = UIControl.State public typealias ObjectType = Surfaceable & UIControl public var keyColors: [KeyColorConfiguration] = [] public required init() { } public func setSurfaceColors(_ lightColor: UIColor, _ darkColor: UIColor, forState state: KeyType) { setSurfaceColors(lightColor, darkColor, forKey: state) } public func getColor(_ object: any ObjectType) -> UIColor { let state = object.state let surface = object.surface // find the exact match if let keyColor = keyColors.first(where: {$0.key == state }) { return keyColor.surfaceConfig.getColor(surface) } else if state.contains(.disabled), let keyColor = keyColors.first(where: {$0.key == .disabled }) { return keyColor.surfaceConfig.getColor(surface) } else if state.contains(.highlighted), let keyColor = keyColors.first(where: {$0.key == .highlighted }) { return keyColor.surfaceConfig.getColor(surface) } else { return .clear } } } public class ViewColorConfiguration: KeyColorConfigurable { public typealias KeyType = Bool public typealias ObjectType = Surfaceable & Disabling public var keyColors: [KeyColorConfiguration] = [] public required init() { } public func setSurfaceColors(_ lightColor: UIColor, _ darkColor: UIColor, forDisabled disabled: KeyType) { setSurfaceColors(lightColor, darkColor, forKey: disabled) } public func getColor(_ object: ObjectType) -> UIColor { if let keyColor = keyColors.first(where: {$0.key == object.disabled }) { return keyColor.surfaceConfig.getColor(object) } else { return .clear //default } } } /// Generic Class that allows reflection for a object's specific property (equatable) to be defined as the lookup for a SurfaceConfiguration public class KeyedColorConfiguration : KeyColorConfigurable { //Type of Class that at least implements Surfaceable public typealias ObjectType = ObjectType /// property that will be used for reflection, the type of this property must implement Equatable public var keyName: String /// Array of structs where the KeyValue is registered against a SurfaceColorConfiguration public var keyColors: [KeyColorConfiguration] = [] public required init() { self.keyName = "" } //this must be used public convenience init(keyName: String) { self.init() self.keyName = keyName } public func getKeyValue(_ object: ObjectType) -> KeyType? { guard !keyName.isEmpty else { fatalError("keyName must not be empty, make sure you initialize this class using init(keyName: String) method") } let mirror = Mirror(reflecting: object) guard let result = mirror.children.first(where: {$0.label == keyName })?.value as? KeyType, !mirror.children.isEmpty else { return nil } return result } public func getColor(_ object: ObjectType) -> UIColor { if let key = getKeyValue(object), let keyColor = keyColors.first(where: {$0.key == key }) { return keyColor.surfaceConfig.getColor(object) } else { return .clear //default } } }