{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE GADTs                #-}
{-# LANGUAGE LambdaCase           #-}
{-# LANGUAGE TypeOperators        #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE UndecidableInstances #-}

module Test.Schema.QuickCheck.Internal.Gen
     ( ToGen (..)
     ) where

import           Control.Applicative         (liftA2)
import           Control.Applicative.Free
import           Control.Functor.HigherOrder
import           Control.Lens
import           Control.Monad               (liftM)
import           Control.Natural
import           Data.Functor.Sum
import qualified Data.HashMap.Strict         as Map
import qualified Data.List.NonEmpty          as NEL
import           Data.Schema.Internal.Types
import qualified Data.Vector                 as Vector
import           Test.QuickCheck             (Gen)
import qualified Test.QuickCheck             as Gen

optGen :: Gen a -> Gen (Maybe a)
optGen base = Gen.frequency [(1, return Nothing), (3, liftM Just base)]

class ToGen a where
  toGen :: a ~> Gen

instance (ToGen p, ToGen q) => ToGen (Sum p q) where
  toGen (InL l) = toGen l
  toGen (InR r) = toGen r

genAlg :: ToGen p => HAlgebra (SchemaF p) Gen
genAlg = wrapNT $ \case
  PrimitiveSchema p         -> toGen p
  RecordSchema (Field flds) -> runAp genField flds
    where genField :: FieldDef o Gen a -> Gen a
          genField (RequiredField _ g _) = g
          genField (OptionalField _ g _) = optGen g
  UnionSchema alts          -> Gen.oneof . NEL.toList $ fmap genAlt alts
    where genAlt :: AltDef Gen a -> Gen a
          genAlt (AltDef _ genSingle pr) = (view $ re pr) <$> genSingle
  AliasSchema base iso      -> view iso <$> base

instance ToGen s => ToGen (Schema s) where
  toGen schema = (cataNT genAlg) (unwrapSchema schema)