import Distribution.Fields.Field (getName)
type Name = String

type Age = Int

data Country
    = Denmark
    | DPRK
    | Latvia

personAge :: Name -> Maybe Age
personAge "Troels" = Just 38
personAge "Toxo" = Just 8
personAge "Sebastian" = Just 29
personAge _ = Nothing

mayVote0 :: Name -> Maybe Bool
mayVote0 name = fmap (>= 18) (personAge name)

mayVote1 :: (Functor f) => (Name -> f Age) -> Name -> f Bool
mayVote1 getAge name = fmap (>= 18) (getAge name)

anotherPersonAge :: Name -> Either String Age
anotherPersonAge "Troels" = Right 38
anotherPersonAge "Doctor Doom" = Left "Dr. Doom is ageless"
anotherPersonAge name = Left $ "Unknown person: " ++ name

votingAge :: Country -> Maybe Age
votingAge Denmark = Just 18
votingAge DPRK = Just 17
votingAge Latvia = Nothing

mayVote2 :: Applicative f => (Name -> f Age) -> (Country -> f Age) -> Name -> Country -> f Bool
mayVote2 getAge getVotingAge name country = 
    -- (>=) <$> (getAge name) <*> (getVotingAge country) -- Name check first
    (<=) <$> (getVotingAge country) <*> (getAge name) -- Age check first
    -- pure (>=) <*> (getAge name) <*> (getVotingAge country)

-- "Talking to the outside world is dangerous and uncomfortable for computer scientists"

findVoters :: Monad f => (Name -> f Age) -> (Country -> f Age) -> [Name] -> Country -> f [Name]
findVoters getAge getMinAge [] country = pure []
findVoters getAge getMinAge (name: names) country = canVote name >>= filtering
    where
        remaining = findVoters getAge getMinAge names country
        filtering b = if b then (name:) <$> remaining else remaining
        canVote name = (>=) <$> getAge name <*> getMinAge country

myVoters :: Maybe [Name]
myVoters = findVoters personAge votingAge ["Troels", "asd"] Latvia

getAgeFromUser :: Name -> IO Age
getAgeFromUser name = do
    putStrLn $ "How old is " ++ name ++ "?"
    a <- getLine
    pure (read a)

