//
//  Created by Jake Lin on 12/4/15.
//  Copyright © 2015 IBAnimatable. All rights reserved.
//

import UIKit

public protocol GradientDesignable: class {
  var gradientMode: GradientMode { get set }
  var startColor: UIColor? { get set }
  var endColor: UIColor? { get set }
  var predefinedGradient: GradientType? { get set }
  var startPoint: GradientStartPoint { get set }
}

// MARK: - UIView

extension GradientDesignable where Self: UIView {

  public func configureGradient() {
    configureGradient(in: self)
  }

}

// MARK: - NavigationBar

extension GradientDesignable where Self: UINavigationBar {

  @available(iOSApplicationExtension, unavailable)
  public func configureGradient() {
    guard let gradientLayer = makeGradientLayer() else {
      setBackgroundImage(nil, for: .default)
      return
    }
    let statusBarHeight: CGFloat
    if #available(iOS 13, *) {
      statusBarHeight = 0 /*self.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0 */ 
    } else {
      statusBarHeight = UIApplication.shared.statusBarFrame.height
    }
    let height = bounds.height + statusBarHeight
    let navBarFrame = CGRect(x: 0, y: 0, width: bounds.width, height: height)
    gradientLayer.frame = navBarFrame
    gradientLayer.cornerRadius = layer.cornerRadius

    let image = gradientLayer.makeImage()
    setBackgroundImage(image, for: .default)
  }

}

// MARK: - Layers

extension CALayer {

  fileprivate func makeImage() -> UIImage? {
    UIGraphicsBeginImageContext(frame.size)
    render(in: UIGraphicsGetCurrentContext()!)
    let outputImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return outputImage
  }

}

// MARK: - Common

extension GradientDesignable {

  func configureGradient(in view: UIView) {
    view.subviews.filter { $0 is PrivateGradientView }.forEach {
      $0.removeFromSuperview()
    }

    guard let gradientLayer = makeGradientLayer() else {
      return
    }

    gradientLayer.cornerRadius = view.layer.cornerRadius
    gradientLayer.frame = view.bounds
    let gradientView = PrivateGradientView(frame: view.bounds, gradientLayer: gradientLayer)
    view.insertSubview(gradientView, at: 0)
  }

  private func makeGradientLayer() -> CALayer? {
    guard let colors = gradientColors() else {
      return nil
    }

    switch gradientMode {
    case .linear:
      return makeLinearLayer(colors: colors)
    case .radial:
      return makeRadialLayer(colors: colors)
    case .conical:
      return makeConicalLayer(colors: colors)
    }
  }

  private func gradientColors() -> [CGColor]? {
    if let startColor = startColor, let endColor = endColor {
      return [startColor.cgColor, endColor.cgColor]
    } else if let startColor = predefinedGradient?.colors.start, let endColor = predefinedGradient?.colors.end {
      return [startColor.cgColor, endColor.cgColor]
    }
    return nil
  }

  private func makeLinearLayer(colors: [CGColor]) -> CAGradientLayer {
    let layer = CAGradientLayer()
    layer.colors = colors

    layer.startPoint = startPoint.startPoint
    layer.endPoint = startPoint.endPoint
    return layer
  }

  private func makeRadialLayer(colors: [CGColor]) -> RadialGradientLayer {
    let layer = RadialGradientLayer()
    layer.colors = colors

    layer.startPoint = startPoint.startPoint
    layer.endPoint = startPoint.endPoint
    return layer
  }

  private func makeConicalLayer(colors: [CGColor]) -> ConicalGradientLayer {
    let layer = ConicalGradientLayer()
    layer.colors = colors

    layer.startPoint = startPoint.startPoint
    layer.endPoint = startPoint.endPoint
    return layer
  }

}

// MARK: - PrivateGradientView

private class PrivateGradientView: UIView {

  init(frame: CGRect, gradientLayer: CALayer) {
    super.init(frame: frame)
    layer.insertSublayer(gradientLayer, at: 0)
    autoresizingMask = [.flexibleWidth, .flexibleHeight]
  }

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

  override class var layerClass: AnyClass {
    return CALayer.self
  }

}
