import Foundation

let program = [3,8,1001,8,10,8,105,1,0,0,21,38,59,84,93,110,191,272,353,434,99999,3,9,101,5,9,9,1002,9,5,9,101,5,9,9,4,9,99,3,9,1001,9,3,9,1002,9,2,9,101,4,9,9,1002,9,4,9,4,9,99,3,9,102,5,9,9,1001,9,4,9,1002,9,2,9,1001,9,5,9,102,4,9,9,4,9,99,3,9,1002,9,2,9,4,9,99,3,9,1002,9,5,9,101,4,9,9,102,2,9,9,4,9,99,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,99,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,99,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,99,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,99]


class Io: IO {
    var inputs = [4, 0, 3, 2, 1, 0]

    func input() -> Int {
        let input = inputs.remove(at: 0)
        return input
    }

    var lastOutput = 0
    func output(_ value: Int) {
        lastOutput = value
        if !inputs.isEmpty {
            inputs.insert(value, at: 1)
        }
    }
}

class Buffered: IO {
    var inputs: [Int] = []
    let condition = NSCondition()
    var outputHandler: (Int) -> Void = { _ in }



    func input() -> Int {
        condition.lock()
        defer { condition.unlock() }

        while inputs.isEmpty {
            condition.wait()
        }

        return inputs.removeFirst()
    }

    func add(input: Int) {
        condition.lock()
        defer { condition.unlock() }

        inputs.append(input)
        condition.signal()
    }

    func output(_ value: Int) {
        outputHandler(value)
    }
}

func test(phases: [Int]) -> Int {

    let ios = phases.map { phase -> Buffered in
        let b = Buffered()
        b.add(input: phase)
        return b
    }

    ios[0].add(input: 0)

    for i in 0 ..< ios.count - 1 {
        ios[i].outputHandler = { [unowned next = ios[i + 1]] val in
            next.add(input: val)
        }
    }

    var lastOutput = -1

    ios[ios.count - 1].outputHandler = { [unowned first = ios[0]] val in
        first.add(input: val)
        lastOutput = val
        print("out>", val)
    }

    let group = DispatchGroup()

    for io in ios {
        DispatchQueue.global().async(group: group) {
            IntCode(program: program, io: io).run()
        }
    }

    group.wait()

    print("done>", lastOutput)
    print("\n\n")

    return lastOutput
}

var maxOutput = Int.min
var maxPhases: [Int] = []

for a in 0...4 {
    for b in 0...4  where b != a {
        for c in 0...4 where c != b && c != a {
            for d in 0...4 where d != c && d != b && d != a {
                for e in 0...4 where e != d && e != c && e != c && e != b && e != a {
                    let phases = [5 + a, 5 + b, 5 + c, 5 + d, 5 + e]
                    let output = test(phases: phases)
                    if maxOutput < output {
                        maxOutput = output
                        maxPhases = phases
                    }
                }
            }
        }
    }
}

maxOutput
maxPhases