module Sqel.Codec.Product where

import Generics.SOP (
  All,
  I,
  K (K),
  NP,
  NS (Z),
  Projection,
  SOP (SOP),
  Top,
  hcmap,
  hcollapse,
  hsequence,
  hzipWith,
  projections,
  unI,
  unSOP,
  unZ,
  type  (-.->) (Fn),
  )
import Generics.SOP.GGP (gfrom, gto)
import Lens.Micro.Extras (view)

import qualified Sqel.Data.Codec as Codec
import Sqel.Data.Codec (Codec (Codec), Decoder, Encoder, FullCodec)
import Sqel.SOP.Constraint (ConstructProd, ReifyProd)

prodParams ::
   as b .
  Contravariant b =>
  ( x . Monoid (b x)) =>
  All Top as =>
  NP b as ->
  b (NP I as)
prodParams :: forall (as :: [*]) (b :: * -> *).
(Contravariant b, forall x. Monoid (b x), All Top as) =>
NP b as -> b (NP I as)
prodParams NP b as
np =
  forall a. Monoid a => [a] -> a
mconcat (forall k l (h :: (k -> *) -> l -> *) (xs :: l) a.
(HCollapse h, SListIN h xs) =>
h (K a) xs -> CollapseTo h a
hcollapse NP (K (b (NP I as))) as
qps)
  where
    qps :: NP (K (b (NP I as))) as
    qps :: NP (K (b (NP I as))) as
