{-# LANGUAGE MultiParamTypeClasses #-}

import Control.Monad.Chrono
import Control.Monad.IO.Class

newtype Pos = Pos (Int, Int, Int) deriving Show

data DeltaPos = MoveX Int | MoveY Int | MoveZ Int

instance Keystone Pos DeltaPos where
    redo (MoveX n) (Pos (x, y, z)) = Pos (x + n, y, z)
    redo (MoveY n) (Pos (x, y, z)) = Pos (x, y + n, z)
    redo (MoveZ n) (Pos (x, y, z)) = Pos (x, y, z + n)
    undo (MoveX n) = redo $ MoveX $ -n
    undo (MoveY n) = redo $ MoveY $ -n
    undo (MoveZ n) = redo $ MoveZ $ -n

chronoExample :: ChronoT Pos DeltaPos IO ()
chronoExample = do
    step $ MoveX 10
    step $ MoveY 9
    step $ MoveZ 8
    step $ MoveX 2
    s1 <- get
    rewind 2
    s2 <- get
    unwind 1
    s3 <- get
    rewind 1
    s4 <- get
    put $ Pos (1, 1, 1)
    s5 <- get
    unwind 2
    s6 <- get
    liftIO $ print $ "Splices: " ++ show (s1, s2, s3, s4, s5, s6)

main :: IO ()
main = do
    out <- execChronoT chronoExample $ Pos (0, 0, 0)
    print $ "Output: " ++ show out