JohnCoates/Aerial

View on GitHub
Aerial/Source/Views/Layers/Weather/ConditionSymbolLayer.swift

Summary

Maintainability
C
1 day
Test Coverage
//
//  ConditionSymbolLayer.swift
//  Aerial
//
//  Created by Guillaume Louel on 24/04/2020.
//  Copyright © 2020 Guillaume Louel. All rights reserved.
//

import Cocoa
import Foundation

class ConditionSymbolLayer: CALayer {
    let mainSymbols = [200: "cloud.bolt.rain",
                       201: "cloud.bolt.rain",
                       202: "cloud.bolt.rain",
                       210: "cloud.sun.bolt",
                       211: "cloud.bolt",
                       212: "cloud.bolt",
                       221: "cloud.bolt",
                       230: "cloud.bolt.rain",
                       231: "cloud.bolt.rain",
                       232: "cloud.bolt.rain",

                       300: "cloud.drizzle",
                       301: "cloud.drizzle",
                       302: "cloud.drizzle",
                       310: "cloud.drizzle",
                       311: "cloud.drizzle",
                       312: "cloud.drizzle",
                       313: "cloud.drizzle",
                       314: "cloud.drizzle",
                       321: "cloud.drizzle",

                       500: "cloud.sun.rain",
                       501: "cloud.rain",
                       502: "cloud.heavyrain",
                       503: "cloud.heavyrain",
                       504: "cloud.heavyrain",

                       511: "cloud.sleet",

                       520: "cloud.rain",
                       521: "cloud.rain",
                       522: "cloud.heavyrain",
                       531: "cloud.rain",

                       600: "snow",
                       601: "snow",
                       602: "cloud.snow",

                       611: "cloud.sleet",
                       612: "cloud.sleet",
                       613: "cloud.sleet",
                       615: "cloud.sleet",
                       616: "cloud.sleet",

                       620: "snow",
                       621: "snow",
                       622: "cloud.snow",

                       701: "sun.haze",
                       711: "smoke",
                       721: "sun.haze",
                       731: "sun.dust",
                       741: "sun.haze",
                       751: "sun.dust",
                       761: "sun.dust",
                       762: "sun.dust",
                       781: "tornado",

                       800: "sun.max",
                       801: "sun.max",
                       802: "cloud.sun",
                       803: "cloud.sun",
                       804: "cloud" ]//

    let nightSymbols = [210: "cloud.moon.bolt",

                        500: "cloud.moon.rain",

                        800: "moon.stars",
                        801: "moon",
                        802: "cloud.moon",
                        803: "cloud.moon" ]

    init(weather: OWWeather, dt: Int, isNight: Bool, size: Int, square: Bool = false) {
        super.init()

        var img: NSImage?

        switch PrefsInfo.weather.icons {
        case .flat:
            img = makeSymbol(name: getSymbol(condition: weather.id,
                                                 isNight: isNight), size: size)
        case .colorflat:
            img = makeColorSymbol(name: getColorSymbol(condition: weather.id,
                                                  isNight: isNight), size: size)
        case .oweather:
            downloadImage(from: URL(string: "http://openweathermap.org/img/wn/\(weather.icon)@4x.png")!, size: size)
            img = nil
        }

        if let img = img {
            if !square {
                frame.size.height = CGFloat(size)
                frame.size.width = CGFloat(size) * img.size.width / img.size.height
            } else {
                if frame.size.height > frame.size.width {
                    frame.size.height = CGFloat(size)
                    frame.size.width = CGFloat(size) * img.size.width / img.size.height
                } else {
                    frame.size.width = CGFloat(size)
                    frame.size.height = CGFloat(size) * img.size.height / img.size.width
                }
            }

            contents = img
        }
    }

    init(weather: OWWeather, dt: Int, sunrise: Int, sunset: Int, size: Int, square: Bool = false) {
        super.init()

        // In case icons are updated, it's important to test them !
        // test()

        let isNight = isNight(dt: dt, sunrise: sunrise, sunset: sunset)
        var img: NSImage?

        switch PrefsInfo.weather.icons {
        case .flat:
            img = makeSymbol(name: getSymbol(condition: weather.id,
                                                 isNight: isNight), size: size)
        case .colorflat:
            img = makeColorSymbol(name: getColorSymbol(condition: weather.id,
                                                  isNight: isNight), size: size)
        case .oweather:
            downloadImage(from: URL(string: "http://openweathermap.org/img/wn/\(weather.icon)@4x.png")!, size: size)
            img = nil
        }

        if let img = img {
            if !square {
                frame.size.height = CGFloat(size)
                frame.size.width = CGFloat(size) * img.size.width / img.size.height
            } else {
                if frame.size.height > frame.size.width {
                    frame.size.height = CGFloat(size)
                    frame.size.width = CGFloat(size) * img.size.width / img.size.height
                } else {
                    frame.size.width = CGFloat(size)
                    frame.size.height = CGFloat(size) * img.size.height / img.size.width
                }
            }

            contents = img
        }
    }

