{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}

-- |
-- Decoding types for @Enc@
--
-- This module is re-exported in "Data.TypedEncoding" and it is best not to import it directly.
module Data.TypedEncoding.Common.Types.Decoding where

import           Data.Proxy
import           GHC.TypeLits

import           Data.TypedEncoding.Common.Types.Enc
import           Data.TypedEncoding.Common.Types.Common

-- |
-- Similar to 'Data.TypedEncoding.Common.Types.Enc.Encoding'
--
-- Wraps the decoding function.
--
-- Can be used with 'Data.TypedEncoding.Common.Class.Decode.Decode' type class.
-- 
-- "Examples.TypedEncoding.Instances.DiySignEncoding" contains an implementation example.
--
-- "Examples.TypedEncoding.Overview" shows decoding usage examples.
--
-- @since 0.3.0.0
data Decoding f (nm :: Symbol) (alg :: Symbol) conf str where
    -- | Consider this constructor as private or use it with care
    --
    -- Using that constructor:
    -- @
    -- UnsafeMkDecoding :: Proxy nm -> (forall (xs :: [Symbol]) . Enc (nm ': xs) conf str -> f (Enc xs conf str)) -> Decoding f nm (AlgNm nm) conf str
    -- @
    -- 
    -- would make compilation much slower
    UnsafeMkDecoding :: Proxy nm -> (forall (xs :: [Symbol]) . Enc (nm ': xs) conf str -> f (Enc xs conf str)) -> Decoding f nm alg conf str

-- | Type safe smart constructor
-- (See also 'Data.TypedEncoding.Common.Types.Enc._mkEncoding')  
--
-- @since 0.3.0.0    
mkDecoding :: forall f (nm :: Symbol) conf str . (forall (xs :: [Symbol]) . Enc (nm ': xs) conf str -> f (Enc xs conf str)) -> Decoding f nm (AlgNm nm) conf str
mkDecoding :: (forall (xs :: [Symbol]).
 Enc @[Symbol] ((':) @Symbol nm xs) conf str
 -> f (Enc @[Symbol] xs conf str))
-> Decoding f nm (AlgNm nm) conf str
mkDecoding = (forall (xs :: [Symbol]).
 Enc @[Symbol] ((':) @Symbol nm xs) conf str
 -> f (Enc @[Symbol] xs conf str))
-> Decoding f nm (AlgNm nm) conf str
forall (f :: * -> *) (nm :: Symbol) conf str.
(forall (xs :: [Symbol]).
 Enc @[Symbol] ((':) @Symbol nm xs) conf str
 -> f (Enc @[Symbol] xs conf str))
-> Decoding f nm (AlgNm nm) conf str
_mkDecoding

{-# DEPRECATED mkDecoding "Use _mkDecoding" #-}



-- | Type safe smart constructor
-- (See also 'Data.TypedEncoding.Common.Types.Enc._mkEncoding')  
-- 
-- This function follows the naming convention of using "_" when the typechecker figures out @alg@
-- @since 0.5.0.0    
_mkDecoding :: forall f (nm :: Symbol) conf str . (forall (xs :: [Symbol]) . Enc (nm ': xs) conf str -> f (Enc xs conf str)) -> Decoding f nm (AlgNm nm) conf str
_mkDecoding :: (forall (xs :: [Symbol]).
 Enc @[Symbol] ((':) @Symbol nm xs) conf str
 -> f (Enc @[Symbol] xs conf str))
-> Decoding f nm (AlgNm nm) conf str
_mkDecoding = Proxy @Symbol nm
-> (forall (xs :: [Symbol]).
    Enc @[Symbol] ((':) @Symbol nm xs) conf str
    -> f (Enc @[Symbol] xs conf str))
-> Decoding f nm (Concat (LTakeUntil (ToList1 nm "") ":")) conf str
forall (nm :: Symbol) conf str (f :: * -> *) (alg :: Symbol).
Proxy @Symbol nm
-> (forall (xs :: [Symbol]).
    Enc @[Symbol] ((':) @Symbol nm xs) conf str
    -> f (Enc @[Symbol] xs conf str))
-> Decoding f nm alg conf str
UnsafeMkDecoding Proxy @Symbol nm
forall k (t :: k). Proxy @k t
Proxy

-- |
-- This function assumes @mn ~ alg@, making its type different from previous (before v.0.5) versions.
--
-- @since 0.5.0.0
runDecoding :: forall nm f xs conf str . Decoding f nm nm conf str -> Enc (nm ': xs) conf str -> f (Enc xs conf str)
runDecoding :: Decoding f nm nm conf str
-> Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
runDecoding (UnsafeMkDecoding Proxy @Symbol nm
_ forall (xs :: [Symbol]).
Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
fn) = Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
forall (xs :: [Symbol]).
Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
fn

-- |
-- @since 0.3.0.0
runDecoding' :: forall alg nm f xs conf str . Decoding f nm alg conf str -> Enc (nm ': xs) conf str -> f (Enc xs conf str)
runDecoding' :: Decoding f nm alg conf str
-> Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
runDecoding' (UnsafeMkDecoding Proxy @Symbol nm
_ forall (xs :: [Symbol]).
Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
fn) = Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
forall (xs :: [Symbol]).
Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
fn

-- | Same as 'runDecoding" but compiler figures out algorithm name
--
-- Using it can slowdown compilation
--
-- @since 0.3.0.0  
_runDecoding :: forall nm f xs conf str alg . (AlgNm nm ~ alg) => Decoding f nm alg conf str -> Enc (nm ': xs) conf str -> f (Enc xs conf str)
_runDecoding :: Decoding f nm alg conf str
-> Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
_runDecoding = forall (alg :: Symbol) (nm :: Symbol) (f :: * -> *)
       (xs :: [Symbol]) conf str.
Decoding f nm alg conf str
-> Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
forall (nm :: Symbol) (f :: * -> *) (xs :: [Symbol]) conf str.
Decoding f nm (AlgNm nm) conf str
-> Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
runDecoding' @(AlgNm nm)

-- |
-- Wraps a list of @Decoding@ elements.
--
-- Similarly to 'Data.TypedEncoding.Common.Types.Enc.Encodings' can be used with a typeclass
-- 'Data.TypedDecoding.Internal.Class.Decode.DecodeAll'
--
-- @since 0.3.0.0  
data Decodings f (nms :: [Symbol]) (algs :: [Symbol]) conf str where
    -- | constructor is to be treated as Unsafe to Encode and Decode instance implementations
    -- particular encoding instances may expose smart constructors for limited data types
    ZeroD :: Decodings f '[] '[] conf str
    ConsD ::  Decoding f nm alg conf str -> Decodings f nms algs conf str -> Decodings f (nm ': nms) (alg ': algs) conf str

-- |
-- This function assumes @nms ~ algs@, making its type different from previous (before v.0.5) versions.
--
-- @since 0.5.0.0
runDecodings :: forall nms f c str . (Monad f) => Decodings f nms nms c str -> Enc nms c str -> f (Enc ('[]::[Symbol]) c str)
runDecodings :: Decodings f nms nms c str
-> Enc @[Symbol] nms c str -> f (Enc @[Symbol] ('[] @Symbol) c str)
runDecodings = forall (algs :: [Symbol]) (nms :: [Symbol]) (f :: * -> *) c str.
Monad f =>
Decodings f nms algs c str
-> Enc @[Symbol] nms c str -> f (Enc @[Symbol] ('[] @Symbol) c str)
forall (f :: * -> *) c str.
Monad f =>
Decodings f nms nms c str
-> Enc @[Symbol] nms c str -> f (Enc @[Symbol] ('[] @Symbol) c str)
runDecodings' @nms @nms


runDecodings' :: forall algs nms f c str . (Monad f) => Decodings f nms algs c str -> Enc nms c str -> f (Enc ('[]::[Symbol]) c str)
runDecodings' :: Decodings f nms algs c str
-> Enc @[Symbol] nms c str -> f (Enc @[Symbol] ('[] @Symbol) c str)
runDecodings' Decodings f nms algs c str
ZeroD Enc @[Symbol] nms c str
enc0 = Enc @[Symbol] nms c str -> f (Enc @[Symbol] nms c str)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Enc @[Symbol] nms c str
enc0
runDecodings' (ConsD Decoding f nm alg c str
fn Decodings f nms algs c str
xs) Enc @[Symbol] nms c str
enc = 
        let f (Enc @[Symbol] nms c str)
re :: f (Enc _ c str) = Decoding f nm alg c str
-> Enc @[Symbol] ((':) @Symbol nm nms) c str
-> f (Enc @[Symbol] nms c str)
forall (alg :: Symbol) (nm :: Symbol) (f :: * -> *)
       (xs :: [Symbol]) conf str.
Decoding f nm alg conf str
-> Enc @[Symbol] ((':) @Symbol nm xs) conf str
-> f (Enc @[Symbol] xs conf str)
runDecoding' Decoding f nm alg c str
fn Enc @[Symbol] nms c str
Enc @[Symbol] ((':) @Symbol nm nms) c str
enc
        in f (Enc @[Symbol] nms c str)
re f (Enc @[Symbol] nms c str)
-> (Enc @[Symbol] nms c str
    -> f (Enc @[Symbol] ('[] @Symbol) c str))
-> f (Enc @[Symbol] ('[] @Symbol) c str)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Decodings f nms algs c str
-> Enc @[Symbol] nms c str -> f (Enc @[Symbol] ('[] @Symbol) c str)
forall (algs :: [Symbol]) (nms :: [Symbol]) (f :: * -> *) c str.
Monad f =>
Decodings f nms algs c str
-> Enc @[Symbol] nms c str -> f (Enc @[Symbol] ('[] @Symbol) c str)
runDecodings' Decodings f nms algs c str
xs

-- | At possibly big compilation cost, have compiler figure out algorithm names.
--
-- @since 0.3.0.0  
_runDecodings :: forall nms f c str algs . (Monad f, algs ~ AlgNmMap nms) => Decodings f nms algs c str -> Enc nms c str -> f (Enc ('[]::[Symbol]) c str)
_runDecodings :: Decodings f nms algs c str
-> Enc @[Symbol] nms c str -> f (Enc @[Symbol] ('[] @Symbol) c str)
_runDecodings = forall (algs :: [Symbol]) (nms :: [Symbol]) (f :: * -> *) c str.
Monad f =>
Decodings f nms algs c str
-> Enc @[Symbol] nms c str -> f (Enc @[Symbol] ('[] @Symbol) c str)
forall (nms :: [Symbol]) (f :: * -> *) c str.
Monad f =>
Decodings f nms (AlgNmMap nms) c str
-> Enc @[Symbol] nms c str -> f (Enc @[Symbol] ('[] @Symbol) c str)
runDecodings' @(AlgNmMap nms)