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

import UIKit
import PlaygroundSupport
import SPCCore

/// A button is a control component that responds to a press.
/// It has properties that let you customize its appearance.
/// You can use its `pressed` output to receive notifications when it’s been pressed.
///
/// - localizationKey: Button
public class Button: PlaceableComponent {
    public override class var componentType: String { return String(describing: Button.self) }
    
    public override var liveComponent: LiveComponent? {
        return liveButton
    }
    
    private var liveButton: LiveButton?
    
    enum EventName: ComponentEventName {
        case imageChanged
        case pressed
        case titleChanged
    }
    
    fileprivate var pressCount: Int = 0
    
    // MARK: Properties
    
    /// The image to be displayed in the button.
    ///
    /// - localizationKey: Button.image
    public var image: Image {
        get {
            guard let image = liveButton?.image(for: .normal) else { return _image }
            return image
        }
        set {
            _image = newValue
            liveButton?.setImage(newValue, for: .normal)
            updateLiveComponent(.imageChanged, value: newValue.playgroundValue)
        }
    }
    private var _image = Image()
    
    /// The title to be displayed in the center of the button.
    ///
    /// - localizationKey: Button.title
    public var title: String {
        get {
            guard let title = liveButton?.title(for: .normal) else { return _title }
            return title
        }
        set {
            _title = newValue
            liveButton?.setTitle(newValue, for: .normal)
            updateLiveComponent(.titleChanged, value: newValue.playgroundValue)
        }
    }
    private var _title = String()
    
    // SpacePlaceable
    
    /// The default size of the button if its size is not set: 50 in width by 50 in height.
    ///
    /// - localizationKey: Button.intrinsicSize
    public override var intrinsicSize: Size { return Size(width: 50, height: 50) }
    
    // MARK: Outputs
    
    /// An output that sends an event notification of type `Pulse` each time the button is pressed.
    ///
    /// You can connect it to an input of type `Input<Pulse>`, or to a function with a single parameter of type `Pulse`.
    ///
    /// - localizationKey: Button.pressed
    public let pressed = Output<Pulse>()
    
    /// An output that sends an event notification of type `Int` each time the button is pressed. The value sent in the event is the number of times the button has been pressed.
    ///
    /// You can connect it to an input of type `Input<Int>`, or to a function with a single parameter of type `Int`.
    ///
    /// - localizationKey: Button.pressCountChanged
    public let pressCountChanged = Output<Int>()
    
    // MARK: Initialization
    override func createLiveComponent() {
        liveButton = LiveButton(component: self)
    }
    
    // MARK: Messaging
    private func updateLiveComponent(_ eventName: Button.EventName, value: PlaygroundValue?) {
        guard Process.isUser, let playgroundValue = value else { return }
        let event = ComponentEvent(name: eventName.rawValue, value: playgroundValue)
        event.send(to: self, in: .live)
    }
    
    public override func receive(_ event: ComponentEvent, from origin: ComponentMessageOrigin) {
        super.receive(event, from: origin)
        guard let eventName = Button.EventName(rawValue: event.name) else { return }
        switch eventName {
        case .pressed:
            pressCount += 1
            pressed.notifyInputs(Pulse())
            pressCountChanged.notifyInputs(pressCount)
        case.imageChanged:
            guard let newImage = UIImage.from(event.value) as? UIImage else { return }
            if let liveButton = liveButton {
                liveButton.setImage(newImage, for: .normal)
                liveButton.backgroundColor = .clear
            }
        case .titleChanged:
            guard let newTitle = String.from(event.value) as? String else { return }
            if let liveButton = liveButton {
                liveButton.setTitle(newTitle, for: .normal)
            }
        }
    }
}

class LiveButton: UIButton, LiveComponent {
    weak var component: Component?
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    required init(component: Component) {
        self.component = component
        super.init(frame: CGRect.zero)
        backgroundColor = .white
        layer.cornerRadius = 10
        showsTouchWhenHighlighted = true
        titleLabel?.numberOfLines = 0
        titleLabel?.textAlignment = .center
        setTitleColor(.darkGray, for: .normal)
        setAccessibilityInfo()
        addTarget(self, action: #selector(didPress(_:)), for: .touchUpInside)
    }
    
    @objc
    func didPress(_ sender: UIButton) {
        guard let component = component as? Button else { return }
        let event = ComponentEvent(name: Button.EventName.pressed.rawValue, value: .boolean(true))
        if Process.isLiveViewConnectionOpen {
            event.send(to: component, in: Environment.user) // Update remote (user) component
        } else {
            // Not running in a playgroundbook.
            component.receive(event, from: ComponentMessageOrigin.current) // Update local (live) component
        }
    }
}
