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

-- | 
-- Checks that satisfy D76 Unicode standard (/text/ replaces chars that are in range
-- U+D800 to U+DFFF inclusive)
--
-- Note, no IsSuperset "r-UNICODE.D76" "r-CHAR8" mapping even though the numeric range of D76 includes all CHAR8 bytes.
-- This is more /nominal/ decision that prevents certain unwanted conversions from being possible.
--
-- Similarly no IsSuperset "r-UNICODE.D76" "r-ByteRep", this annotation acts as a guard to what can go into @Text@.
-- 
-- @since 0.4.0.0
module Data.TypedEncoding.Instances.Restriction.D76 where

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

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


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


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

newtype NonTextChar = NonTextChar Char deriving (Eq, Show)

-- * Encoding @"r-UNICODE.D76"@

instance Encode (Either EncodeEx) "r-UNICODE.D76" "r-UNICODE.D76" c Char where
    encoding = encD76Char

instance Encode (Either EncodeEx) "r-UNICODE.D76" "r-UNICODE.D76" c String where
    encoding = encD76

encD76Char :: Encoding (Either EncodeEx) "r-UNICODE.D76" "r-UNICODE.D76" c Char
encD76Char = _implEncodingEx (\c -> explainBool NonTextChar (c, nonTextChar c))

encD76 :: Encoding (Either EncodeEx) "r-UNICODE.D76" "r-UNICODE.D76" c String
encD76 = _implEncodingEx @"r-UNICODE.D76" encImpl

-- | No-check version
trustMe :: Applicative f =>  Encoding f "r-UNICODE.D76" "r-UNICODE.D76" c String
trustMe = _implEncodingP id



-- * Decoding @"r-UNICODE.D76"@

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

instance (RecreateErr f, Applicative f) => Validate f "r-UNICODE.D76" "r-UNICODE.D76" () String where
    validation = validR encD76


-- * Implementation 

-- @'\xd800'@ to  @'\xdfff'@ inclusive
nonTextChar :: Char -> Bool
nonTextChar c =  x >= 55296 && x <= 57343
   where x = ord c


-- \ UNICODE.D76
encImpl :: String -> Either NonTextChar String
encImpl str = case find nonTextChar str of
    Nothing -> Right str
    Just ch -> Left $ NonTextChar ch