# LANGUAGE CPP #
# LANGUAGE GeneralizedNewtypeDeriving #
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE RankNTypes #-}
# LANGUAGE TemplateHaskell #
# LANGUAGE DeriveGeneric #
# LANGUAGE TypeFamilies #
# LANGUAGE TypeApplications #
# LANGUAGE PatternSynonyms #

module System.OsString.Internal.Types
  (
    WindowsString(..)
  , pattern WS
  , unWS
  , PosixString(..)
  , unPS
  , pattern PS
  , PlatformString
  , WindowsChar(..)
  , unWW
  , pattern WW
  , PosixChar(..)
  , unPW
  , pattern PW
  , PlatformChar
  , OsString(..)
  , OsChar(..)
  )
where


import Control.DeepSeq
import Data.Data
import Data.Word
import Language.Haskell.TH.Syntax
    ( Lift (..), lift )
#if !MIN_VERSION_base(4,11,0)
import Data.Semigroup
#endif
import GHC.Generics (Generic)

import System.OsPath.Encoding.Internal
import qualified System.OsPath.Data.ByteString.Short as BS
import qualified System.OsPath.Data.ByteString.Short.Word16 as BS16
#if MIN_VERSION_template_haskell(2,16,0)
import qualified Language.Haskell.TH.Syntax as TH
#endif

  Using unpinned bytearrays to avoid Heap fragmentation and
  which are reasonably cheap to pass to FFI calls
  wrapped with typeclass - friendly types allowing to avoid CPP
--
  Note that , while unpinned bytearrays incur a memcpy on each
  FFI call , this overhead is generally much preferable to
  the memory fragmentation of pinned bytearrays

-- | Commonly used windows string as wide character bytes.
newtype WindowsString = WindowsString { getWindowsString :: BS.ShortByteString }
  deriving (Eq, Ord, Semigroup, Monoid, Typeable, Generic, NFData)

  | Decodes as  .
instance Show WindowsString where
  -- cWcharsToChars_UCS2 is total
  show = show . cWcharsToChars_UCS2 . BS16.unpack . getWindowsString

  | Just a short bidirectional synonym for '  ' constructor .
pattern WS :: BS.ShortByteString -> WindowsString
pattern WS { unWS } <- WindowsString unWS where
  WS a = WindowsString a
#if __GLASGOW_HASKELL__ >= 802
# COMPLETE WS #
#endif


instance Lift WindowsString where
  lift (WindowsString bs)
    = [| WindowsString (BS.pack $(lift $ BS.unpack bs)) :: WindowsString |]
#if MIN_VERSION_template_haskell(2,17,0)
  liftTyped = TH.unsafeCodeCoerce . TH.lift
#elif MIN_VERSION_template_haskell(2,16,0)
  liftTyped = TH.unsafeTExpCoerce . TH.lift
#endif

  | Commonly used  string as uninterpreted @char[]@
-- array.
newtype PosixString = PosixString { getPosixString :: BS.ShortByteString }
  deriving (Eq, Ord, Semigroup, Monoid, Typeable, Generic, NFData)

-- | Prints the raw bytes without decoding.
instance Show PosixString where
  show (PosixString ps) = show ps

-- | Just a short bidirectional synonym for 'PosixString' constructor.
pattern PS :: BS.ShortByteString -> PosixString
pattern PS { unPS } <- PosixString unPS where
  PS a = PosixString a