    override init(layer: Any) {
        super.init(layer: layer)
    }

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

    func makeSymbol(name: String, size: Int) -> NSImage? {
        if #available(macOS 11.0, *) {
            if let image = NSImage(systemSymbolName: name, accessibilityDescription: name) {
                image.isTemplate = true

                // return image
                let config = NSImage.SymbolConfiguration(pointSize: CGFloat(size), weight: .regular)
                return image.withSymbolConfiguration(config)?.tinting(with: .white)
            }
        } else {
            // We fallback on the pdf icons
            let imagePath = Bundle(for: PanelWindowController.self).path(
                forResource: name, ofType: "pdf") ?? ""

            let img = NSImage(contentsOfFile: imagePath)

            return img
        }

        return nil
    }

    func makeColorSymbol(name: String, size: Int) -> NSImage? {
        if #available(macOS 11.0, *) {
            if let image = NSImage(systemSymbolName: name, accessibilityDescription: name) {
                image.isTemplate = false

                // return image
                let config = NSImage.SymbolConfiguration(pointSize: CGFloat(size), weight: .regular)
                return image.withSymbolConfiguration(config) // ?.tinting(with: .white)
            }
        }

        return nil
    }

    func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void) {
        URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
    }

    func downloadImage(from url: URL, size: Int) {
        frame.size.height = CGFloat(size)
        frame.size.width = CGFloat(size)

        getData(from: url) { data, _, error in
            guard let data = data, error == nil else { return }

            DispatchQueue.main.async() {
                let img = NSImage(data: data)
                self.contents = img

                /*
                // If we have something, trim and put it up
                if let img = imgs {
                    // Get the trimmed image first, goes on the left
                    let trimmedimg = img.trim()!

                    let imglayer = CALayer()
                    imglayer.frame.size.height = trimmedimg.size.height / 2
                    imglayer.frame.size.width = trimmedimg.size.width / 2
                    imglayer.contents = trimmedimg

                    imglayer.anchorPoint = CGPoint(x: 0, y: 0.5)
                    imglayer.position = CGPoint(x: 0, y: 50)
                    self.addSublayer(imglayer)

                    let tempWidth = self.addTemperature(at: imglayer.frame.width + 15)
                    self.addFeelsLike(at: imglayer.frame.width + 15 + (tempWidth / 2))
                    self.addWind(at: (imglayer.frame.width + 15 + tempWidth) / 2)

                    // Set the final size
                    self.frame.size = CGSize(width: imglayer.frame.width + 15 + tempWidth, height: 75)
                }
                 */
            }
        }
    }

    func test() {
        nightSymbols.forEach { (key: Int, value: String) in
            let imagePath = Bundle(for: PanelWindowController.self).path(
            forResource: getSymbol(condition: key, isNight: true),
            ofType: "pdf")
            if imagePath == nil {
                debugLog("ERROR night \(key) \(value)")
            } else {
                debugLog("OK night \(key) \(value)")
            }
        }

        mainSymbols.forEach { (key: Int, value: String) in
            let imagePath = Bundle(for: PanelWindowController.self).path(
            forResource: getSymbol(condition: key, isNight: true),
            ofType: "pdf")
            if imagePath == nil {
                debugLog("ERROR day \(key) \(value)")
            } else {
                debugLog("OK day \(key) \(value)")
            }
        }

    }

    func getSymbol(condition: Int, isNight: Bool) -> String {
        if isNight && nightSymbols[condition] != nil {
            return nightSymbols[condition]!
        } else {
            if mainSymbols[condition] != nil {
                return mainSymbols[condition]!
            } else {
                return "wrench"
            }
        }
    }

    func getColorSymbol(condition: Int, isNight: Bool) -> String {
        let regular = getSymbol(condition: condition, isNight: isNight)

        if regular != "wrench" && regular != "snow" && regular != "tornado" {
            return regular + ".fill"
        } else {
            return regular
        }
    }

    func isNight(dt: Int, sunrise: Int, sunset: Int) -> Bool {
        if dt < sunrise || dt > sunset {
            return true
        } else {
            return false
        }
    }

}