#!/usr/bin/swift
//
//  main.swift
//  UpdateAutogenerated
//
//  Created by Krunoslav Zaher on 1/6/17.
//  Copyright © 2017 Krunoslav Zaher. All rights reserved.
//

import Foundation

if CommandLine.argc < 2 {
    print("find Sourcery -name \"*.swift\" | xargs ./Scripts/update-placeholders ./Sourcery/CodeGenerated/Coding.Generated.swift")
    exit(-1)
}

let finalVersions = CommandLine.arguments[1]
let targetFiles = CommandLine.arguments.dropFirst(2)

func escape(_ text: String) -> String {
    return NSRegularExpression.escapedPattern(for: text)
}

extension NSString {
    var entireRange: NSRange {
        return NSRange(location: 0, length: self.length)
    }
}

func placeholderRegex(name: String) -> NSRegularExpression {
    let startPattern = "\(escape("//"))\\s+(\(name))\\s+\(escape("{"))"
    let endPattern = "\(escape("//"))\\s+\(escape("}"))\\s+(\(name))"
    let ignoreMiddle = "(?:(?!\(endPattern)).)*"
    return try! NSRegularExpression(pattern:
        startPattern +
        ignoreMiddle +
        endPattern, options: [.allowCommentsAndWhitespace, .dotMatchesLineSeparators])
}

func placeholderNames(content: String) -> [String] {
    let regex = placeholderRegex(name: "\\S+")

    // if this cast doesn't exist, it will crash, Swift :(
    let nsContent = content as NSString
    return regex.matches(in: nsContent as String,
                         options: [],
                         range: nsContent.entireRange).map { match in
                            return (content as NSString).substring(with: match.rangeAt(1))
    }
}

extension String {
    func getPlaceholderValue(name: String) -> String? {
        let regex = placeholderRegex(name: escape(name))

        let range = regex.matches(in: self, options: [], range: self.entireRange).first!
        return (self as NSString).substring(with: range.rangeAt(0))
    }

    func setPlaceholder(name: String, value: String) -> String {
        let regex = placeholderRegex(name: escape(name))
        let first = regex.matches(in: self, options: [], range: self.entireRange).first!
        return (self as NSString).replacingCharacters(in: first.rangeAt(0), with: value)
    }
}

let content = try! String(contentsOfFile: finalVersions)

var placeholders: [String: String] = [:]
for name in placeholderNames(content: content) {
    placeholders[name] = content.getPlaceholderValue(name: name)
}

var replacedPlaceholders = Set<String>()
for filePath in targetFiles {
    let targetFile = try! String(contentsOfFile: filePath)

    if targetFile.hasPrefix("// Generated using Sourcery") {
        continue
    }

    let finalFile = placeholderNames(content: targetFile).reduce(targetFile){ (content, name) -> String in
        replacedPlaceholders.insert(name)
        guard let value = placeholders[name] else {
            fatalError("\(filePath): \(name) placeholder not found")
        }
        return content.setPlaceholder(name: name, value: value)
    }

    try! finalFile.write(toFile: filePath, atomically: true, encoding: .utf8)
}

let notReplaced = Set(placeholders.keys).subtracting(replacedPlaceholders)

if !notReplaced.isEmpty {
    print("We've not been able to find files to update \(notReplaced)")
    exit(-1)
}
