module System.Random.Atmosphere (
  getRandomNumbers  -- :: Int -> Int -> Int -> IO (Either String [Int])
, getRandomSequence -- :: Int -> Int -> IO (Either String [Int])
, getRandomStrings  -- :: Int -> Int -> Bool -> Bool -> Bool -> Bool -> IO (Either String [String])
, getQuota          -- :: IO (Either String Int)
) where
import Data.Either
import Network.URI
import Network.HTTP.Simple

-- URI for integer generation
int_uri num min max 
    = concat [ "http://random.org/integers/?num=", show num
             , "&min=", show min
             , "&max=", show max
             , "&col=1&base=10&format=plain&rnd=new" ]

-- URI for sequence generation
seq_uri min max
    = concat [ "http://www.random.org/sequences/?min=", show min
             , "&max=", show max
             , "&format=plain&rnd=new" ]

-- URI for string generation
str_uri num len digits uppercase lowercase unique
    = concat [ "http://www.random.org/strings/?num=", show num
             , "&len=", show len
             , "&digits=",     if digits    == True then "on" else "off"
             , "&upperalpha=", if uppercase == True then "on" else "off"
             , "&loweralpha=", if lowercase == True then "on" else "off"
             , "&unique=",     if unique    == True then "on" else "off"
             , "&format=plain&rnd=new" ]

-- | Used to get a list of random numbers from http://random.org.
-- Note: the minimum must be greater than the maximum. The maximum
-- amount of numbers you can retrieve is 10,000, and the numbers 
-- themselves are limited to a range of +/- 1,000,000,000 (inclusive)
getRandomNumbers :: Int -- ^ Number of integers to get
                 -> Int -- ^ Minimum number
                 -> Int -- ^ Maximum number
                 -> IO (Either String [Int]) -- ^ Returns either an error string or the list of integers
getRandomNumbers num min max
    | max <= min           = return $ Left "err: minimum must be greater than maximum"
    | num > 10000          = return $ Left "err: can't retrieve more than 10,000 numbers"
    | abs min > 1000000000 = return $ Left "err: min can't be more/less than 1,000,000,000"
    | abs max > 1000000000 = return $ Left "err: max can't be more/less than 1,000,000,000"
    | otherwise            = do let uri' = parseURI (int_uri num min max)
                                case uri' of
                                  Nothing -> return $ Left "err: parseURI returned Nothing"
                                  Just u  -> retrieve u (map read . lines)

-- | This is just like 'getRandomNumbers', but instead it will
-- randomize a given interval of integers. The limits on the minimum/maximum
-- are -1e9 to 1e9, inclusive. The interval itself must be less than 10,000.
getRandomSequence :: Int -- ^ Minimum
                  -> Int -- ^ Maximum
                  -> IO (Either String [Int])
getRandomSequence min max
    | max <= min            = return $ Left "err: max must be greater than min"
    | abs max > 1000000000  = return $ Left "err: max can't be greater/less than +/-1e9"
    | abs min > 1000000000  = return $ Left "err: min can't be greater/less than +/-1e9"
    | (max - min)+1 > 10000 = return $ Left "err: sequence must be smaller than 10,000"
    | otherwise     = do let uri = parseURI (seq_uri min max)
                         case uri of
                           Nothing -> return $ Left "err: parseURI returned Nothing"
                           Just u  -> retrieve u (map read . lines)

-- | Generate random strings. NOTE: if you specify to generate unique
-- strings, the amount you want to retrieve /must/ be less than or equal to the
-- amount of strings that exist within the character set you specify.
-- In the future there will be a check for this, but you have to be sure as of
-- right now.
getRandomStrings :: Int  -- ^ Number of strings requested, max is 10,000
                 -> Int  -- ^ Length of each string, max is 20
                 -> Bool -- ^ Should digits be allowed?
                 -> Bool -- ^ Should uppercase be allowed?
                 -> Bool -- ^ Should lowercase be allowed?
                 -> Bool -- ^ Should each string be unique?
                 -> IO (Either String [String])
getRandomStrings num len digits uppercase lowercase unique
    | num > 10000  = return $ Left "err: maximum number of strings is 10,000"
    | len > 20     = return $ Left "err: maximum str len is 20"
    | otherwise    = do let uri = parseURI (str_uri num len digits uppercase lowercase unique)
                        case uri of
                          Nothing -> return $ Left "err: parseURI returned Nothing"
                          Just u' -> retrieve u' lines

-- | Used to check how much of your random.org quota you have. Please see
-- <http://random.org/quota> for more info.
getQuota :: IO (Either String Int)
getQuota = do let uri = parseURI "http://random.org/quota/?format=plain"
              case uri of
                Nothing -> return $ Left "err: parseURI returned Nothing"
                Just u  -> retrieve u read


-- helper utility that calls httpGet for us
-- it applies some function to the returned string so we can remove some
-- boilerplate
retrieve uri f = do s <- httpGet uri
                    case s of
                      Nothing -> return $ Left "err: httpGet returned nothing"
                      Just s' -> return $ Right (f s')