{-# LANGUAGE CPP             #-}
{-# LANGUAGE GADTs           #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies    #-}

#if __GLASGOW_HASKELL__ >= 702
{-# LANGUAGE DeriveGeneric   #-}
#endif

{-|
Module:      Derived.Infix
Copyright:   (C) 2014-2015 Ryan Scott
License:     BSD-style (see the file LICENSE)
Maintainer:  Ryan Scott
Stability:   Provisional
Portability: GHC

Defines data types with infix constructors.
-}
module Derived.Infix (
      TyConPlain(..)
    , TyConGADT(..)
    , TyFamilyPlain(..)
    , TyFamilyGADT(..)
    ) where

#include "generic.h"

#if __GLASGOW_HASKELL__ >= 702
import GHC.Generics (Generic)
# if defined(__LANGUAGE_DERIVE_GENERIC1__)
import GHC.Generics (Generic1)
# endif
#endif
import GHC.Show (appPrec, appPrec1, showSpace)

import Prelude ()
import Prelude.Compat

import Test.QuickCheck (Arbitrary(..), oneof)

import TextShow.TH (deriveTextShow, deriveTextShow1, deriveTextShow2)

import TransformersCompat (Show1(..), Show2(..), showsBinaryWith)

-------------------------------------------------------------------------------

infixl 3 :!:
infix  4 :@:
infixr 5 `TyConPlain`
data TyConPlain a b = (:!:) a b
                    | a :@: b
                    | a `TyConPlain` b
  deriving ( Show
#if __GLASGOW_HASKELL__ >= 702
           , Generic
# if defined(__LANGUAGE_DERIVE_GENERIC1__)
           , Generic1
# endif
#endif
           )

-------------------------------------------------------------------------------

infixr 1 :., :..., :....
data TyConGADT a b where
    (:.)    ::           c ->       d        -> TyConGADT c d
    (:..)   ::           e ->       f        -> TyConGADT e f
    (:...)  ::           g ->       h -> Int -> TyConGADT g h
    (:....) :: { tcg1 :: i, tcg2 :: j }      -> TyConGADT i j
  deriving ( Show
#if __GLASGOW_HASKELL__ >= 706
           , Generic
# if defined(__LANGUAGE_DERIVE_GENERIC1__)
           , Generic1
# endif
#endif
           )

-------------------------------------------------------------------------------

data family TyFamilyPlain
#if __GLASGOW_HASKELL__ >= 708 && __GLASGOW_HASKELL__ < 710
                          a b :: *
#else
                          y z :: *
#endif

infixl 3 :#:
infix  4 :$:
infixr 5 `TyFamilyPlain`
data instance TyFamilyPlain a b = (:#:) a b
                                | a :$: b
                                | a `TyFamilyPlain` b
  deriving ( Show
#if __GLASGOW_HASKELL__ >= 706
           , Generic
# if defined(__LANGUAGE_DERIVE_GENERIC1__)
           , Generic1
# endif
#endif
           )

-------------------------------------------------------------------------------

data family TyFamilyGADT
#if __GLASGOW_HASKELL__ >= 708 && __GLASGOW_HASKELL__ < 710
                         a b :: *
#else
                         y z :: *
#endif

infixr 1 :*, :***, :****
data instance TyFamilyGADT a b where
    (:*)    ::           c ->       d        -> TyFamilyGADT c d
    (:**)   ::           e ->       f        -> TyFamilyGADT e f
    (:***)  ::           g ->       h -> Int -> TyFamilyGADT g h
    (:****) :: { tfg1 :: i, tfg2 :: j }      -> TyFamilyGADT i j
  deriving ( Show
#if __GLASGOW_HASKELL__ >= 706
           , Generic
# if defined(__LANGUAGE_DERIVE_GENERIC1__)
           , Generic1
# endif
#endif
           )

-------------------------------------------------------------------------------

instance (Arbitrary a, Arbitrary b) => Arbitrary (TyConPlain a b) where
    arbitrary = oneof (map pure [(:!:), (:@:), TyConPlain]) <*> arbitrary <*> arbitrary

instance (Arbitrary a, Arbitrary b) => Arbitrary (TyConGADT a b) where
    arbitrary = oneof [ pure (:.)
                      , pure (:..)
                      , flip (flip . (:...)) <$> arbitrary
                      , pure (:....)
                      ]
                <*> arbitrary <*> arbitrary

instance (Arbitrary a, Arbitrary b) => Arbitrary (TyFamilyPlain a b) where
    arbitrary = oneof (map pure [(:#:), (:$:), TyFamilyPlain]) <*> arbitrary <*> arbitrary

instance (Arbitrary a, Arbitrary b) => Arbitrary (TyFamilyGADT a b) where
    arbitrary = oneof [ pure (:*)
                      , pure (:**)
                      , flip (flip . (:***)) <$> arbitrary
                      , pure (:****)
                      ]
                <*> arbitrary <*> arbitrary

-------------------------------------------------------------------------------

instance Show a => Show1 (TyConPlain a) where
    showsPrecWith = showsPrecWith2 showsPrec
instance Show2 TyConPlain where
    showsPrecWith2 sp1 sp2 p (a :!: b) =
        showsBinaryWith sp1 sp2 "(:!:)" p a b
    showsPrecWith2 sp1 sp2 p (a :@: b) =
        showsInfix sp1 sp2 ":@:" p 4 a b
    showsPrecWith2 sp1 sp2 p (TyConPlain a b) =
        showsInfix sp1 sp2 "`TyConPlain`" p 5 a b

instance Show a => Show1 (TyConGADT a) where
    showsPrecWith = showsPrecWith2 showsPrec
instance Show2 TyConGADT where
    showsPrecWith2 sp1 sp2 p (a :. b) =
        showsInfix sp1 sp2 ":." p 1 a b
    showsPrecWith2 sp1 sp2 p (a :.. b) =
        showsBinaryWith sp1 sp2 "(:..)" p a b
    showsPrecWith2 sp1 sp2 p ((:...) a b i) =
        showsTernaryWith sp1 sp2 "(:...)" p a b i
    showsPrecWith2 sp1 sp2 p (a :.... b) =
        showsBinaryWith sp1 sp2 "(:....)" p a b

instance Show a => Show1 (TyFamilyPlain a) where
    showsPrecWith = showsPrecWith2 showsPrec
instance Show2 TyFamilyPlain where
    showsPrecWith2 sp1 sp2 p (a :#: b) =
        showsBinaryWith sp1 sp2 "(:#:)" p a b
    showsPrecWith2 sp1 sp2 p (a :$: b) =
        showsInfix sp1 sp2 ":$:" p 4 a b
    showsPrecWith2 sp1 sp2 p (TyFamilyPlain a b) =
        showsInfix sp1 sp2 "`TyFamilyPlain`" p 5 a b

instance Show a => Show1 (TyFamilyGADT a) where
    showsPrecWith = showsPrecWith2 showsPrec
instance Show2 TyFamilyGADT where
    showsPrecWith2 sp1 sp2 p (a :* b) =
        showsInfix sp1 sp2 ":*" p 1 a b
    showsPrecWith2 sp1 sp2 p (a :** b) =
        showsBinaryWith sp1 sp2 "(:**)" p a b
    showsPrecWith2 sp1 sp2 p ((:***) a b i) =
        showsTernaryWith sp1 sp2 "(:***)" p a b i
    showsPrecWith2 sp1 sp2 p (a :**** b) =
        showsBinaryWith sp1 sp2 "(:****)" p a b

showsInfix :: (Int -> a -> ShowS) -> (Int -> b -> ShowS)
           -> String -> Int -> Int -> a -> b -> ShowS
showsInfix sp1 sp2 name p infixPrec a b = showParen (p > infixPrec) $
      sp1 (infixPrec + 1) a . showSpace
    . showString name       . showSpace
    . sp2 (infixPrec + 1) b

showsTernaryWith :: (Int -> a -> ShowS) -> (Int -> b -> ShowS)
                 -> String -> Int -> a -> b -> Int -> ShowS
showsTernaryWith sp1 sp2 name p a b i = showParen (p > appPrec) $
      showString name . showSpace
    . sp1 appPrec1 a  . showSpace
    . sp2 appPrec1 b  . showSpace
    . showsPrec appPrec1 i

-------------------------------------------------------------------------------

$(deriveTextShow  ''TyConPlain)
$(deriveTextShow1 ''TyConPlain)
$(deriveTextShow2 ''TyConPlain)

$(deriveTextShow  ''TyConGADT)
$(deriveTextShow1 ''TyConGADT)
$(deriveTextShow2 ''TyConGADT)

#if MIN_VERSION_template_haskell(2,7,0)
$(deriveTextShow  '(:#:))
$(deriveTextShow1 '(:$:))
$(deriveTextShow2 'TyFamilyPlain)

$(deriveTextShow  '(:*))
$(deriveTextShow1 '(:***))
$(deriveTextShow2 '(:****))
#endif
