//
//  Parameter.swift
//  Pachyderm
//
//  Created by Shadowfacts on 9/8/18.
//  Copyright © 2018 Shadowfacts. All rights reserved.
//

import Foundation

struct Parameter: Sendable {
    let name: String
    let value: String?
}
precedencegroup ParameterizationPrecedence {
    associativity: left
    higherThan: AdditionPrecedence
}
infix operator => : ParameterizationPrecedence

extension String {
    static func =>(name: String, value: String?) -> Parameter {
        return Parameter(name: name, value: value)
    }
    
    static func =>(name: String, value: Bool?) -> Parameter {
        return Parameter(name: name, value: value?.description)
    }
    
    static func =>(name: String, value: Int?) -> Parameter {
        return Parameter(name: name, value: value?.description)
    }
    
    static func =>(name: String, value: Date?) -> Parameter {
        if let value = value {
            let formatter = ISO8601DateFormatter()
            formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
            let string = formatter.string(from: value)
            return Parameter(name: name, value: string)
        } else {
            return Parameter(name: name, value: nil)
        }
    }
    
    static func =>(name: String, value: TimeInterval?) -> Parameter {
        return name => (value == nil ? nil : Int(value!))
    }
    
    static func =>(name: String, focus: (Float, Float)?) -> Parameter {
        guard let focus = focus else { return Parameter(name: name, value: nil) }
        return Parameter(name: name, value: "\(focus.0),\(focus.1)")
    }
    
    static func =>(name: String, values: [String]?) -> [Parameter] {
        guard let values = values else { return [] }
        let name = "\(name)[]"
        return values.map { Parameter(name: name, value: $0) }
    }
    
    static func =>(name: String, values: [Int]) -> [Parameter] {
        return name => values.map { $0.description }
    }
}

extension Parameter: CustomStringConvertible {
    var description: String {
        if let value = value {
            return "\(name)=\(value)"
        } else {
            return name
        }
    }
}

extension Array where Element == Parameter {
    var urlEncoded: String {
        return compactMap {
            guard let value = $0.value,
                let escapedValue = value.addingPercentEncoding(withAllowedCharacters: .alphanumerics) else {
                    return nil
            }
            return "\($0.name)=\(escapedValue)"
        }.joined(separator: "&")
    }
    
    var queryItems: [URLQueryItem] {
        return compactMap {
            guard let value = $0.value else { return nil }
            return URLQueryItem(name: $0.name, value: value)
        }
    }
}
