//
//  UIBezierPath+extensions.swift
//  
//  Copyright © 2016-2019 Apple Inc. All rights reserved.
//

import Foundation
import UIKit

func degrees2radians(_ degrees: CGFloat) -> CGFloat {
    return CGFloat.pi * degrees/180
}

extension UIBezierPath
{
    // Returns a copy of the path scaled to size.
    func scaledTo(size: CGSize) -> UIBezierPath {
        guard
            bounds.size.width > 0, bounds.size.height > 0,
            let cgPath = self.cgPath.mutableCopy()
            else { return self }
        
        let scaleX = size.width / bounds.size.width
        let scaleY = size.height / bounds.size.height
        
        let scaledPath = UIBezierPath(cgPath: cgPath)
        scaledPath.apply(CGAffineTransform(scaleX: scaleX, y: scaleY))
        return scaledPath
    }
    
    static func pathWith(points: [CGPoint]) -> UIBezierPath {
        let bezierPath = UIBezierPath()
        var prevPoint: CGPoint?
        var isFirst = true
        
        for point in points {
            if let prevPoint = prevPoint {
                let midPoint = CGPoint(
                    x: (point.x + prevPoint.x) / 2,
                    y: (point.y + prevPoint.y) / 2)
                if isFirst {
                    bezierPath.addLine(to: midPoint)
                }
                else {
                    bezierPath.addQuadCurve(to: midPoint, controlPoint: prevPoint)
                }
                isFirst = false
            }
            else {
                bezierPath.move(to: point)
            }
            prevPoint = point
        }
        if let prevPoint = prevPoint {
            bezierPath.addLine(to: prevPoint)
        }
        return bezierPath
    }
    
    private static func polygonPointArray(sides: Int, x: CGFloat, y: CGFloat, radius: CGFloat, adjustment: CGFloat = 0) -> [CGPoint] {
        let angle = degrees2radians(360 / CGFloat(sides))
        let cx = x // x origin
        let cy = y // y origin
        let r  = radius // radius of circle
        var i = sides
        var points = [CGPoint]()
        while points.count <= sides {
            let xpo = cx - r * cos(angle * CGFloat(i) + degrees2radians(adjustment))
            let ypo = cy - r * sin(angle * CGFloat(i) + degrees2radians(adjustment))
            points.append(CGPoint(x: xpo, y: ypo))
            i -= 1
        }
        return points
    }
    
    private static func starCGPath(x: CGFloat, y: CGFloat, radius: CGFloat, numberOfPoints: Int, sharpness: CGFloat) -> CGPath {
        let adjustment = 360 / numberOfPoints / 2
        let path = CGMutablePath()
        let points = polygonPointArray(sides: numberOfPoints, x: x, y: y, radius: radius)
        let cpg = points[0]
        let points2 = polygonPointArray(sides: numberOfPoints, x: x, y: y, radius: radius * sharpness, adjustment: CGFloat(adjustment))
        var i = 0
        path.move(to: CGPoint(x: cpg.x, y: cpg.y))
        for p in points {
            path.addLine(to: CGPoint(x: points2[i].x, y: points2[i].y))
            path.addLine(to: CGPoint(x: p.x, y: p.y))
            i += 1
        }
        path.closeSubpath()
        return path
    }
    
    private static func polygonCGPath(x: CGFloat, y: CGFloat, radius: CGFloat, sides: Int) -> CGPath {
        let path = CGMutablePath()
        let points = polygonPointArray(sides: sides, x: x, y: y, radius: radius)
        let cpg = points[0]
        var i = 0
        path.move(to: CGPoint(x: cpg.x, y: cpg.y))
        for p in points {
            path.addLine(to: CGPoint(x: p.x, y: p.y))
            i += 1
        }
        path.closeSubpath()
        return path
    }
}

// Some functions to make the Catmull-Rom splice code a little more readable.
// These multiply/divide a `CGPoint` by a scalar and add/subtract one `CGPoint`
// from another.

private func * (lhs: CGPoint, rhs: Float) -> CGPoint {
    return CGPoint(x: lhs.x * CGFloat(rhs), y: lhs.y * CGFloat(rhs))
}

private func / (lhs: CGPoint, rhs: Float) -> CGPoint {
    return CGPoint(x: lhs.x / CGFloat(rhs), y: lhs.y / CGFloat(rhs))
}

private func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
    return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
}

private func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
    return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
}

extension UIBezierPath
{
    public convenience init(starIn rect: CGRect, points: Int, sharpness: CGFloat = 1.4) {
        let midpoint = CGPoint(x: rect.midX, y: rect.midY)
        let radius = min(rect.width, rect.height) / 2 / sharpness
        let path = UIBezierPath.starCGPath(x: midpoint.x, y: midpoint.y, radius: radius, numberOfPoints: points, sharpness: sharpness)
        self.init(cgPath: path)
    }
    
    public convenience init(polygonIn rect: CGRect, sides: Int) {
        let midpoint = CGPoint(x: rect.midX, y: rect.midY)
        let radius = min(rect.width, rect.height) / 2
        let path = UIBezierPath.polygonCGPath(x: midpoint.x, y: midpoint.y, radius: radius, sides: sides)
        self.init(cgPath: path)
    }
}