qps =
      forall {k} {l} (h :: (k -> *) -> l -> *) (xs :: l) (f :: k -> *)
       (f' :: k -> *) (f'' :: k -> *).
(SListIN (Prod h) xs, HAp h, HAp (Prod h)) =>
(forall (a :: k). f a -> f' a -> f'' a)
-> Prod h f xs -> h f' xs -> h f'' xs
hzipWith forall a. b a -> Projection I as a -> K (b (NP I as)) a
qp NP b as
np (forall {k} (xs :: [k]) (f :: k -> *).
SListI xs =>
NP (Projection f xs) xs
projections :: NP (Projection I as) as)
    {-# inline qps #-}
    qp ::  a . b a -> Projection I as a -> K (b (NP I as)) a
    qp :: forall a. b a -> Projection I as a -> K (b (NP I as)) a
qp b a
par (Fn K (NP I as) a -> I a
proj) =
      forall k a (b :: k). a -> K a b
K (forall (f :: * -> *) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap (forall a. I a -> a
unI forall b c a. (b -> c) -> (a -> b) -> a -> c
. K (NP I as) a -> I a
proj forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall k a (b :: k). a -> K a b
K) b a
par)
    {-# inline qp #-}
{-# inline prodParams #-}

type GetEncoder :: (Type -> Type) -> Type -> Constraint
class GetEncoder b a where
  getEncoder :: b a -> Encoder a

instance GetEncoder FullCodec a where
  getEncoder :: FullCodec a -> Encoder a
getEncoder = forall a s. Getting a s a -> s -> a
view forall a. IsLabel "encoder" a => a
#encoder

instance GetEncoder Encoder a where
  getEncoder :: Encoder a -> Encoder a
getEncoder = forall a. a -> a
id

type GetDecoder :: (Type -> Type) -> Type -> Constraint
class GetDecoder b a where
  getDecoder :: b a -> Decoder a

instance GetDecoder FullCodec a where
  getDecoder :: FullCodec a -> Decoder a
getDecoder = forall a s. Getting a s a -> s -> a
view forall a. IsLabel "decoder" a => a
#decoder

type ProdEncoder :: (Type -> Type) -> Type -> [Type] -> Constraint
class ProdEncoder b a as | a -> as where
  prodEncoder :: NP b as -> Encoder a

instance (
    ConstructProd a as,
    All (GetEncoder b) as
  ) => ProdEncoder b a as where
    prodEncoder :: NP b as -> Encoder a
prodEncoder NP b as
np = forall {k} (f :: k -> *) (x :: k). NS f '[x] -> f x
unZ forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (f :: k -> *) (xss :: [[k]]). SOP f xss -> NS (NP f) xss
unSOP forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (GFrom a, Generic a) => a -> SOP I (GCode a)
gfrom forall (f :: * -> *) a b. Contravariant f => (a -> b) -> f b -> f a
>$< forall (as :: [*]) (b :: * -> *).
(Contravariant b, forall x. Monoid (b x), All Top as) =>
NP b as -> b (NP I as)
prodParams (forall {k} {l} (h :: (k -> *) -> l -> *) (c :: k -> Constraint)
       (xs :: l) (proxy :: (k -> Constraint) -> *) (f :: k -> *)
       (f' :: k -> *).
(AllN (Prod h) c xs, HAp h) =>
proxy c
-> (forall (a :: k). c a => f a -> f' a) -> h f xs -> h f' xs
hcmap (forall {k} (t :: k). Proxy t
Proxy @(GetEncoder b)) forall (b :: * -> *) a. GetEncoder b a => b a -> Encoder a
getEncoder NP b as
np)

type ProdDecoder :: (Type -> Type) -> Type -> [Type] -> Constraint
class ProdDecoder b a as | a -> as where
  prodDecoder :: NP b as -> Decoder a

instance (
    ReifyProd a as,
    All (GetDecoder b) as
  ) => ProdDecoder b a as where
    prodDecoder :: NP b as -> Decoder a
prodDecoder NP b as
np = forall a. (GTo a, Generic a) => SOP I (GCode a) -> a
gto forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall k (f :: k -> *) (xss :: [[k]]). NS (NP f) xss -> SOP f xss
SOP forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (a :: k -> *) (x :: k) (xs :: [k]). a x -> NS a (x : xs)
Z forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall {l} (h :: (* -> *) -> l -> *) (xs :: l) (f :: * -> *).
(SListIN h xs, SListIN (Prod h) xs, HSequence h, Applicative f) =>
h f xs -> f (h I xs)
hsequence (forall {k} {l} (h :: (k -> *) -> l -> *) (c :: k -> Constraint)
       (xs :: l) (proxy :: (k -> Constraint) -> *) (f :: k -> *)
       (f' :: k -> *).
(AllN (Prod h) c xs, HAp h) =>
proxy c
-> (forall (a :: k). c a => f a -> f' a) -> h f xs -> h f' xs
hcmap (forall {k} (t :: k). Proxy t
Proxy @(GetDecoder b)) forall (b :: * -> *) a. GetDecoder b a => b a -> Decoder a
getDecoder NP b as
np)

type ProdCodec :: (Type -> Type) -> Type -> [Type] -> Constraint
class ProdCodec b a as | a -> as where
  prodCodec :: NP b as -> b a

instance (
    ProdDecoder FullCodec a as,
    ProdEncoder FullCodec a as
  ) => ProdCodec FullCodec a as where
    prodCodec :: NP FullCodec as -> FullCodec a
prodCodec NP FullCodec as
np =
      Codec {
        $sel:decoder:Codec :: Decoder a
decoder = forall (b :: * -> *) a (as :: [*]).
ProdDecoder b a as =>
NP b as -> Decoder a
prodDecoder NP FullCodec as
np,
        $sel:encoder:Codec :: Encoder a
encoder = forall (b :: * -> *) a (as :: [*]).
ProdEncoder b a as =>
NP b as -> Encoder a
prodEncoder NP FullCodec as
np
      }

instance (
    ProdEncoder Encoder a as
  ) => ProdCodec Encoder a as where
    prodCodec :: NP Encoder as -> Encoder a
prodCodec = forall (b :: * -> *) a (as :: [*]).
ProdEncoder b a as =>
NP b as -> Encoder a
prodEncoder