-- |
  Module       : Unicode .  . Identifiers . Security
  Copyright    : ( c ) 2021 Composewell Technologies and Contributors
-- License     : Apache-2.0
-- Maintainer  : 
-- Stability   : experimental
--
-- Unicode Security Mechanisms functions based on
  [ Unicode Technical Standard # 39]( / reports / tr39/ ) .
--
  @since 0.1.0

module Unicode.Char.Identifiers.Security
    ( -- * Identifier status
      isAllowedInIdentifier

      -- * Identifier type
    , T.IdentifierType(..)
    , identifierTypes
    , isIdentifierTypeAllowed

      -- * Confusables
    , confusablePrototype
    -- , prototype
    , intentionalConfusables
    , isIntentionallyConfusable
    )
where

import           Data.List.NonEmpty (NonEmpty)
import           Data.Maybe (isJust)
import qualified GHC.Foreign as Foreign
import qualified GHC.IO.Encoding as Encoding
import           System.IO.Unsafe (unsafePerformIO)

import qualified Unicode.Internal.Char.Security.Confusables as C
import qualified Unicode.Internal.Char.Security.IdentifierStatus as S
import qualified Unicode.Internal.Char.Security.IdentifierType as T
import qualified Unicode.Internal.Char.Security.IntentionalConfusables as IC

-- | Returns 'True' if the given character is allowed in an identifier.
--
-- * /Restricted/ characters should be treated with caution when considering
-- possible use in identifiers, and should be disallowed unless there is
-- good reason to allow them in the environment in question.
-- * /Allowed/ characters are not typically used as is by implementations.
-- Instead, they are applied as filters to the set of supported characters.
--
  @since 0.1.0
# INLINE isAllowedInIdentifier #
isAllowedInIdentifier :: Char -> Bool
isAllowedInIdentifier = S.isAllowedInIdentifier

  | Return ' True ' if the given ' T.IdentifierType ' is allowed .
--
  @since 0.1.0
{-# INLINE isIdentifierTypeAllowed #-}
isIdentifierTypeAllowed :: T.IdentifierType -> Bool
isIdentifierTypeAllowed = \case
    T.Inclusion   -> True
    T.Recommended -> True
    _             -> False

  | Returns the ' IdentifierType 's corresponding to a character .
--
  @since 0.1.0
# INLINE identifierTypes #
identifierTypes :: Char -> NonEmpty T.IdentifierType
identifierTypes = T.decodeIdentifierTypes . T.identifierTypes

  | Returns the /prototype/ of a character if it is 
-- confusable, else 'Nothing'.
--
  @since 0.1.0
# INLINE confusablePrototype #
confusablePrototype :: Char -> Maybe String
confusablePrototype = fmap decode . C.confusablePrototype
    where
    decode = unsafePerformIO . Foreign.peekCString Encoding.utf8

-- [TODO] Assess the need for this function
-- -- | Returns the /prototype/ of a character.
-- --
  -- Note : returns the character itself if it is not  confusable .
-- --
  -- @since 0.1.0
-- {-# INLINE prototype #-}
  prototype : :    String
  prototype c = fromMaybe [ c ] ( confusablePrototype c )

-- | Returns the list of /intentional/ confusables of a character, if any.
--
  @since 0.1.0
# INLINE intentionalConfusables #
intentionalConfusables :: Char -> String
intentionalConfusables = maybe mempty decode . IC.intentionalConfusables
    where
    decode = unsafePerformIO . Foreign.peekCString Encoding.utf8

-- | Returns 'True' if the character is /intentionally/ confusable.
--
  @since 0.1.0
# INLINE isIntentionallyConfusable #
isIntentionallyConfusable :: Char -> Bool
isIntentionallyConfusable = isJust . IC.intentionalConfusables

 |
 License     : Apache-2.0
 Maintainer  : 
 Stability   : experimental

 Unicode Security Mechanisms functions based on

 * Identifier status
 * Identifier type
 * Confusables
 , prototype
 | Returns 'True' if the given character is allowed in an identifier.

 * /Restricted/ characters should be treated with caution when considering
 possible use in identifiers, and should be disallowed unless there is
 good reason to allow them in the environment in question.
 * /Allowed/ characters are not typically used as is by implementations.
 Instead, they are applied as filters to the set of supported characters.


# INLINE isIdentifierTypeAllowed #

 confusable, else 'Nothing'.

 [TODO] Assess the need for this function
 -- | Returns the /prototype/ of a character.
 --
 Note : returns the character itself if it is not  confusable .
 --
 @since 0.1.0
 {-# INLINE prototype #-}
 | Returns the list of /intentional/ confusables of a character, if any.

 | Returns 'True' if the character is /intentionally/ confusable.

  Module       : Unicode .  . Identifiers . Security
  Copyright    : ( c ) 2021 Composewell Technologies and Contributors
  [ Unicode Technical Standard # 39]( / reports / tr39/ ) .
  @since 0.1.0

module Unicode.Char.Identifiers.Security
      isAllowedInIdentifier

    , T.IdentifierType(..)
    , identifierTypes
    , isIdentifierTypeAllowed

    , confusablePrototype
    , intentionalConfusables
    , isIntentionallyConfusable
    )
where

import           Data.List.NonEmpty (NonEmpty)
import           Data.Maybe (isJust)
import qualified GHC.Foreign as Foreign
import qualified GHC.IO.Encoding as Encoding
import           System.IO.Unsafe (unsafePerformIO)

import qualified Unicode.Internal.Char.Security.Confusables as C
import qualified Unicode.Internal.Char.Security.IdentifierStatus as S
import qualified Unicode.Internal.Char.Security.IdentifierType as T
import qualified Unicode.Internal.Char.Security.IntentionalConfusables as IC

  @since 0.1.0
# INLINE isAllowedInIdentifier #
isAllowedInIdentifier :: Char -> Bool
isAllowedInIdentifier = S.isAllowedInIdentifier

  | Return ' True ' if the given ' T.IdentifierType ' is allowed .
  @since 0.1.0
isIdentifierTypeAllowed :: T.IdentifierType -> Bool
isIdentifierTypeAllowed = \case
    T.Inclusion   -> True
    T.Recommended -> True
    _             -> False

  | Returns the ' IdentifierType 's corresponding to a character .
  @since 0.1.0
# INLINE identifierTypes #
identifierTypes :: Char -> NonEmpty T.IdentifierType
identifierTypes = T.decodeIdentifierTypes . T.identifierTypes

  | Returns the /prototype/ of a character if it is 
  @since 0.1.0
# INLINE confusablePrototype #
confusablePrototype :: Char -> Maybe String
confusablePrototype = fmap decode . C.confusablePrototype
    where
    decode = unsafePerformIO . Foreign.peekCString Encoding.utf8

  prototype : :    String
  prototype c = fromMaybe [ c ] ( confusablePrototype c )

  @since 0.1.0
# INLINE intentionalConfusables #
intentionalConfusables :: Char -> String
intentionalConfusables = maybe mempty decode . IC.intentionalConfusables
    where
    decode = unsafePerformIO . Foreign.peekCString Encoding.utf8

  @since 0.1.0
# INLINE isIntentionallyConfusable #
isIntentionallyConfusable :: Char -> Bool
isIntentionallyConfusable = isJust . IC.intentionalConfusables
