{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE UndecidableInstances #-}
module Test.SmallCheck.GenericK where

import Control.Applicative
import Data.Kind (Type)
import Data.Type.Equality
import GHC.Generics (C)
import Generics.Kind hiding ((:~:))
import Type.Reflection (Typeable, typeRep)

import Test.SmallCheck.Series

genericSeries ::  m a x q . (GenericK a, GSerial m (RepK a) x) => q x -> Series m a
genericSeries _ = toK <$> (gSeries :: Series m (RepK a x))

class GSerial m (f :: LoT k -> Type) (x :: LoT k) where
    gSeries :: Series m (f x)

instance GSerial m U1 x where
    gSeries = pure U1
instance GSerial m V1 x where
    gSeries = empty
instance (Monad m, GSerial m f x, GSerial m g x) => GSerial m (f :+: g) x where
    gSeries = (L1 <$> gSeries) \/ (R1 <$> gSeries)
instance (Monad m, GSerial m f x, GSerial m g x) => GSerial m (f :*: g) x where
    gSeries = (:*:) <$> gSeries <~> gSeries
instance (Typeable i, GSerial m f x) => GSerial m (M1 i c f) x where
    gSeries = ($ M1 <$> gSeries) $ case testEquality typeRep typeRep :: Maybe (i :~: C) of
        Just _ -> decDepth; _ -> id
instance (Serial m (Interpret t x)) => GSerial m (Field t) x where
    gSeries = Field <$> series
instance ( (t :: k) . GSerial m f (t :&&: x)) => GSerial m (Exists k f) x where
    gSeries = Exists <$> gSeries
instance (Interpret c x, GSerial m f x) => GSerial m (c :=>: f) x where
    gSeries = SuchThat <$> gSeries