#if __GLASGOW_HASKELL__ >= 802
{-# COMPLETE PS #-}
#endif

instance Lift PosixString where
  lift (PosixString bs)
    = [| PosixString (BS.pack $(lift $ BS.unpack bs)) :: PosixString |]
#if MIN_VERSION_template_haskell(2,17,0)
  liftTyped = TH.unsafeCodeCoerce . TH.lift
#elif MIN_VERSION_template_haskell(2,16,0)
  liftTyped = TH.unsafeTExpCoerce . TH.lift
#endif


#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
type PlatformString = WindowsString
#else
type PlatformString = PosixString
#endif

newtype WindowsChar = WindowsChar { getWindowsChar :: Word16 }
  deriving (Eq, Ord, Typeable, Generic, NFData)

instance Show WindowsChar where
  show (WindowsChar wc) = show wc

newtype PosixChar   = PosixChar { getPosixChar :: Word8 }
  deriving (Eq, Ord, Typeable, Generic, NFData)

instance Show PosixChar where
  show (PosixChar pc) = show pc

  | Just a short bidirectional synonym for ' WindowsChar ' constructor .
pattern WW :: Word16 -> WindowsChar
pattern WW { unWW } <- WindowsChar unWW where
  WW a = WindowsChar a
#if __GLASGOW_HASKELL__ >= 802
{-# COMPLETE WW #-}
#endif

-- | Just a short bidirectional synonym for 'PosixChar' constructor.
pattern PW :: Word8 -> PosixChar
pattern PW { unPW } <- PosixChar unPW where
  PW a = PosixChar a
#if __GLASGOW_HASKELL__ >= 802
{-# COMPLETE PW #-}
#endif

#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
type PlatformChar = WindowsChar
#else
type PlatformChar = PosixChar
#endif


  | Newtype representing short operating system specific strings .
--
  Internally this is either '  ' or ' PosixString ' ,
-- depending on the platform. Both use unpinned
-- 'ShortByteString' for efficiency.
--
  The constructor is only exported via " System . OsString . Internal . Types " , since
-- dealing with the internals isn't generally recommended, but supported
-- in case you need to write platform specific code.
newtype OsString = OsString { getOsString :: PlatformString }
  deriving (Typeable, Generic, NFData)

  | On windows , decodes as UCS-2 . On unix prints the raw bytes without decoding .
instance Show OsString where
  show (OsString os) = show os

  | Byte equality of the internal representation .
instance Eq OsString where
  (OsString a) == (OsString b) = a == b

  | Byte ordering of the internal representation .
instance Ord OsString where
  compare (OsString a) (OsString b) = compare a b


  | \"String - Concatenation\ " for '  ' . This is _ _ not _ _ the same
-- as '(</>)'.
instance Monoid OsString where
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
    mempty      = OsString (WindowsString BS.empty)
#if MIN_VERSION_base(4,16,0)
    mappend = (<>)
#else
    mappend (OsString (WindowsString a)) (OsString (WindowsString b))
      = OsString (WindowsString (mappend a b))
#endif
#else
    mempty      = OsString (PosixString BS.empty)
#if MIN_VERSION_base(4,16,0)
    mappend = (<>)
#else
    mappend (OsString (PosixString a)) (OsString (PosixString b))
      = OsString (PosixString (mappend a b))
#endif
#endif
#if MIN_VERSION_base(4,11,0)
instance Semigroup OsString where
#if MIN_VERSION_base(4,16,0)
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
    (<>) (OsString (WindowsString a)) (OsString (WindowsString b))
      = OsString (WindowsString (mappend a b))
#else
    (<>) (OsString (PosixString a)) (OsString (PosixString b))
      = OsString (PosixString (mappend a b))
#endif
#else
    (<>) = mappend
#endif
#endif


instance Lift OsString where
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
  lift (OsString (WindowsString bs))
    = [| OsString (WindowsString (BS.pack $(lift $ BS.unpack bs))) :: OsString |]
#else
  lift (OsString (PosixString bs))
    = [| OsString (PosixString (BS.pack $(lift $ BS.unpack bs))) :: OsString |]
#endif
#if MIN_VERSION_template_haskell(2,17,0)
  liftTyped = TH.unsafeCodeCoerce . TH.lift
#elif MIN_VERSION_template_haskell(2,16,0)
  liftTyped = TH.unsafeTExpCoerce . TH.lift
#endif


  | Newtype representing a code unit .
--
  On Windows , this is restricted to two - octet codepoints ' Word16 ' ,
  on POSIX one - octet ( ' Word8 ' ) .
newtype OsChar = OsChar { getOsChar :: PlatformChar }
  deriving (Typeable, Generic, NFData)

instance Show OsChar where
  show (OsChar pc) = show pc

  | Byte equality of the internal representation .
instance Eq OsChar where
  (OsChar a) == (OsChar b) = a == b

  | Byte ordering of the internal representation .
instance Ord OsChar where
  compare (OsChar a) (OsChar b) = compare a b


# LANGUAGE PackageImports #
# LANGUAGE RankNTypes #

 | Commonly used windows string as wide character bytes.
 cWcharsToChars_UCS2 is total
 array.
 | Prints the raw bytes without decoding.
 | Just a short bidirectional synonym for 'PosixString' constructor.
# COMPLETE PS #
# COMPLETE WW #
 | Just a short bidirectional synonym for 'PosixChar' constructor.
# COMPLETE PW #

 depending on the platform. Both use unpinned
 'ShortByteString' for efficiency.

 dealing with the internals isn't generally recommended, but supported
 in case you need to write platform specific code.
 as '(</>)'.

# LANGUAGE CPP #
# LANGUAGE GeneralizedNewtypeDeriving #
# LANGUAGE TemplateHaskell #
# LANGUAGE DeriveGeneric #
# LANGUAGE TypeFamilies #
# LANGUAGE TypeApplications #
# LANGUAGE PatternSynonyms #

module System.OsString.Internal.Types
  (
    WindowsString(..)
  , pattern WS
  , unWS
  , PosixString(..)
  , unPS
  , pattern PS
  , PlatformString
  , WindowsChar(..)
  , unWW
  , pattern WW
  , PosixChar(..)
  , unPW
  , pattern PW
  , PlatformChar
  , OsString(..)
  , OsChar(..)
  )
where


import Control.DeepSeq
import Data.Data
import Data.Word
import Language.Haskell.TH.Syntax
    ( Lift (..), lift )
#if !MIN_VERSION_base(4,11,0)
import Data.Semigroup
#endif
import GHC.Generics (Generic)

import System.OsPath.Encoding.Internal
import qualified System.OsPath.Data.ByteString.Short as BS
import qualified System.OsPath.Data.ByteString.Short.Word16 as BS16
#if MIN_VERSION_template_haskell(2,16,0)
import qualified Language.Haskell.TH.Syntax as TH
#endif

  Using unpinned bytearrays to avoid Heap fragmentation and
  which are reasonably cheap to pass to FFI calls
  wrapped with typeclass - friendly types allowing to avoid CPP
  Note that , while unpinned bytearrays incur a memcpy on each
  FFI call , this overhead is generally much preferable to
  the memory fragmentation of pinned bytearrays

newtype WindowsString = WindowsString { getWindowsString :: BS.ShortByteString }
  deriving (Eq, Ord, Semigroup, Monoid, Typeable, Generic, NFData)

  | Decodes as  .
instance Show WindowsString where
  show = show . cWcharsToChars_UCS2 . BS16.unpack . getWindowsString

  | Just a short bidirectional synonym for '  ' constructor .
pattern WS :: BS.ShortByteString -> WindowsString
pattern WS { unWS } <- WindowsString unWS where
  WS a = WindowsString a
#if __GLASGOW_HASKELL__ >= 802
# COMPLETE WS #
#endif


instance Lift WindowsString where
  lift (WindowsString bs)
    = [| WindowsString (BS.pack $(lift $ BS.unpack bs)) :: WindowsString |]
#if MIN_VERSION_template_haskell(2,17,0)
  liftTyped = TH.unsafeCodeCoerce . TH.lift
#elif MIN_VERSION_template_haskell(2,16,0)
  liftTyped = TH.unsafeTExpCoerce . TH.lift
#endif

  | Commonly used  string as uninterpreted @char[]@
newtype PosixString = PosixString { getPosixString :: BS.ShortByteString }
  deriving (Eq, Ord, Semigroup, Monoid, Typeable, Generic, NFData)

instance Show PosixString where
  show (PosixString ps) = show ps

pattern PS :: BS.ShortByteString -> PosixString
pattern PS { unPS } <- PosixString unPS where
  PS a = PosixString a
#if __GLASGOW_HASKELL__ >= 802
#endif

instance Lift PosixString where
  lift (PosixString bs)
    = [| PosixString (BS.pack $(lift $ BS.unpack bs)) :: PosixString |]
#if MIN_VERSION_template_haskell(2,17,0)
  liftTyped = TH.unsafeCodeCoerce . TH.lift
#elif MIN_VERSION_template_haskell(2,16,0)
  liftTyped = TH.unsafeTExpCoerce . TH.lift
#endif


#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
type PlatformString = WindowsString
#else
type PlatformString = PosixString
#endif

newtype WindowsChar = WindowsChar { getWindowsChar :: Word16 }
  deriving (Eq, Ord, Typeable, Generic, NFData)

instance Show WindowsChar where
  show (WindowsChar wc) = show wc

newtype PosixChar   = PosixChar { getPosixChar :: Word8 }
  deriving (Eq, Ord, Typeable, Generic, NFData)

instance Show PosixChar where
  show (PosixChar pc) = show pc

  | Just a short bidirectional synonym for ' WindowsChar ' constructor .
pattern WW :: Word16 -> WindowsChar
pattern WW { unWW } <- WindowsChar unWW where
  WW a = WindowsChar a
#if __GLASGOW_HASKELL__ >= 802
#endif

pattern PW :: Word8 -> PosixChar
pattern PW { unPW } <- PosixChar unPW where
  PW a = PosixChar a
#if __GLASGOW_HASKELL__ >= 802
#endif

#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
type PlatformChar = WindowsChar
#else
type PlatformChar = PosixChar
#endif


  | Newtype representing short operating system specific strings .
  Internally this is either '  ' or ' PosixString ' ,
  The constructor is only exported via " System . OsString . Internal . Types " , since
newtype OsString = OsString { getOsString :: PlatformString }
  deriving (Typeable, Generic, NFData)

  | On windows , decodes as UCS-2 . On unix prints the raw bytes without decoding .
instance Show OsString where
  show (OsString os) = show os

  | Byte equality of the internal representation .
instance Eq OsString where
  (OsString a) == (OsString b) = a == b

  | Byte ordering of the internal representation .
instance Ord OsString where
  compare (OsString a) (OsString b) = compare a b


  | \"String - Concatenation\ " for '  ' . This is _ _ not _ _ the same
instance Monoid OsString where
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
    mempty      = OsString (WindowsString BS.empty)
#if MIN_VERSION_base(4,16,0)
    mappend = (<>)
#else
    mappend (OsString (WindowsString a)) (OsString (WindowsString b))
      = OsString (WindowsString (mappend a b))
#endif
#else
    mempty      = OsString (PosixString BS.empty)
#if MIN_VERSION_base(4,16,0)
    mappend = (<>)
#else
    mappend (OsString (PosixString a)) (OsString (PosixString b))
      = OsString (PosixString (mappend a b))
#endif
#endif
#if MIN_VERSION_base(4,11,0)
instance Semigroup OsString where
#if MIN_VERSION_base(4,16,0)
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
    (<>) (OsString (WindowsString a)) (OsString (WindowsString b))
      = OsString (WindowsString (mappend a b))
#else
    (<>) (OsString (PosixString a)) (OsString (PosixString b))
      = OsString (PosixString (mappend a b))
#endif
#else
    (<>) = mappend
#endif
#endif


instance Lift OsString where
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
  lift (OsString (WindowsString bs))
    = [| OsString (WindowsString (BS.pack $(lift $ BS.unpack bs))) :: OsString |]
#else
  lift (OsString (PosixString bs))
    = [| OsString (PosixString (BS.pack $(lift $ BS.unpack bs))) :: OsString |]
#endif
#if MIN_VERSION_template_haskell(2,17,0)
  liftTyped = TH.unsafeCodeCoerce . TH.lift
#elif MIN_VERSION_template_haskell(2,16,0)
  liftTyped = TH.unsafeTExpCoerce . TH.lift
#endif


  | Newtype representing a code unit .
  On Windows , this is restricted to two - octet codepoints ' Word16 ' ,
  on POSIX one - octet ( ' Word8 ' ) .
newtype OsChar = OsChar { getOsChar :: PlatformChar }
  deriving (Typeable, Generic, NFData)

instance Show OsChar where
  show (OsChar pc) = show pc

  | Byte equality of the internal representation .
instance Eq OsChar where
  (OsChar a) == (OsChar b) = a == b

  | Byte ordering of the internal representation .
instance Ord OsChar where
  compare (OsChar a) (OsChar b) = compare a b

