//
//  XcassetsGenerator.swift
//  
//
//  Created by Thibaut Schmitt on 24/01/2022.
//

import Foundation
import ToolCore

class XcassetsGenerator {
    
    static let outputImageExtension = "png"
    
    let forceGeneration: Bool
    
    // MARK: - Init
    
    init(forceGeneration: Bool) {
        self.forceGeneration = forceGeneration
    }
    
    // MARK: - Assets generation
    
    func generateXcassets(inputPath: String, imagesToGenerate: [ParsedImage], xcassetsPath: String) {
        let fileManager = FileManager()
        let svgConverter = Images.getSvgConverterPath()
        let allSubFiles = fileManager.getAllRegularFileIn(directory: inputPath)
        
        var generatedAssetsPaths = [String]()
        
        // Generate new assets
        imagesToGenerate.forEach { parsedImage in
            // Get image path
            let imageData: (path: String, ext: String) = {
                for subfile in allSubFiles {
                    if subfile.hasSuffix("/" + parsedImage.name + ".svg") {
                        return (subfile, "svg")
                    }
                    if subfile.hasSuffix("/" + parsedImage.name + ".png") {
                        return (subfile, "png")
                    }
                    if subfile.hasSuffix("/" + parsedImage.name + ".jpg") {
                        return (subfile, "jpg")
                    }
                    if subfile.hasSuffix("/" + parsedImage.name + ".jepg") {
                        return (subfile, "jepg")
                    }
                }
                let error = ImagesError.unknownImageExtension(parsedImage.name)
                print(error.description)
                Images.exit(withError: error)
            }()
            
            // Create imageset folder name
            let imagesetName = "\(parsedImage.name).imageset"
            let imagesetPath = "\(xcassetsPath)/\(imagesetName)"
            
            // Store managed images path
            generatedAssetsPaths.append(imagesetName)
            
            // Generate output images path
            let output1x = "\(imagesetPath)/\(parsedImage.name).\(XcassetsGenerator.outputImageExtension)"
            let output2x = "\(imagesetPath)/\(parsedImage.name)@2x.\(XcassetsGenerator.outputImageExtension)"
            let output3x = "\(imagesetPath)/\(parsedImage.name)@3x.\(XcassetsGenerator.outputImageExtension)"
            
            // Check if we need to convert image
            guard self.shouldGenerate(inputImagePath: imageData.path, xcassetImagePath: output1x) else {
                //print("\(parsedImage.name) -> Not regenerating")
                return
            }
            
            // Create imageset folder
            if fileManager.fileExists(atPath: imagesetPath) == false {
                do {
                    try fileManager.createDirectory(atPath: imagesetPath,
                                                    withIntermediateDirectories: true)
                } catch {
                    let error = ImagesError.createAssetFolder(imagesetPath)
                    print(error.description)
                    Images.exit(withError: error)
                }
            }
            
            // Convert image
            let convertArguments = parsedImage.convertArguments
            if imageData.ext == "svg" {
                // /usr/local/bin/rsvg-convert path/to/image.png -w 200 -h 300 -o path/to/output.png
                // /usr/local/bin/rsvg-convert path/to/image.png -w 200 -o path/to/output.png
                // /usr/local/bin/rsvg-convert path/to/image.png -h 300 -o path/to/output.png
                var command1x = ["\(svgConverter)", "\(imageData.path)"]
                var command2x = ["\(svgConverter)", "\(imageData.path)"]
                var command3x = ["\(svgConverter)", "\(imageData.path)"]
                
                self.addConvertArgument(command: &command1x, convertArgument: convertArguments.x1)
                self.addConvertArgument(command: &command2x, convertArgument: convertArguments.x2)
                self.addConvertArgument(command: &command3x, convertArgument: convertArguments.x3)
                
                command1x.append(contentsOf: ["-o", output1x])
                command2x.append(contentsOf: ["-o", output2x])
                command3x.append(contentsOf: ["-o", output3x])
                
                Shell.shell(command1x)
                Shell.shell(command2x)
                Shell.shell(command3x)
            } else {
                // convert path/to/image.png -resize 200x300 path/to/output.png
                // convert path/to/image.png -resize 200x path/to/output.png
                // convert path/to/image.png -resize x300 path/to/output.png
                Shell.shell(["convert", "\(imageData.path)",
                             "-resize", "\(convertArguments.x1.width ?? "")x\(convertArguments.x1.height ?? "")",
                             output1x])
                Shell.shell(["convert", "\(imageData.path)",
                             "-resize", "\(convertArguments.x2.width ?? "")x\(convertArguments.x2.height ?? "")",
                             output2x])
                Shell.shell(["convert", "\(imageData.path)",
                             "-resize", "\(convertArguments.x3.width ?? "")x\(convertArguments.x3.height ?? "")",
                             output3x])
            }
            
            // Write Content.json
            let imagesetContentJson = parsedImage.contentJson
            let contentJsonFilePath = "\(imagesetPath)/Contents.json"
            
            let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath)
            try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: false, encoding: .utf8)
            
            print("\(parsedImage.name) -> Generated")
        }
        
        // Success info
        let generatedAssetsCount = generatedAssetsPaths.count
        print("Images generated: \(generatedAssetsCount)")
        
        // Delete old assets
        let allImagesetName = Set(fileManager.getAllImageSetFolderIn(directory: xcassetsPath))        
        let imagesetToRemove = allImagesetName.subtracting(Set(generatedAssetsPaths))
        
        imagesetToRemove.forEach {
            print("Will remove: \($0)")
        }
        
        imagesetToRemove.forEach { itemToRemove in
            try! fileManager.removeItem(atPath: "\(xcassetsPath)/\(itemToRemove)")
        }
        print("Removed \(imagesetToRemove.count) images")
    }
    
    // MARK: - Helpers: SVG command
    
    private func addConvertArgument(command: inout [String], convertArgument: ConvertArgument) {
        if let width = convertArgument.width, width.isEmpty == false {
            command.append("-w")
            command.append("\(width)")
        }
        if let height = convertArgument.height, height.isEmpty == false {
            command.append("-h")
            command.append("\(height)")
        }
    }
    
    // MARK: - Helpers: bypass generation
    
    private func shouldGenerate(inputImagePath: String, xcassetImagePath: String) -> Bool {
        if forceGeneration {
            return true
        }
        
        return GeneratorChecker.isFile(inputImagePath, moreRecenThan: xcassetImagePath)
    }
}
