    {-# LANGUAGE CPP #-}
    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE MagicHash #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE TypeOperators #-}
    {-# LANGUAGE TypeSynonymInstances #-}

    #if __GLASGOW_HASKELL__ >= 701
    {-# LANGUAGE DefaultSignatures #-}
    {-# LANGUAGE Trustworthy #-}
    #endif

    #if __GLASGOW_HASKELL__ < 709
    {-# LANGUAGE OverlappingInstances #-}
    #endif

    module Generics.Deriving.Show (
      -- * Generic show class
        GShow(..)

      -- * Default definition
      , gshowsPrecdefault

      -- * Internal show class
      , GShow'(..)

      ) where

    import Control.Applicative (Const, ZipList)

    import           Data.Char (GeneralCategory)
    import           Data.Int
    import           Data.Monoid (All, Any, Dual, Product, Sum)
    import qualified Data.Monoid as Monoid (First, Last)
    import           Data.Version (Version)
    import           Data.Word

    import           Foreign.C.Types
    import           Foreign.ForeignPtr (ForeignPtr)
    import           Foreign.Ptr

    import           Generics.Deriving.Base

    import           GHC.Exts hiding (Any)

    import           System.Exit (ExitCode)
    import           System.IO (BufferMode, Handle, HandlePosn, IOMode, SeekMode)
    import           System.IO.Error (IOErrorType)
    import           System.Posix.Types

    #if MIN_VERSION_base(4,4,0)
    import           Data.Complex (Complex)
    #endif

    #if MIN_VERSION_base(4,7,0)
    import           Data.Proxy (Proxy)
    #endif

    #if MIN_VERSION_base(4,8,0)
    import           Data.Functor.Identity (Identity)
    import           Data.Monoid (Alt)
    import           Data.Void (Void)
    import           Numeric.Natural (Natural)
    #endif

    #if MIN_VERSION_base(4,9,0)
    import           Data.List.NonEmpty (NonEmpty)
    import qualified Data.Semigroup as Semigroup (First, Last)
    import           Data.Semigroup (Arg, Max, Min, Option, WrappedMonoid)
    #endif

    --------------------------------------------------------------------------------
    -- Generic show
    --------------------------------------------------------------------------------

    intersperse :: a -> [a] -> [a]
    intersperse _ []    = []
    intersperse _ [h]   = [h]
    intersperse x (h:t) = h : x : (intersperse x t)

    appPrec :: Int
    appPrec = 2

    data Type = Rec | Tup | Pref | Inf String

    class GShow' f where
      gshowsPrec' :: Type -> Int -> f a -> ShowS
      isNullary   :: f a -> Bool
      isNullary = error "generic show (isNullary): unnecessary case"

    instance GShow' U1 where
      gshowsPrec' _ _ U1 = id
      isNullary _ = True

    instance (GShow c) => GShow' (K1 i c) where
      gshowsPrec' _ n (K1 a) = gshowsPrec n a
      isNullary _ = False

    -- No instances for P or Rec because gshow is only applicable to types of kind *

    instance (GShow' a, Constructor c) => GShow' (M1 C c a) where
      gshowsPrec' _ n c@(M1 x) =
        case fixity of
          Prefix    -> showParen (n > appPrec && not (isNullary x))
                        ( showString (conName c)
                        . if (isNullary x) then id else showChar ' '
                        . showBraces t (gshowsPrec' t appPrec x))
          Infix _ m -> showParen (n > m) (showBraces t (gshowsPrec' t m x))
          where fixity = conFixity c
                t = if (conIsRecord c) then Rec else
                      case (conIsTuple c) of
                        True -> Tup
                        False -> case fixity of
                                    Prefix    -> Pref
                                    Infix _ _ -> Inf (show (conName c))
                showBraces :: Type -> ShowS -> ShowS
                showBraces Rec     p = showChar '{' . p . showChar '}'
                showBraces Tup     p = showChar '(' . p . showChar ')'
                showBraces Pref    p = p
                showBraces (Inf _) p = p

                conIsTuple :: C1 c f p -> Bool
                conIsTuple y = tupleName (conName y) where
                  tupleName ('(':',':_) = True
                  tupleName _           = False

    instance (Selector s, GShow' a) => GShow' (M1 S s a) where
      gshowsPrec' t n s@(M1 x) | selName s == "" = --showParen (n > appPrec)
                                                     (gshowsPrec' t n x)
                               | otherwise       =   showString (selName s)
                                                   . showString " = "
                                                   . gshowsPrec' t 0 x
      isNullary (M1 x) = isNullary x

    instance (GShow' a) => GShow' (M1 D d a) where
      gshowsPrec' t n (M1 x) = gshowsPrec' t n x

    instance (GShow' a, GShow' b) => GShow' (a :+: b) where
      gshowsPrec' t n (L1 x) = gshowsPrec' t n x
      gshowsPrec' t n (R1 x) = gshowsPrec' t n x

    instance (GShow' a, GShow' b) => GShow' (a :*: b) where
      gshowsPrec' t@Rec     n (a :*: b) =
        gshowsPrec' t n     a . showString ", " . gshowsPrec' t n     b
      gshowsPrec' t@(Inf s) n (a :*: b) =
        gshowsPrec' t n     a . showString s    . gshowsPrec' t n     b
      gshowsPrec' t@Tup     n (a :*: b) =
        gshowsPrec' t n     a . showChar ','    . gshowsPrec' t n     b
      gshowsPrec' t@Pref    n (a :*: b) =
        gshowsPrec' t (n+1) a . showChar ' '    . gshowsPrec' t (n+1) b

      -- If we have a product then it is not a nullary constructor
      isNullary _ = False

    -- Unboxed types
    instance GShow' UChar where
      gshowsPrec' _ _ (UChar c)   = showsPrec 0 (C# c) . showChar '#'
    instance GShow' UDouble where
      gshowsPrec' _ _ (UDouble d) = showsPrec 0 (D# d) . showString "##"
    instance GShow' UFloat where
      gshowsPrec' _ _ (UFloat f)  = showsPrec 0 (F# f) . showChar '#'
    instance GShow' UInt where
      gshowsPrec' _ _ (UInt i)    = showsPrec 0 (I# i) . showChar '#'
    instance GShow' UWord where
      gshowsPrec' _ _ (UWord w)   = showsPrec 0 (W# w) . showString "##"

    class GShow a where
      gshowsPrec :: Int -> a -> ShowS
      gshows :: a -> ShowS
      gshows = gshowsPrec 0
      gshow :: a -> String
      gshow x = gshows x ""
    #if __GLASGOW_HASKELL__ >= 701
      default gshowsPrec :: (Generic a, GShow' (Rep a))
                         => Int -> a -> ShowS
      gshowsPrec = gshowsPrecdefault
    #endif

    gshowsPrecdefault :: (Generic a, GShow' (Rep a))
                      => Int -> a -> ShowS
    gshowsPrecdefault n = gshowsPrec' Pref n . from

    -- Base types instances
    -- Base types instances
    instance GShow () where
      gshowsPrec = gshowsPrecdefault

    instance (GShow a, GShow b) => GShow (a, b) where
      gshowsPrec = gshowsPrecdefault

    instance (GShow a, GShow b, GShow c) => GShow (a, b, c) where
      gshowsPrec = gshowsPrecdefault

    instance (GShow a, GShow b, GShow c, GShow d) => GShow (a, b, c, d) where
      gshowsPrec = gshowsPrecdefault

    instance (GShow a, GShow b, GShow c, GShow d, GShow e) => GShow (a, b, c, d, e) where
      gshowsPrec = gshowsPrecdefault

    instance (GShow a, GShow b, GShow c, GShow d, GShow e, GShow f)
        => GShow (a, b, c, d, e, f) where
      gshowsPrec = gshowsPrecdefault

    instance (GShow a, GShow b, GShow c, GShow d, GShow e, GShow f, GShow g)
        => GShow (a, b, c, d, e, f, g) where
      gshowsPrec = gshowsPrecdefault

    instance
    #if __GLASGOW_HASKELL__ >= 709
        {-# OVERLAPPABLE #-}
    #endif
        (GShow a) => GShow [a] where
      gshowsPrec _ l =   showChar '['
                       . foldr (.) id
                          (intersperse (showChar ',') (map (gshowsPrec 0) l))
                       . showChar ']'

    instance (GShow (f p), GShow (g p)) => GShow ((f :+: g) p) where
      gshowsPrec = gshowsPrecdefault

    instance (GShow (f p), GShow (g p)) => GShow ((f :*: g) p) where
      gshowsPrec = gshowsPrecdefault

    instance GShow (f (g p)) => GShow ((f :.: g) p) where
      gshowsPrec = gshowsPrecdefault

    instance GShow All where
      gshowsPrec = gshowsPrecdefault

    #if MIN_VERSION_base(4,8,0)
    instance GShow (f a) => GShow (Alt f a) where
      gshowsPrec = gshowsPrecdefault
    #endif

    instance GShow Any where
      gshowsPrec = gshowsPrecdefault

    #if MIN_VERSION_base(4,9,0)
    instance (GShow a, GShow b) => GShow (Arg a b) where
      gshowsPrec = gshowsPrecdefault
    #endif

    #if !(MIN_VERSION_base(4,9,0))
    instance GShow Arity where
      gshowsPrec = gshowsPrecdefault
    #endif

    instance GShow Associativity where
      gshowsPrec = gshowsPrecdefault

    instance GShow Bool where
      gshowsPrec = gshowsPrecdefault

    instance GShow BufferMode where
      gshowsPrec = showsPrec

    #if defined(HTYPE_CC_T)
    instance GShow CCc where
      gshowsPrec = showsPrec
    #endif

    instance GShow CChar where
      gshowsPrec = showsPrec

    instance GShow CClock where
      gshowsPrec = showsPrec

    #if defined(HTYPE_DEV_T)
    instance GShow CDev where
      gshowsPrec = showsPrec
    #endif

    instance GShow CDouble where
      gshowsPrec = showsPrec

    instance GShow CFloat where
      gshowsPrec = showsPrec

    #if defined(HTYPE_GID_T)
    instance GShow CGid where
      gshowsPrec = showsPrec
    #endif

    instance GShow Char where
      gshowsPrec = showsPrec

    #if defined(HTYPE_INO_T)
    instance GShow CIno where
      gshowsPrec = showsPrec
    #endif

    instance GShow CInt where
      gshowsPrec = showsPrec

    instance GShow CIntMax where
      gshowsPrec = showsPrec

    instance GShow CIntPtr where
      gshowsPrec = showsPrec

    instance GShow CLLong where
      gshowsPrec = showsPrec

    instance GShow CLong where
      gshowsPrec = showsPrec

    #if defined(HTYPE_MODE_T)
    instance GShow CMode where
      gshowsPrec = showsPrec
    #endif

    #if defined(HTYPE_NLINK_T)
    instance GShow CNlink where
      gshowsPrec = showsPrec
    #endif

    #if defined(HTYPE_OFF_T)
    instance GShow COff where
      gshowsPrec = showsPrec
    #endif

    #if MIN_VERSION_base(4,4,0)
    instance GShow a => GShow (Complex a) where
      gshowsPrec = gshowsPrecdefault
    #endif

    instance GShow a => GShow (Const a b) where
      gshowsPrec = gshowsPrecdefault

    #if defined(HTYPE_PID_T)
    instance GShow CPid where
      gshowsPrec = showsPrec
    #endif

    instance GShow CPtrdiff where
      gshowsPrec = showsPrec

    #if defined(HTYPE_RLIM_T)
    instance GShow CRLim where
      gshowsPrec = showsPrec
    #endif

    instance GShow CSChar where
      gshowsPrec = showsPrec

    #if defined(HTYPE_SPEED_T)
    instance GShow CSpeed where
      gshowsPrec = showsPrec
    #endif

    #if MIN_VERSION_base(4,4,0)
    instance GShow CSUSeconds where
      gshowsPrec = showsPrec
    #endif

    instance GShow CShort where
      gshowsPrec = showsPrec

    instance GShow CSigAtomic where
      gshowsPrec = showsPrec

    instance GShow CSize where
      gshowsPrec = showsPrec

    #if defined(HTYPE_SSIZE_T)
    instance GShow CSsize where
      gshowsPrec = showsPrec
    #endif

    #if defined(HTYPE_TCFLAG_T)
    instance GShow CTcflag where
      gshowsPrec = showsPrec
    #endif

    instance GShow CTime where
      gshowsPrec = showsPrec

    instance GShow CUChar where
      gshowsPrec = showsPrec

    #if defined(HTYPE_UID_T)
    instance GShow CUid where
      gshowsPrec = showsPrec
    #endif

    instance GShow CUInt where
      gshowsPrec = showsPrec

    instance GShow CUIntMax where
      gshowsPrec = showsPrec

    instance GShow CUIntPtr where
      gshowsPrec = showsPrec

    instance GShow CULLong where
      gshowsPrec = showsPrec

    instance GShow CULong where
      gshowsPrec = showsPrec

    #if MIN_VERSION_base(4,4,0)
    instance GShow CUSeconds where
      gshowsPrec = showsPrec
    #endif

    instance GShow CUShort where
      gshowsPrec = showsPrec

    instance GShow CWchar where
      gshowsPrec = showsPrec

    instance GShow Double where
      gshowsPrec = showsPrec

    instance GShow a => GShow (Dual a) where
      gshowsPrec = gshowsPrecdefault

    instance (GShow a, GShow b) => GShow (Either a b) where
      gshowsPrec = gshowsPrecdefault

    instance GShow ExitCode where
      gshowsPrec = gshowsPrecdefault

    instance GShow Fd where
      gshowsPrec = showsPrec

    instance GShow a => GShow (Monoid.First a) where
      gshowsPrec = gshowsPrecdefault

    #if MIN_VERSION_base(4,9,0)
    instance GShow a => GShow (Semigroup.First a) where
      gshowsPrec = gshowsPrecdefault
    #endif

    instance GShow Fixity where
      gshowsPrec = gshowsPrecdefault

    instance GShow Float where
      gshowsPrec = showsPrec

    instance GShow (ForeignPtr a) where
      gshowsPrec = showsPrec

    instance GShow (FunPtr a) where
      gshowsPrec = showsPrec

    instance GShow GeneralCategory where
      gshowsPrec = showsPrec

    instance GShow Handle where
      gshowsPrec = showsPrec

    instance GShow HandlePosn where
      gshowsPrec = showsPrec

    #if MIN_VERSION_base(4,8,0)
    instance GShow a => GShow (Identity a) where
      gshowsPrec = gshowsPrecdefault
    #endif

    instance GShow Int where
      gshowsPrec = showsPrec

    instance GShow Int8 where
      gshowsPrec = showsPrec

    instance GShow Int16 where
      gshowsPrec = showsPrec

    instance GShow Int32 where
      gshowsPrec = showsPrec

    instance GShow Int64 where
      gshowsPrec = showsPrec

    instance GShow Integer where
      gshowsPrec = showsPrec

    instance GShow IntPtr where
      gshowsPrec = showsPrec

    instance GShow IOError where
      gshowsPrec = showsPrec

    instance GShow IOErrorType where
      gshowsPrec = showsPrec

    instance GShow IOMode where
      gshowsPrec = showsPrec

    instance GShow c => GShow (K1 i c p) where
      gshowsPrec = gshowsPrecdefault

    instance GShow a => GShow (Monoid.Last a) where
      gshowsPrec = gshowsPrecdefault

    #if MIN_VERSION_base(4,9,0)
    instance GShow a => GShow (Semigroup.Last a) where
      gshowsPrec = gshowsPrecdefault
    #endif

    instance GShow (f p) => GShow (M1 i c f p) where
      gshowsPrec = gshowsPrecdefault

    #if MIN_VERSION_base(4,9,0)
    instance GShow a => GShow (Max a) where
      gshowsPrec = gshowsPrecdefault
    #endif

    instance GShow a => GShow (Maybe a) where
      gshowsPrec = gshowsPrecdefault

    #if MIN_VERSION_base(4,9,0)
    instance GShow a => GShow (Min a) where
      gshowsPrec = gshowsPrecdefault
    #endif

    #if MIN_VERSION_base(4,8,0)
    instance GShow Natural where
      gshowsPrec = showsPrec
    #endif

    #if MIN_VERSION_base(4,9,0)
    instance GShow a => GShow (NonEmpty a) where
      gshowsPrec = gshowsPrecdefault

    instance GShow a => GShow (Option a) where
      gshowsPrec = gshowsPrecdefault
    #endif

    instance GShow Ordering where
      gshowsPrec = gshowsPrecdefault

    instance GShow p => GShow (Par1 p) where
      gshowsPrec = gshowsPrecdefault

    instance GShow a => GShow (Product a) where
      gshowsPrec = gshowsPrecdefault

    #if MIN_VERSION_base(4,7,0)
    instance GShow (Proxy s) where
      gshowsPrec = gshowsPrecdefault
    #endif

    instance GShow (Ptr a) where
      gshowsPrec = showsPrec

    instance GShow (f p) => GShow (Rec1 f p) where
      gshowsPrec = gshowsPrecdefault

    instance GShow SeekMode where
      gshowsPrec = showsPrec

    instance
    #if __GLASGOW_HASKELL__ >= 709
        {-# OVERLAPPING #-}
    #endif
        GShow String where
      gshowsPrec = showsPrec

    instance GShow a => GShow (Sum a) where
      gshowsPrec = gshowsPrecdefault

    instance GShow (U1 p) where
      gshowsPrec = gshowsPrecdefault

    instance GShow (UChar p) where
      gshowsPrec = gshowsPrecdefault

    instance GShow (UDouble p) where
      gshowsPrec = gshowsPrecdefault

    instance GShow (UFloat p) where
      gshowsPrec = gshowsPrecdefault

    instance GShow (UInt p) where
      gshowsPrec = gshowsPrecdefault

    instance GShow (UWord p) where
      gshowsPrec = gshowsPrecdefault

    instance GShow Version where
      gshowsPrec = gshowsPrecdefault

    #if MIN_VERSION_base(4,8,0)
    instance GShow Void where
      gshowsPrec = showsPrec
    #endif

    instance GShow Word where
      gshowsPrec = showsPrec

    instance GShow Word8 where
      gshowsPrec = showsPrec

    instance GShow Word16 where
      gshowsPrec = showsPrec

    instance GShow Word32 where
      gshowsPrec = showsPrec

    instance GShow Word64 where
      gshowsPrec = showsPrec

    instance GShow WordPtr where
      gshowsPrec = showsPrec

    #if MIN_VERSION_base(4,9,0)
    instance GShow m => GShow (WrappedMonoid m) where
      gshowsPrec = gshowsPrecdefault
    #endif

    instance GShow a => GShow (ZipList a) where
      gshowsPrec = gshowsPrecdefault

    #if MIN_VERSION_base(4,10,0)
    instance GShow CBool where
      gshowsPrec = showsPrec

    # if defined(HTYPE_BLKSIZE_T)
    instance GShow CBlkSize where
      gshowsPrec = showsPrec
    # endif

    # if defined(HTYPE_BLKCNT_T)
    instance GShow CBlkCnt where
      gshowsPrec = showsPrec
    # endif

    # if defined(HTYPE_CLOCKID_T)
    instance GShow CClockId where
      gshowsPrec = showsPrec
    # endif

    # if defined(HTYPE_FSBLKCNT_T)
    instance GShow CFsBlkCnt where
      gshowsPrec = showsPrec
    # endif

    # if defined(HTYPE_FSFILCNT_T)
    instance GShow CFsFilCnt where
      gshowsPrec = showsPrec
    # endif

    # if defined(HTYPE_ID_T)
    instance GShow CId where
      gshowsPrec = showsPrec
    # endif

    # if defined(HTYPE_KEY_T)
    instance GShow CKey where
      gshowsPrec = showsPrec
    # endif

    # if defined(HTYPE_TIMER_T)
    instance GShow CTimer where
      gshowsPrec = showsPrec
    # endif
    #endif