A good snapshot stops a beautiful moment from running away. At some point, we’ll press the restore button and relive that moment again.

The memento pattern captures the complete state of an object into a memento that can be used to reset the object at a later date. It allows a complete reset of an object without the need to track and apply individual undo commands. –  Design Pattern in Swift

//: Just a sign to show the class / struct is a memento, nothing special
protocol Memento {
}

//: The originator can create and restore game state
protocol Originator {
    func createMemento() -> Memento
    func applyMemento(memento: Memento)
}

//: a game state entry
struct GameStateEntry {
    var level: Int
    var weapon: String
    var points: Int
}

//: A GameState memento, which contains all the information the CheckPoint needed to restore the state
struct GameMemento: Memento {
    private let entries: [Int: GameStateEntry]
    private let nextId: Int
    private let totalPoints: Int
    init(checkPoint: CheckPoint){
        self.entries = checkPoint.entries
        self.nextId = checkPoint.nextId
        self.totalPoints = checkPoint.totalPoints
    }
    func apply(checkPoint: CheckPoint) {
        print("Restoring a game state to a checkpoint...")
        checkPoint.nextId = nextId
        checkPoint.totalPoints = totalPoints
        checkPoint.entries = entries
    }
}
//: A CheckPoint can create and restore game state
class CheckPoint: Originator {
    private var entries: [Int: GameStateEntry] = [:]
    private var totalPoints: Int  = 0
    private var nextId: Int = 0
    
    func addGameStateEntry(level: Int, weapon: String, points: Int) {
        let entry = GameStateEntry(level: level, weapon: weapon, points: points)
        entries[nextId++] = entry
        totalPoints += points
    }
    
    func createMemento() -> Memento {
        return GameMemento(checkPoint: self)
    }
    
    func applyMemento(memento: Memento) {
        guard let m = memento as? GameMemento  else { return }
        m.apply(self)
    }
    
    func printCheckPoint() {
        print("Printing checkPoint....")
        entries.sort {$0.0 < $1.0}
            .map {
                print("Level: \($0.1.level)   Weapon: \($0.1.weapon)   Points: \($0.1.points) ")
        }
        print("Total Points: \(totalPoints)\n")
        
    }
}


//:Testing

let checkPoint = CheckPoint()
checkPoint.addGameStateEntry(0, weapon: "Fire Ball", points: 20)
checkPoint.addGameStateEntry(1, weapon: "Flood", points: 10)
checkPoint.printCheckPoint()
let memento = GameMemento(checkPoint: checkPoint)
checkPoint.addGameStateEntry(2, weapon: "Crusher", points: 30)
checkPoint.addGameStateEntry(4, weapon: "Flower", points: 30)
checkPoint.printCheckPoint()

checkPoint.applyMemento(memento)
checkPoint.printCheckPoint()

memento