{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Dormouse.Generators.Json 
  ( genJsonValue
  , JsonGenRanges(..)
  )
  where

import qualified Data.Aeson as A
import qualified Data.Scientific as S
import qualified Data.Vector as V
import Hedgehog
import qualified Hedgehog.Gen as Gen

data JsonGenRanges = JsonGenRanges 
  { stringRanges :: Range Int
  , doubleRanges :: Range Double
  , arrayLenRanges :: Range Int
  }

genJsonNull :: Gen A.Value
genJsonNull = pure A.Null

genJsonString :: Range Int -> Gen A.Value
genJsonString sr = fmap A.String $ Gen.text sr Gen.unicode

genJsonBool :: Gen A.Value
genJsonBool = fmap A.Bool Gen.bool

genJsonNumber :: Range Double -> Gen A.Value
genJsonNumber r = fmap (A.Number . S.fromFloatDigits) $ Gen.double r

genJsonArray :: JsonGenRanges -> Gen A.Value
genJsonArray ranges = fmap (A.Array . V.fromList) $ Gen.list ar gen
  where
    gen = Gen.recursive Gen.choice [genJsonBool, genJsonNumber nr, genJsonString sr] [genJsonValue ranges, genJsonObject ranges]
    nr = doubleRanges ranges
    sr = stringRanges ranges
    ar = arrayLenRanges ranges

genJsonObject :: JsonGenRanges -> Gen A.Value
genJsonObject ranges =  fmap A.object $ Gen.list ar genNameValue
  where
    genNameValue = do
      name <- Gen.text sr Gen.unicode
      value <- genJsonValue ranges
      return (name, value)
    sr = stringRanges ranges
    ar = arrayLenRanges ranges

genJsonValue :: JsonGenRanges -> Gen A.Value
genJsonValue ranges = Gen.choice [genJsonNull, genJsonString (stringRanges ranges), genJsonBool, genJsonNumber (doubleRanges ranges), genJsonArray ranges, genJsonObject ranges]