{- Exercise 1 -}
-- TODO: add type synonym for Age

exampleAges :: [Age]
exampleAges = [("Alice", 17), ("Bob", 35), ("Clara", 17)]

-- Question 1.2
ticketCostA :: Age -> String
ticketCostA = undefined

ticketCostB :: Age -> String
ticketCostB = undefined

-- Question 1.3
ageLookup :: [Age] -> Integer -> Maybe [String]
ageLookup = undefined

-- Question 1.4
bidirectionalLookup :: (Eq b, Eq a) => Either a b -> [(a, b)] -> Maybe (Either a b)
bidirectionalLookup = undefined

{- Exercise 2 -}
data Tree a = Node a (Tree a) (Tree a) | X deriving Show

exampleTree = Node 1 (Node 2 X X) (Node 3 X (Node 4 X X))

takeLevels :: Int -> Tree a -> Tree a
takeLevels = undefined

dropLevels :: Int -> Tree a -> [Tree a]
dropLevels _ X = [X]
dropLevels n t = undefined

splitAtLevel :: Int -> Tree a -> (Tree a, [Tree a])
splitAtLevel = undefined

fillXs :: Tree a -> [Tree a] -> (Tree a, [Tree a])
fillXs = undefined

{- Tests -}
checkEx1 = sequence_ [test1, test2, test3, test4]

checkEx2 = mapM_ putStrLn  [test5, test6, test7, test8, test9, test10]

-- Internal construction of tests
test1 = mapM_ (putStrLn . (\((n, a), c) -> check "ticketCostA" (show $ n ++ " pays " ++ c ++ " euros for a ticket") (ticketCostA (n, a)))) (zip testAges testCosts)

test2 = mapM_ (putStrLn . (\((n, a), c) -> check "ticketCostB" (show $ n ++ " pays " ++ c ++ " euros for a ticket") (ticketCostB (n, a)))) (zip testAges testCosts)

test3 =
  mapM_
    (putStrLn . (\(i, o) -> check "ageLookup" (show o) (ageLookup testAges i)))
    (zip [50, 13, 12] [Just ["D"], Nothing, Just ["B", "E"]])

test4 =
  mapM_
    (putStrLn . (\(i, o) -> check "bidirectionalLookup" (show o) (bidirectionalLookup i testAges)))
    (zip [Right 0, Right 12, Left "E", Left "F"] [Just (Left "A"), Just (Left "B"), Just (Right 12), Nothing])

test5 = check "test1" (show $ Node 1 (Node 2 X X) (Node 3 X X)) (takeLevels 2 exampleTree)
test6 = check "test2" (show [X, X, X, Node 4 X X]) (dropLevels 2 exampleTree)
test7 = check "test3" (show (Node 1 (Node 2 X X) (Node 3 X X), [X, X, X, Node 4 X X])) (splitAtLevel 2 exampleTree)
test8 = check "test4" (show (exampleTree, [] :: [Tree Int])) (uncurry fillXs $ splitAtLevel 0 exampleTree)
test9 = check "test5" (show (exampleTree, [] :: [Tree Int])) (uncurry fillXs $ splitAtLevel 2 exampleTree)
test10 = check "test6" (show (exampleTree, [] :: [Tree Int])) (uncurry fillXs $ splitAtLevel 5 exampleTree)

testAges = [("A", 0), ("B", 12), ("C", 17), ("D", 50), ("E", 12)]
testCosts = ["5", "5", "7.50", "15", "5"]

check name e c =
  "*** " ++ name ++ ": " ++ (
    if show c == e then "OK"
    else "ERROR; expected '" ++ e ++ "', but found '" ++ show c ++ "'")