//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import UIKit

public class BezierPathView: UIView {

    public typealias ShapeLayerConfigurationBlock = (CAShapeLayer, CGRect) -> Void

    // Configure the view with this method if it uses a single Bezier path.
    public var shapeLayerConfigurationBlock: ShapeLayerConfigurationBlock? {
        didSet { updateShapeLayer() }
    }

    public convenience init(configurationBlock: @escaping ShapeLayerConfigurationBlock) {
        self.init(frame: .zero)
        shapeLayerConfigurationBlock = configurationBlock
    }

    public override init(frame: CGRect) {
        super.init(frame: frame)

        isOpaque = false
        isUserInteractionEnabled = false
        backgroundColor = .clear
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    public override var frame: CGRect {
        didSet {
            guard oldValue.size != frame.size else { return }
            updateShapeLayer()
        }
    }

    public override var bounds: CGRect {
        didSet {
            guard oldValue.size != bounds.size else { return }
            updateShapeLayer()
        }
    }

    // This method forces the view to reconstruct its layer content.  It shouldn't
    // be necessary to call this unless the ConfigureShapeLayerBlocks depend on external
    // state which has changed.
    private var shapeLayer: CAShapeLayer?
    private func updateShapeLayer() {
        guard bounds.width > 0 && bounds.height > 0 else { return }

        if let shapeLayer {
            shapeLayer.removeFromSuperlayer()
        }

        guard let shapeLayerConfigurationBlock else { return }

        let shapeLayer = CAShapeLayer()
        shapeLayer.disableAnimationsWithDelegate()
        shapeLayerConfigurationBlock(shapeLayer, bounds)
        layer.addSublayer(shapeLayer)
        self.shapeLayer = shapeLayer

        setNeedsDisplay()
    }

    public override func action(for layer: CALayer, forKey event: String) -> CAAction? {
        return nil
    }
}
