{-# LANGUAGE GADTs               #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE ConstraintKinds     #-}
{-# LANGUAGE TypeOperators       #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveGeneric       #-}
-- |Derives a generic show, for example
--
-- > data MyList a = MyNil | MyCons { hd :: a, tl :: MyList a } deriving Generic
-- > 
-- > myListValue :: MyList Integer
-- > myListValue = MyCons 1 (MyCons 2 (MyCons 3 MyNil))
-- > 
-- > instance Show a => Show (MyList a) where
-- >   show = gshow
--
-- The code here was adapted from `generic-deriving`
-- https://github.com/dreixel/generic-deriving/blob/master/src/Generics/Deriving/Show.hs
module Generics.Simplistic.Derive.Show (gshow , gshowsPrec) where

import Generics.Simplistic
import GHC.Generics

appPrec :: Int
appPrec = 2

data Type = Rec | Tup | Pref | Inf String

gshow :: (Generic t, GShallow (Rep t), OnLeaves Show (Rep t))
      => t -> String
gshow v = gshowsPrec Pref 0 v ""

gshowsPrec :: (Generic t, GShallow (Rep t), OnLeaves Show (Rep t))
           => Type -> Int -> t -> ShowS
gshowsPrec t n v = gshowsPrec' t n (fromS v)

gshowsPrec' :: (OnLeaves Show f)
            => Type -> Int -> SRep I f -> ShowS
-- "Simple" cases
gshowsPrec' _ _ S_U1       = id
gshowsPrec' t n (S_L1 x)   = gshowsPrec' t n x
gshowsPrec' t n (S_R1 x)   = gshowsPrec' t n x
gshowsPrec' _ n (S_K1 x)   = showsPrec n x
gshowsPrec' t n (S_ST x)   = gshowsPrec' t n x
-- The complex case of tuples
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
-- The case of metadata
gshowsPrec' _ n (S_M1 (SM_C :: SMeta i c) (x :: SRep I f)) = 
  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 c :: M1 C c f () = undefined
          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
gshowsPrec' t n (S_M1 (SM_S :: SMeta i c) (x :: SRep I f)) 
  | selName s == "" = --showParen (n > appPrec)
                      (gshowsPrec' t n x)
  | otherwise       =   showString (selName s)
                      . showString " = "
                      . gshowsPrec' t 0 x
    where s :: M1 S c f () = undefined
gshowsPrec' t n (S_M1 _ x) = gshowsPrec' t n x

isNullary :: SRep I a -> Bool
isNullary S_U1       = True
isNullary (S_L1 _)   = error "unnecessary case"
isNullary (S_R1 _)   = error "unnecessary case"
isNullary (_ :**: _) = False
isNullary (S_K1 _)   = False
isNullary (S_M1 t x) = case t of
                         SM_S -> isNullary x
                         _    -> error "unnecessary case"
isNullary (S_ST x)   = isNullary x
