{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

-- | Examples about how to work with encoded data.
-- This topic is (an interesting) work-in-progress.
--
-- Modifying encoded data would typically corrupt the encoding. 
-- Current approach is to use 'Data.TypedEncoding.Unsafe.Unsafe' wrapping class that exposes
-- Functor and (limited) Applicative and Monad instances.

module Examples.TypedEncoding.Unsafe where

import qualified Data.Text as T
import           Data.Semigroup ((<>))

import           Data.TypedEncoding
import qualified Data.TypedEncoding.Unsafe as Unsafe
import qualified Data.TypedEncoding.Instances.Restriction.ASCII()


-- $setup
-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds

-- | Starting example
exAsciiTE :: Either EncodeEx (Enc '["r-ASCII"] () T.Text)
exAsciiTE :: Either
  EncodeEx
  (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
exAsciiTE = Enc @[Symbol] ('[] @Symbol) () Text
-> Either
     EncodeEx
     (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
forall (nms :: [Symbol]) (f :: * -> *) c str.
(Monad f, EncodeAll f nms nms c str) =>
Enc @[Symbol] ('[] @Symbol) c str -> f (Enc @[Symbol] nms c str)
encodeFAll (Enc @[Symbol] ('[] @Symbol) () Text
 -> Either
      EncodeEx
      (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text))
-> (Text -> Enc @[Symbol] ('[] @Symbol) () Text)
-> Text
-> Either
     EncodeEx
     (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. () -> Text -> Enc @[Symbol] ('[] @Symbol) () Text
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding () (Text
 -> Either
      EncodeEx
      (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text))
-> Text
-> Either
     EncodeEx
     (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
forall a b. (a -> b) -> a -> b
$ Text
"HELLO" 

-- | with either removed
exAsciiT :: Enc '["r-ASCII"] () T.Text
Right Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
exAsciiT = Either
  EncodeEx
  (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
exAsciiTE

-- * Safe and Slow approach

-- |
-- 'recreateFAll' is the way to recover encoding in a safe way
--
-- >>> let payload = getPayload exAsciiT
-- >>> let newPayload = payload <> " some extra stuff"
-- >>> recreateFAll . toEncoding () $ newPayload :: Either RecreateEx (Enc '["r-ASCII"] () T.Text)
-- Right (UnsafeMkEnc Proxy () "HELLO some extra stuff")
--
-- Alternatively, 'UncheckedEnc' type can be used in recreation, see 'Examples.TypedEncoding.Overview'
-- 
modifiedAsciiT :: Either RecreateEx (Enc '["r-ASCII"] () T.Text)
modifiedAsciiT :: Either
  RecreateEx
  (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
modifiedAsciiT =  Enc @[Symbol] ('[] @Symbol) () Text
-> Either
     RecreateEx
     (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
forall (nms :: [Symbol]) (f :: * -> *) c str.
(Monad f, ValidateAll f nms nms c str) =>
Enc @[Symbol] ('[] @Symbol) c str -> f (Enc @[Symbol] nms c str)
recreateFAll (Enc @[Symbol] ('[] @Symbol) () Text
 -> Either
      RecreateEx
      (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text))
-> (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
    -> Enc @[Symbol] ('[] @Symbol) () Text)
-> Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Either
     RecreateEx
     (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. () -> Text -> Enc @[Symbol] ('[] @Symbol) () Text
forall conf str.
conf -> str -> Enc @[Symbol] ('[] @Symbol) conf str
toEncoding () (Text -> Enc @[Symbol] ('[] @Symbol) () Text)
-> (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
    -> Text)
-> Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Enc @[Symbol] ('[] @Symbol) () Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ( Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" some extra stuff") (Text -> Text)
-> (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
    -> Text)
-> Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Text
forall k (enc :: k) conf str. Enc @k enc conf str -> str
getPayload (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
 -> Either
      RecreateEx
      (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text))
-> Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Either
     RecreateEx
     (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
forall a b. (a -> b) -> a -> b
$ Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
exAsciiT
  

-- * Unsafe but fast

-- |
-- The issue with 'recreateFAll' is that it may be expensive.
--
-- This apprach uses 'Data.TypedEncoding.Unsafe.Unsafe' to perform (in general risky) operation on
-- the internal payload.
--  
-- >>> exAsciiTE
-- Right (UnsafeMkEnc Proxy () "HELLO")
-- >>> exAsciiTE >>= pure . Unsafe.withUnsafe (fmap T.toLower)
-- Right (UnsafeMkEnc Proxy () "hello")
--
-- Example uses of 'T.toLower' within encoded data
-- this operation is safe for ASCII restriction
-- but @Enc '["r-ASCII"] () T.Text@ does not expose it
-- We use Functor instance of Unsafe wrapper type to accomplish this
toLowerAscii :: Either EncodeEx (Enc '["r-ASCII"] () T.Text)
toLowerAscii :: Either
  EncodeEx
  (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
toLowerAscii = (Unsafe @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
 -> Unsafe @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
-> Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
forall k (e :: k) c s1 s2.
(Unsafe @k e c s1 -> Unsafe @k e c s2)
-> Enc @k e c s1 -> Enc @k e c s2
Unsafe.withUnsafe ((Text -> Text)
-> Unsafe @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Unsafe @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> Text
T.toLower) (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
 -> Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
-> Either
     EncodeEx
     (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
-> Either
     EncodeEx
     (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either
  EncodeEx
  (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
exAsciiTE

-- | 
-- Similar example uses applicative instance of 'Unsafe.Unsafe'
--
-- >>> let Right hELLO = exAsciiTE
-- >>> let Right hello = toLowerAscii
-- >>> displ $ Unsafe.runUnsafe ((<>) <$> Unsafe.Unsafe hELLO <*> Unsafe.Unsafe hello)
-- "Enc '[r-ASCII] () (Text HELLOhello)"
appendAscii :: Either EncodeEx (Enc '["r-ASCII"] () T.Text)
appendAscii :: Either
  EncodeEx
  (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
appendAscii = do 
    Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
hELLO <- Either
  EncodeEx
  (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
exAsciiTE
    Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
hello <- Either
  EncodeEx
  (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
toLowerAscii
    Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Either
     EncodeEx
     (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
 -> Either
      EncodeEx
      (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text))
-> Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Either
     EncodeEx
     (Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text)
forall a b. (a -> b) -> a -> b
$ Unsafe @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
forall k (enc :: k) conf str.
Unsafe @k enc conf str -> Enc @k enc conf str
Unsafe.runUnsafe (Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
(<>) (Text -> Text -> Text)
-> Unsafe @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Unsafe
     @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () (Text -> Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Unsafe @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
forall k (enc :: k) conf str.
Enc @k enc conf str -> Unsafe @k enc conf str
Unsafe.Unsafe Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
hELLO Unsafe
  @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () (Text -> Text)
-> Unsafe @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Unsafe @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
-> Unsafe @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
forall k (enc :: k) conf str.
Enc @k enc conf str -> Unsafe @k enc conf str
Unsafe.Unsafe Enc @[Symbol] ((':) @Symbol "r-ASCII" ('[] @Symbol)) () Text
hello)