module Crypto.PasswordGenerator
( Flag(..)
, genPassword
) where

import Control.Applicative
import Data.List
import Test.QuickCheck.Gen

data Flag = Digits
          | Uppers
          | Lowers
          | Symbols
          | Ambiguous
          | Vowels
            deriving (Eq)

-- | Generate a password of required length with provided flags.
genPassword :: [Flag] -> Int -> IO String
genPassword flags len = try
  where
    try = do
      l <- sample' $ vectorOf len $ oneof gens
      let v = find good l
      case v of
        Just x -> return x
        Nothing -> try
    gens = map toGen flags
    good = foldl' (\a v -> (liftA2 (&&)) a $ toChecker v) alwaysTrue flags

digitList    = sort "0123456789"
upperList    = sort "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
lowerList    = sort "abcdefghijklmnopqrstuvwxyz"
symbolList   = sort "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
ambiguouList = sort "B8G6I1l0OQDS5Z2"
vowelList    = sort "01aeiouyAEIOUY"

toGen Digits    = elements digitList
toGen Uppers    = elements upperList
toGen Lowers    = elements lowerList
toGen Symbols   = elements symbolList
toGen Ambiguous = elements ambiguouList
toGen Vowels    = elements vowelList

toChecker Digits  s = s `containsAnyOf` digitList
toChecker Uppers  s = s `containsAnyOf` upperList
toChecker Symbols s = s `containsAnyOf` symbolList
toChecker _       s = True

alwaysTrue s = True

s `containsAnyOf` l = not $ null $ intersect l s