//
//  DoughnutChart.swift
//  MVMCoreUI
//
//  Created by Murugan, Vimal on 07/01/20.
//  Copyright © 2020 Verizon Wireless. All rights reserved.
//

import UIKit


open class DoughnutChart: View {
    //--------------------------------------------------
    // MARK: - Properties
    //--------------------------------------------------
    
    public var doughnutLayer = CALayer()
    public var titleLabel = Label(fontStyle: .BoldTitleLarge)
    public var subTitleLabel = Label(fontStyle: .RegularMicro)
    public var labelContainer = MVMCoreUICommonViewsUtility.commonView()
    public static let heightConstant: CGFloat = 136
    private var sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: heightConstant)!
    
    //--------------------------------------------------
    // MARK: - Constraints
    //--------------------------------------------------
    
    public var labelContainerLeftConstraint: NSLayoutConstraint?
    public var labelContainerTopConstraint: NSLayoutConstraint?
    public var labelContainerBottomConstraint: NSLayoutConstraint?
    public var labelContainerRightConstraint: NSLayoutConstraint?
    public var heightConstraint: NSLayoutConstraint?
    
    //--------------------------------------------------
    // MARK: - Computed Properties
    //--------------------------------------------------
    
    public var doughnutChartModel: DoughnutChartModel? {
        get { return model as? DoughnutChartModel }
    }
    
    public var height: CGFloat = heightConstant {
        didSet {
            if height != oldValue {
                sizeObject = MFStyler.sizeObjectGeneric(forCurrentDevice: height)!
                updateContainer()
                drawGraph()
            }
        }
    }
    
    //--------------------------------------------------
    // MARK: - Lifecycle
    //--------------------------------------------------
    
    open override func updateView(_ size: CGFloat) {
        super.updateView(size)
        titleLabel.updateView(size)
        subTitleLabel.updateView(size)
        updateContainer()
        drawGraph()
    }
    
    open override func reset() {
        super.reset()
        titleLabel.reset()
        titleLabel.numberOfLines = 1
        subTitleLabel.reset()
        clearLayers()
    }
    
    open override func setupView() {
        super.setupView()
        
        addSubview(labelContainer)
        
        labelContainer.addSubview(titleLabel)
        labelContainer.addSubview(subTitleLabel)
        titleLabel.textAlignment = .center
        subTitleLabel.textAlignment = .center
        
        // Make label font size to adjust width if label content is high
        titleLabel.numberOfLines = 1
        titleLabel.adjustsFontSizeToFitWidth = true
        
        layer.addSublayer(doughnutLayer)
        heightConstraint = heightAnchor.constraint(equalToConstant: sizeObject.getValueBasedOnApplicationWidth())
        heightConstraint?.isActive = true
        widthAnchor.constraint(equalTo: heightAnchor).isActive = true
        
        labelContainerLeftConstraint = labelContainer.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: Padding.Three)
        labelContainerLeftConstraint?.isActive = true
        labelContainerTopConstraint = labelContainer.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: Padding.Three)
        labelContainerTopConstraint?.isActive = true
        labelContainerRightConstraint = trailingAnchor.constraint(greaterThanOrEqualTo: labelContainer.trailingAnchor, constant: Padding.Three)
        labelContainerRightConstraint?.isActive = true
        labelContainerBottomConstraint = bottomAnchor.constraint(greaterThanOrEqualTo: labelContainer.bottomAnchor, constant: Padding.Three)
        labelContainerBottomConstraint?.isActive = true
        labelContainer.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
        labelContainer.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        
        NSLayoutConstraint.constraintPinSubview(titleLabel, pinTop: true, pinBottom: false, pinLeft: true, pinRight: true)
        NSLayoutConstraint.constraintPinSubview(subTitleLabel, pinTop: false, pinBottom: true, pinLeft: true, pinRight: true)
        _ = NSLayoutConstraint(pinFirstView: titleLabel, toSecondView: subTitleLabel, withConstant: 0, directionVertical: true)
        // Rotate view for initial draw
        doughnutLayer.transform = CATransform3DMakeRotation(1 * .pi, 0, 0, 1)
    }
    
    //--------------------------------------------------
    // MARK: - MoleculeViewProtocol
    //--------------------------------------------------
    
    open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
        super.set(with: model, delegateObject, additionalData)
        
        clearLayers()
        
        guard let doughnutChartModel = doughnutChartModel else { return }
        
        titleLabel.setOptional(with: doughnutChartModel.title, delegateObject, additionalData)
        subTitleLabel.setOptional(with: doughnutChartModel.subtitle, delegateObject, additionalData)
        titleLabel.textAlignment = .center
        subTitleLabel.textAlignment = .center
        updateLabelContainer()
        drawGraph()
    }
    
    //--------------------------------------------------
    // MARK: - Draw Graph
    //--------------------------------------------------
    
    private func drawGraph() {
        
        clearLayers()
        
        let widthHeight = sizeObject.getValueBasedOnApplicationWidth()
        doughnutLayer.frame = CGRect(x: 0, y: 0, width: widthHeight, height: widthHeight)
        
        if let doughnutChart = doughnutChartModel {
            var prevPercent: CGFloat = 0.0
            for model in doughnutChart.sections {
                prevPercent += drawBar(model, prevPercent)
            }
        }
    }
    
    private func drawBar(_ chartModel: DoughnutChartItemModel, _ prevPercent: CGFloat) -> CGFloat {
        
        let shapeLayer = CAShapeLayer()
        shapeLayer.frame = doughnutLayer.frame
        shapeLayer.lineWidth = lineWidth()
        shapeLayer.fillColor = nil
        shapeLayer.strokeColor = chartModel.color.uiColor.cgColor
        
        let arcCenter = shapeLayer.position
        let radius = shapeLayer.bounds.size.width / 2.0 - lineWidth() / 2.0
        
        let value: CGFloat = chartModel.percent
        let gap: CGFloat = spaceRequired() ? lineGap() : 0.0
        let startAngle =  ((prevPercent / 100.0) * 2 * .pi) - (0.5 * .pi)
        let endAngle = ((value / 100.0) * 2 * .pi) + startAngle - gap
        let circlePath = UIBezierPath(arcCenter: arcCenter,
                                      radius: radius,
                                      startAngle: startAngle,
                                      endAngle: endAngle,
                                      clockwise: true)
        
        shapeLayer.path = circlePath.cgPath
        doughnutLayer.addSublayer(shapeLayer)
        
        return value
    }
    
    private func clearLayers() {
        doughnutLayer.sublayers?.forEach { $0.removeFromSuperlayer() }
    }
    
    public func updateContainer() {
        
        heightConstraint?.constant = sizeObject.getValueBasedOnApplicationWidth()
        updateLabelContainer()
        setNeedsDisplay()
    }
    
    public func lineWidth() -> CGFloat {
        return 12
    }
    
    public func lineGap() -> CGFloat {
        // If array is having the single item then no space required
        return doughnutChartModel?.sections.count == 1 ? 0.0 : 0.05
    }
    
    public func spaceRequired() -> Bool {
        return doughnutChartModel?.spaceRequired ?? true
    }
    
    public func updateLabelContainer() {
        
        labelContainer.setNeedsDisplay()
        labelContainer.layoutIfNeeded()
        let radius = sizeObject.getValueBasedOnApplicationWidth() / 2 - lineWidth()
        let labelheight = labelContainer.frame.height / 2
        let padding = sizeObject.getValueBasedOnApplicationWidth() / 2 - sqrt(max(0, pow(radius, 2) - pow(labelheight, 2)))
        
        labelContainerLeftConstraint?.constant = round(padding)
        labelContainerRightConstraint?.constant = round(padding)
        labelContainerTopConstraint?.constant = max(radius - labelheight, labelheight)
        labelContainerBottomConstraint?.constant = max(radius - labelheight, labelheight)
    }
}
