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

-- | 
-- @"r-ByteRep"@ represents Characters used for Byte representations.
--
-- It is common to use @Char@ instead of @Word8@ when low level programming on @ByteString@.
--
-- This annotation represents such use of string characters.
-- 
-- Checks if all chars are @< \'\256\'@
--
-- Currently, this should be not used as superset 'Data.TypedEncoding.Common.Class.Superset.IsSuperset'
--
-- It is subset of "r-CHAR8":
--
-- @Superset "r-CHAR8" ""r-ByteRep"    
--
-- Currently, is (intentionally not decodable)
--
-- @since 0.3.1.0
module Data.TypedEncoding.Instances.Restriction.ByteRep where

import           Data.TypedEncoding.Instances.Support
import           Data.TypedEncoding.Common.Class.Util.StringConstraints

import           Data.TypedEncoding.Internal.Util (explainBool)
import           Data.Char

import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL

-- $setup
-- >>> :set -XDataKinds -XTypeApplications


-----------------
-- Encodings  --
-----------------

data CharOutOfRange = CharOutOfRange Int Char deriving (Eq, Show)

-- * Encoding @"r-ByteRep"@

instance Encode (Either EncodeEx) "r-ByteRep" "r-ByteRep" c Char where
    encoding = encByteChar

instance Encode (Either EncodeEx) "r-ByteRep" "r-ByteRep" c B.ByteString where
    encoding = encByteRepB

instance Encode (Either EncodeEx) "r-ByteRep" "r-ByteRep" c BL.ByteString where
    encoding = encByteRepBL

instance Encode (Either EncodeEx) "r-ByteRep" "r-ByteRep" c String where
    encoding = encByteRepS

encByteChar :: Encoding (Either EncodeEx) "r-ByteRep" "r-ByteRep" c Char
encByteChar = _implEncodingEx (\c -> explainBool (CharOutOfRange 255) (c, (> 255) . ord $ c))

encByteRepB :: Encoding (Either EncodeEx) "r-ByteRep" "r-ByteRep" c B.ByteString
encByteRepB = _implEncodingEx @"r-ByteRep" (encImpl 255)

encByteRepBL :: Encoding (Either EncodeEx) "r-ByteRep" "r-ByteRep" c BL.ByteString
encByteRepBL = _implEncodingEx @"r-ByteRep" (encImpl 255)

encByteRepS :: Encoding (Either EncodeEx) "r-ByteRep" "r-ByteRep" c String
encByteRepS = _implEncodingEx @"r-ByteRep" (encImpl 255)


-- * Decoding @"r-ByteRep"@

-- instance (Applicative f) => Decode f "r-ByteRep" "r-ByteRep" c str where
--     decoding = decAnyR


instance (RecreateErr f, Applicative f) => Validate f "r-ByteRep" "r-ByteRep" () B.ByteString where
    validation = validR encByteRepB

instance (RecreateErr f, Applicative f) => Validate f "r-ByteRep" "r-ByteRep" () BL.ByteString where
    validation = validR encByteRepBL

instance (RecreateErr f, Applicative f) => Validate f "r-ByteRep" "r-ByteRep" () String where
    validation = validR encByteRepS


-- * Implementation 

-- 255 for CHAR8 / "r-ByteRep"
encImpl :: Char8Find str => Int -> str -> Either CharOutOfRange str
encImpl bound str = case find ((> bound) . ord) str of
    Nothing -> Right str
    Just ch -> Left $ CharOutOfRange bound ch