{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}

-- |
-- Experimental features, slow to compile when used.
module Data.TypedEncoding.Combinators.Encode.Experimental where

import           Data.TypedEncoding.Combinators.Encode
import           Data.TypedEncoding.Common.Types.Enc
import           Data.TypedEncoding.Common.Types.Common
import           Data.TypedEncoding.Common.Util.TypeLits -- Append
import           Data.TypedEncoding.Common.Class.Encode    
import           Data.Functor.Identity
import           GHC.TypeLits

-- * Combinators equivalent to "Data.TypedEncoding.Common.Class.Encode" that automatically figure out algorithm name.
-- Cause slow compilation when used

_encodeF :: forall nm xs f c str alg . (Encode f nm alg c str, alg ~ AlgNm nm) => Enc xs c str -> f (Enc (nm ': xs) c str)
_encodeF :: Enc @[Symbol] xs c str
-> f (Enc @[Symbol] ((':) @Symbol nm xs) c str)
_encodeF = forall (xs :: [Symbol]) (f :: * -> *) c str.
Encode f nm (AlgNm nm) c str =>
Enc @[Symbol] xs c str
-> f (Enc @[Symbol] ((':) @Symbol nm xs) c str)
forall (alg :: Symbol) (nm :: Symbol) (xs :: [Symbol])
       (f :: * -> *) c str.
Encode f nm alg c str =>
Enc @[Symbol] xs c str
-> f (Enc @[Symbol] ((':) @Symbol nm xs) c str)
encodeF' @(AlgNm nm) @nm

_encodeFAll :: forall nms f c str algs . (Monad f,  EncodeAll f nms algs c str, algs ~ AlgNmMap nms) =>  
               Enc ('[]::[Symbol]) c str 
               -> f (Enc nms c str)  
_encodeFAll :: Enc @[Symbol] ('[] @Symbol) c str -> f (Enc @[Symbol] nms c str)
_encodeFAll = forall (algs :: [Symbol]) (nms :: [Symbol]) (f :: * -> *) c str.
(Monad f, EncodeAll f nms algs c str) =>
Enc @[Symbol] ('[] @Symbol) c str -> f (Enc @[Symbol] nms c str)
forall (f :: * -> *) c str.
(Monad f, EncodeAll f nms (AlgNmMap nms) c str) =>
Enc @[Symbol] ('[] @Symbol) c str -> f (Enc @[Symbol] nms c str)
encodeFAll' @(AlgNmMap nms) @nms

_encodeAll :: forall nms c str algs . (EncodeAll Identity nms algs c str, algs ~ AlgNmMap nms) =>
               Enc ('[]::[Symbol]) c str 
               -> Enc nms c str 
_encodeAll :: Enc @[Symbol] ('[] @Symbol) c str -> Enc @[Symbol] nms c str
_encodeAll = forall (algs :: [Symbol]) (nms :: [Symbol]) c str.
EncodeAll Identity nms algs c str =>
Enc @[Symbol] ('[] @Symbol) c str -> Enc @[Symbol] nms c str
forall c str.
EncodeAll Identity nms (AlgNmMap nms) c str =>
Enc @[Symbol] ('[] @Symbol) c str -> Enc @[Symbol] nms c str
encodeAll' @(AlgNmMap nms) @nms 

_encodeFPart :: forall xs xsf f c str algs . (Monad f, EncodeAll f xs algs c str, algs ~ AlgNmMap xs) => Enc xsf c str -> f (Enc (Append xs xsf) c str)
_encodeFPart :: Enc @[Symbol] xsf c str
-> f (Enc @[Symbol] (Append @Symbol xs xsf) c str)
_encodeFPart = forall (algs :: [Symbol]) (xs :: [Symbol]) (xsf :: [Symbol])
       (f :: * -> *) c str.
(Monad f, EncodeAll f xs algs c str) =>
Enc @[Symbol] xsf c str
-> f (Enc @[Symbol] (Append @Symbol xs xsf) c str)
forall (xsf :: [Symbol]) (f :: * -> *) c str.
(Monad f, EncodeAll f xs (AlgNmMap xs) c str) =>
Enc @[Symbol] xsf c str
-> f (Enc @[Symbol] (Append @Symbol xs xsf) c str)
encodeFPart' @(AlgNmMap xs) @xs

_encodePart :: forall xs xsf c str algs . (EncodeAll Identity xs algs c str, algs ~ AlgNmMap xs) => Enc xsf c str -> Enc (Append xs xsf) c str   
_encodePart :: Enc @[Symbol] xsf c str
-> Enc @[Symbol] (Append @Symbol xs xsf) c str
_encodePart = forall (algs :: [Symbol]) (xs :: [Symbol]) (xsf :: [Symbol]) c str.
EncodeAll Identity xs algs c str =>
Enc @[Symbol] xsf c str
-> Enc @[Symbol] (Append @Symbol xs xsf) c str
forall (xsf :: [Symbol]) c str.
EncodeAll Identity xs (AlgNmMap xs) c str =>
Enc @[Symbol] xsf c str
-> Enc @[Symbol] (Append @Symbol xs xsf) c str
encodePart' @(AlgNmMap xs) @xs