//
//  Component.swift
//  
//  Copyright © 2016-2019 Apple Inc. All rights reserved.
//

import Foundation
import SPCCore

/// A component is a basic building block that implements a particular function or user interface element.
///
/// `Component` is the base class for all other components — both UI and non-UI.
///
/// - localizationKey: Component
open class Component: Equatable {
    public var identity = ComponentIdentity()
    public class var componentType: String { return String(describing: Component.self) }
    public var liveComponent: LiveComponent? { return nil }
    
    public var name: String {
        get { return identity.name }
        set { identity.name = newValue }
    }

    public var identifier: String {
        get { return identity.identifier }
    }
    
    public var description: String {
        return "\(name) \(identifier)"
    }
    
    public static func ==(lhs: Component, rhs: Component) -> Bool {
        return (lhs.identity == rhs.identity)
    }
    
    // Returns the root name of the class e.g. Button
    private class var rootName: String {
        let typeName = String(describing: self).split(separator: ".").last ?? ""
        return String(typeName)
    }
    
    // Returns an autogenerated name for the component: <rootName><index> e.g. button1
    private lazy var autoName: String = {
        let rootName = type(of: self).rootName
        var autoNameIndex = Component.autoNameIndexes[rootName] ?? 0
        autoNameIndex += 1
        Component.autoNameIndexes[rootName] = autoNameIndex
        return "\(rootName.lowercasedFirstLetter())\(autoNameIndex)"
    }()
    private static var autoNameIndexes = [String : Int]()

    public init() {
        // Use autogenerated name.
        let newIdentity = ComponentIdentity(name: autoName)
        self.identity = newIdentity
        initialize()
    }

    public init(name: String = "") {
        // Use autogenerated name if name is not supplied.
        let newName = name.isEmpty ? autoName : name
        let newIdentity = ComponentIdentity(name: newName)
        self.identity = newIdentity
        initialize()
    }
    
    public init(identity: ComponentIdentity) {
        self.identity = identity
        initialize()
    }
    
    func initialize() {
        // Register the component.
        ComponentManager.shared.register(self)
        if Process.isUser {
            // Send a message to create the proxy component and live component in the Live process.
            let message = ComponentMessage.initComponent(origin: ComponentMessageOrigin.current, type: type(of: self).componentType, identity: self.identity)
            message.send(to: .live)
        } else if Process.isLive {
            // Create the live component directly.
            createLiveComponent()
        }
    }
    
    deinit {
        if Process.isUser {
            // Send a message to delete the component in the Live process.
            let message = ComponentMessage.deinitComponent(origin: ComponentMessageOrigin.current, identity: self.identity)
            message.send(to: .live)
        }
        // Deregister the component.
        ComponentManager.shared.deregisterComponentWith(identity: self.identity)
    }
    
    func createLiveComponent() { }
    
    public func receive(_ event: ComponentEvent, from origin: ComponentMessageOrigin) {
        //PBLog("event: \(event.name) -> component: \(identity.name) (\(type(of: self)))")
    }
}

// MARK: Mirror extension

extension Component {
    public func nameOfObservable(_ observable: AnyObject) -> String? {
        let mirror = Mirror(reflecting: self)
        for (label, value) in mirror.children {
            if observable === (value as AnyObject) {
                print("hasObservable \(self.name) has \(label ?? "")")
                return label
            }
        }
        return nil
    }
    
    public func nameOfObserver(_ observer: AnyObject) -> String? {
        let mirror = Mirror(reflecting: self)
        for (label, value) in mirror.children {
            if observer === (value as AnyObject) {
                print("hasObserver \(self.name) has \(label ?? "")")
                return label
            }
        }
        return nil
    }
    
    public func tokenName() -> String? {
        let mirror = Mirror(reflecting: self)
        return mirror.superclassMirror?.children.first?.1 as? String
    }
}
