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

import SignalCoreKit

public extension UIImageView {

    func setImage(imageName: String) {
        guard let image = UIImage(named: imageName) else {
            owsFailDebug("Couldn't load image: \(imageName)")
            return
        }
        self.image = image
    }

    func setTemplateImage(_ templateImage: UIImage?, tintColor: UIColor) {
        guard let templateImage else {
            owsFailDebug("Missing image")
            return
        }
        self.image = templateImage.withRenderingMode(.alwaysTemplate)
        self.tintColor = tintColor
    }

    func setTemplateImageName(_ imageName: String, tintColor: UIColor) {
        guard let image = UIImage(named: imageName) else {
            owsFailDebug("Couldn't load image: \(imageName)")
            return
        }
        setTemplateImage(image, tintColor: tintColor)
    }

    class func withTemplateImage(_ templateImage: UIImage?, tintColor: UIColor) -> UIImageView {
        let imageView = UIImageView()
        imageView.setTemplateImage(templateImage, tintColor: tintColor)
        return imageView
    }

    class func withTemplateImageName(_ imageName: String, tintColor: UIColor) -> UIImageView {
        let imageView = UIImageView()
        imageView.setTemplateImageName(imageName, tintColor: tintColor)
        return imageView
    }

    /// Creates an image view with the given theme icon, tinted with the given
    /// color, and constrained to the given size if present.
    /// - Parameters:
    ///   - icon: The ``ThemeIcon`` to display.
    ///   - tintColor: The color to tint the icon
    ///   - size: The size to constrain the image to.
    ///   When `nil`, no constraints are added.
    /// - Returns: A `UIImageView` of the icon.
    class func withTemplateIcon(
        _ icon: ThemeIcon,
        tintColor: UIColor,
        constrainedTo size: CGSize? = nil
    ) -> UIImageView {
        let imageView = UIImageView()
        imageView.setTemplateImage(Theme.iconImage(icon), tintColor: tintColor)
        if let size {
            imageView.autoSetDimensions(to: size)
        }
        return imageView
    }
}

// MARK: -

public extension UIImage {

    func asTintedImage(color: UIColor) -> UIImage? {
        let template = self.withRenderingMode(.alwaysTemplate)
        let imageView = UIImageView(image: template)
        imageView.tintColor = color

        return imageView.renderAsImage(opaque: imageView.isOpaque, scale: UIScreen.main.scale)
    }

    /// Redraw the image into a new image, with an added background color, and inset the
    /// original image by the provided insets.
    func withBackgroundColor(_ color: UIColor, insets: UIEdgeInsets = .zero) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(size, true, scale)
        defer {
            UIGraphicsEndImageContext()
        }

        guard let ctx = UIGraphicsGetCurrentContext(), let image = cgImage else {
            owsFailDebug("Failed to create image context when setting image background")
            return nil
        }

        let rect = CGRect(origin: .zero, size: size)
        ctx.setFillColor(color.cgColor)
        ctx.fill(rect)
        // draw the background behind
        ctx.concatenate(CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: size.height))
        ctx.draw(image, in: rect.inset(by: insets))

        guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else {
            owsFailDebug("Failed to create background-colored image from context")
            return nil
        }
        return newImage
    }
}

// MARK: -

public extension UIView {

    func renderAsImage() -> UIImage {
        renderAsImage(opaque: false, scale: UIScreen.main.scale)
    }

    func renderAsImage(opaque: Bool, scale: CGFloat) -> UIImage {
        let format = UIGraphicsImageRendererFormat()
        format.scale = scale
        format.opaque = opaque
        let renderer = UIGraphicsImageRenderer(bounds: self.bounds,
                                               format: format)
        return renderer.image { (context) in
            self.layer.render(in: context.cgContext)
        }
    }
}
