    {-# LANGUAGE CPP               #-}
    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE TypeApplications  #-}
    {-|
    Module      : HsLua.Module.Text
    Copyright   : © 2017–2023 Albert Krewinkel
    License     : MIT
    Maintainer  : Albert Krewinkel <tarleb@hslua.org>
    Stability   : alpha
    Portability : GHC only

    Provides a Lua module containing a selection of useful Text functions.
    -}
    module HsLua.Module.Text
      ( -- * Module
        documentedModule
        -- ** Functions
      , fromencoding
      , len
      , lower
      , reverse
      , sub
      , toencoding
      , upper
      ) where

    import Prelude hiding (reverse)
    import Data.Text (Text)
    import Data.Maybe (fromMaybe)
    import Foreign.Marshal.Alloc (alloca)
    import HsLua.Core (LuaError)
    import HsLua.Packaging
    import Lua (lua_pushlstring, lua_tolstring)
    import System.IO.Error (tryIOError)
    import qualified Data.Text as T
    import qualified Foreign.Storable as F
    import qualified GHC.Foreign as GHC
    import qualified GHC.IO.Encoding as GHC
    import qualified HsLua.Core as Lua
    import qualified HsLua.Marshalling as Lua

    -- | The @text@ module.
    documentedModule :: LuaError e => Module e
    documentedModule :: forall e. LuaError e => Module e
    documentedModule = Module
      { moduleName :: Name
    moduleName = Name
    "text"
      , moduleOperations :: [(Operation, DocumentedFunction e)]
    moduleOperations = []
      , moduleFields :: [Field e]
    moduleFields = []
      , moduleFunctions :: [DocumentedFunction e]
    moduleFunctions =
        [ DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    fromencoding
        , DocumentedFunction e
    forall e. DocumentedFunction e
    len
        , DocumentedFunction e
    forall e. DocumentedFunction e
    lower
        , DocumentedFunction e
    forall e. DocumentedFunction e
    reverse
        , DocumentedFunction e
    forall e. DocumentedFunction e
    sub
        , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    toencoding
        , DocumentedFunction e
    forall e. DocumentedFunction e
    upper
        ]
      , moduleDescription :: Text
    moduleDescription =
          Text
    "UTF-8 aware text manipulation functions, implemented in Haskell."
      , moduleTypeInitializers :: [LuaE e Name]
    moduleTypeInitializers = []
      }

    --
    -- Functions
    --

    -- | Recodes a string as UTF-8.
    fromencoding :: LuaError e => DocumentedFunction e
    fromencoding :: forall e. LuaError e => DocumentedFunction e
    fromencoding = Name
    -> (StackIndex -> Maybe String -> LuaE e Text)
    -> HsFnPrecursor e (StackIndex -> Maybe String -> LuaE e Text)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "fromencoding"
      ### (\strIdx menc -> do
              l <- Lua.state
              result <- Lua.liftIO . tryIOError $ do
                encoding <- maybe getFileSystemEncoding GHC.mkTextEncoding menc
                alloca $ \lenPtr -> do
                  cstr <- lua_tolstring l strIdx lenPtr
                  -- cstr cannot be NULL, or stringIndex would have failed.
                  cstrLen <- F.peek lenPtr
                  GHC.peekCStringLen encoding (cstr, fromIntegral cstrLen)
              case result of
                Right s -> pure $ T.pack s
                Left err -> Lua.failLua (show err))
      HsFnPrecursor e (StackIndex -> Maybe String -> LuaE e Text)
    -> Parameter e StackIndex
    -> HsFnPrecursor e (Maybe String -> LuaE e Text)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e StackIndex
    -> TypeSpec -> Text -> Text -> Parameter e StackIndex
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e StackIndex
    forall {e}. StackIndex -> Peek e StackIndex
    stringIndex TypeSpec
    "string" Text
    "s" Text
    "string to be converted"
      HsFnPrecursor e (Maybe String -> LuaE e Text)
    -> Parameter e (Maybe String) -> HsFnPrecursor e (LuaE e Text)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Parameter e String -> Parameter e (Maybe String)
    forall e a. Parameter e a -> Parameter e (Maybe a)
    opt (Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    stringParam Text
    "encoding" Text
    "target encoding")
      HsFnPrecursor e (LuaE e Text)
    -> FunctionResults e Text -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Pusher e Text -> TypeSpec -> Text -> FunctionResults e Text
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult Pusher e Text
    forall e. Pusher e Text
    Lua.pushText TypeSpec
    "string" Text
    "UTF-8 string"
      #? T.unlines
         [ "Converts a string to UTF-8. The `encoding` parameter specifies the"
         , "encoding of the input string. On Windows, that parameter defaults"
         , "to the current ANSI code page; on other platforms the function"
         , "will try to use the file system's encoding."
         , ""
         , "The set of known encodings is system dependent, but includes at"
         , "least `UTF-8`, `UTF-16BE`, `UTF-16LE`, `UTF-32BE`, and `UTF-32LE`."
         , "Note that the default code page on Windows is available through"
         , "`CP0`."
         ]
      where
        stringIndex :: StackIndex -> Peek e StackIndex
    stringIndex StackIndex
    idx = do
          isstr <- LuaE e Bool -> Peek e Bool
    forall e a. LuaE e a -> Peek e a
    Lua.liftLua (StackIndex -> LuaE e Bool
    forall e. StackIndex -> LuaE e Bool
    Lua.isstring StackIndex
    idx)
          if isstr
            then pure idx
            else Lua.typeMismatchMessage "string" idx >>= Lua.failPeek

    -- | Wrapper for @'T.length'@.
    len :: DocumentedFunction e
    len :: forall e. DocumentedFunction e
    len = Name
    -> (Text -> LuaE e Int) -> HsFnPrecursor e (Text -> LuaE e Int)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "len"
      ### liftPure T.length
      HsFnPrecursor e (Text -> LuaE e Int)
    -> Parameter e Text -> HsFnPrecursor e (LuaE e Int)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e Text
    forall e. Text -> Text -> Parameter e Text
    textParam Text
    "s" Text
    "UTF-8 encoded string"
      HsFnPrecursor e (LuaE e Int)
    -> FunctionResults e Int -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Text -> FunctionResults e Int
    forall a e. (Integral a, Show a) => Text -> FunctionResults e a
    integralResult Text
    "length"
      #? "Returns the length of a UTF-8 string, i.e., the number of characters."

    -- | Wrapper for @'T.toLower'@.
    lower :: DocumentedFunction e
    lower :: forall e. DocumentedFunction e
    lower = Name
    -> (Text -> LuaE e Text) -> HsFnPrecursor e (Text -> LuaE e Text)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "lower"
      ### liftPure T.toLower
      HsFnPrecursor e (Text -> LuaE e Text)
    -> Parameter e Text -> HsFnPrecursor e (LuaE e Text)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e Text
    forall e. Text -> Text -> Parameter e Text
    textParam Text
    "s" Text
    "UTF-8 string to convert to lowercase"
      HsFnPrecursor e (LuaE e Text)
    -> FunctionResults e Text -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Text -> FunctionResults e Text
    forall e. Text -> FunctionResults e Text
    textResult Text
    "Lowercase copy of `s`"
      #? "Returns a copy of a UTF-8 string, converted to lowercase."

    -- | Wrapper for @'T.reverse'@.
    reverse :: DocumentedFunction e
    reverse :: forall e. DocumentedFunction e
    reverse = Name
    -> (Text -> LuaE e Text) -> HsFnPrecursor e (Text -> LuaE e Text)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "reverse"
      ### liftPure T.reverse
      HsFnPrecursor e (Text -> LuaE e Text)
    -> Parameter e Text -> HsFnPrecursor e (LuaE e Text)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e Text
    forall e. Text -> Text -> Parameter e Text
    textParam Text
    "s" Text
    "UTF-8 string to revert"
      HsFnPrecursor e (LuaE e Text)
    -> FunctionResults e Text -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Text -> FunctionResults e Text
    forall e. Text -> FunctionResults e Text
    textResult Text
    "Reversed `s`"
      #? "Returns a copy of a UTF-8 string, with characters reversed."

    -- | Returns a substring, using Lua's string indexing rules.
    sub :: DocumentedFunction e
    sub :: forall e. DocumentedFunction e
    sub = Name
    -> (Text -> Int -> Maybe Int -> LuaE e Text)
    -> HsFnPrecursor e (Text -> Int -> Maybe Int -> LuaE e Text)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "sub"
      ### liftPure3 substring
      HsFnPrecursor e (Text -> Int -> Maybe Int -> LuaE e Text)
    -> Parameter e Text
    -> HsFnPrecursor e (Int -> Maybe Int -> LuaE e Text)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e Text
    forall e. Text -> Text -> Parameter e Text
    textParam Text
    "s" Text
    "UTF-8 string"
      HsFnPrecursor e (Int -> Maybe Int -> LuaE e Text)
    -> Parameter e Int -> HsFnPrecursor e (Maybe Int -> LuaE e Text)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e Int
    forall e. Text -> Text -> Parameter e Int
    textIndex Text
    "i" Text
    "substring start position"
      HsFnPrecursor e (Maybe Int -> LuaE e Text)
    -> Parameter e (Maybe Int) -> HsFnPrecursor e (LuaE e Text)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Parameter e Int -> Parameter e (Maybe Int)
    forall e a. Parameter e a -> Parameter e (Maybe a)
    opt (Text -> Text -> Parameter e Int
    forall e. Text -> Text -> Parameter e Int
    textIndex Text
    "j" Text
    "substring end position")
      HsFnPrecursor e (LuaE e Text)
    -> FunctionResults e Text -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Text -> FunctionResults e Text
    forall e. Text -> FunctionResults e Text
    textResult Text
    "text substring"
      #? T.unlines
         [ "Returns a substring of a UTF-8 string, using Lua's string"
         , "indexing rules."
         ]
      where
        substring :: Text -> Int -> Maybe Int -> Text
        substring :: Text -> Int -> Maybe Int -> Text
    substring Text
    s Int
    i Maybe Int
    jopt =
          let j :: Int
    j = Int -> Maybe Int -> Int
    forall a. a -> Maybe a -> a
    fromMaybe (-Int
    1) Maybe Int
    jopt
              fromStart :: Int
    fromStart = if Int
    i Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    0 then  Int
    i Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1 else Text -> Int
    T.length Text
    s Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    i
              fromEnd :: Int
    fromEnd   = if Int
    j Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <  Int
    0 then -Int
    j Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1 else Text -> Int
    T.length Text
    s Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    j
          in Int -> Text -> Text
    T.dropEnd Int
    fromEnd (Text -> Text) -> (Text -> Text) -> Text -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Int -> Text -> Text
    T.drop Int
    fromStart (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ Text
    s

    -- | Converts a UTF-8 string to a different encoding.
    toencoding :: LuaError e => DocumentedFunction e
    toencoding :: forall e. LuaError e => DocumentedFunction e
    toencoding = Name
    -> (Text -> Maybe String -> LuaE e ())
    -> HsFnPrecursor e (Text -> Maybe String -> LuaE e ())
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "toencoding"
      ### (\s menc -> do
              l <- Lua.state
              result <- Lua.liftIO . tryIOError $ do
                encoding <- maybe getFileSystemEncoding GHC.mkTextEncoding menc
                GHC.withCStringLen encoding (T.unpack s) $ \(sPtr, sLen) ->
                  lua_pushlstring l sPtr (fromIntegral sLen)
              case result of
                Right () -> pure ()
                Left err -> Lua.failLua (show err))
      HsFnPrecursor e (Text -> Maybe String -> LuaE e ())
    -> Parameter e Text -> HsFnPrecursor e (Maybe String -> LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e Text
    forall e. Text -> Text -> Parameter e Text
    textParam Text
    "s" Text
    "UTF-8 string"
      HsFnPrecursor e (Maybe String -> LuaE e ())
    -> Parameter e (Maybe String) -> HsFnPrecursor e (LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Parameter e String -> Parameter e (Maybe String)
    forall e a. Parameter e a -> Parameter e (Maybe a)
    opt (Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    stringParam Text
    "enc" Text
    "target encoding")
      HsFnPrecursor e (LuaE e ())
    -> FunctionResults e () -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> (() -> LuaE e ()) -> TypeSpec -> Text -> FunctionResults e ()
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult (LuaE e () -> () -> LuaE e ()
    forall a b. a -> b -> a
    const (() -> LuaE e ()
    forall a. a -> LuaE e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure ())) TypeSpec
    "string" Text
    "re-encoded string"
      #? T.unlines
         [ "Converts a UTF-8 string to a different encoding. The `encoding`"
         , "parameter defaults to the current ANSI code page on Windows; on"
         , "other platforms it will try to guess the file system's encoding."
         , ""
         , "The set of known encodings is system dependent, but includes at"
         , "least `UTF-8`, `UTF-16BE`, `UTF-16LE`, `UTF-32BE`, and `UTF-32LE`."
         , "Note that the default code page on Windows is available through"
         , "`CP0`."
         ]

    -- | Wrapper for @'T.toUpper'@.
    upper :: DocumentedFunction e
    upper :: forall e. DocumentedFunction e
    upper = Name
    -> (Text -> LuaE e Text) -> HsFnPrecursor e (Text -> LuaE e Text)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "upper"
      ### liftPure T.toUpper
      HsFnPrecursor e (Text -> LuaE e Text)
    -> Parameter e Text -> HsFnPrecursor e (LuaE e Text)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e Text
    forall e. Text -> Text -> Parameter e Text
    textParam Text
    "s" Text
    "UTF-8 string to convert to uppercase"
      HsFnPrecursor e (LuaE e Text)
    -> FunctionResults e Text -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Text -> FunctionResults e Text
    forall e. Text -> FunctionResults e Text
    textResult Text
    "Uppercase copy of `s`"
      #? "Returns a copy of a UTF-8 string, converted to uppercase."

    --
    -- Parameters
    --

    -- | String index parameter
    textIndex :: Text -- ^ parameter name
              -> Text -- ^ parameter description
              -> Parameter e Int
    textIndex :: forall e. Text -> Text -> Parameter e Int
    textIndex = forall a e. (Read a, Integral a) => Text -> Text -> Parameter e a
    integralParam @Int

    --
    -- Helpers
    --
    getFileSystemEncoding :: IO GHC.TextEncoding
    getFileSystemEncoding :: IO TextEncoding
    getFileSystemEncoding =
    #if defined(mingw32_HOST_OS)
      GHC.mkTextEncoding "CP0"  -- a.k.a CP_ACP
    #else
      IO TextEncoding
    GHC.getFileSystemEncoding
    #endif