{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}

{-|
Module      : Text.Pretty.Simple.Internal.Output
Copyright   : (c) Dennis Gosnell, 2016
License     : BSD-style (see LICENSE file)
Maintainer  : cdep.illabout@gmail.com
Stability   : experimental
Portability : POSIX

-}
module Text.Pretty.Simple.Internal.Output
  where

#if __GLASGOW_HASKELL__ < 710
-- We don't need this import for GHC 7.10 as it exports all required functions
-- from Prelude
import Control.Applicative
#endif

import Data.Data (Data)
import Data.String (IsString, fromString)
import Data.Typeable (Typeable)
import GHC.Generics (Generic)

-- | Datatype representing how much something is nested.
--
-- For example, a 'NestLevel' of 0 would mean an 'Output' token
-- is at the very highest level, not in any braces.
--
-- A 'NestLevel' of 1 would mean that an 'Output' token is in one single pair
-- of @\{@ and @\}@, or @\[@ and @\], or @\(@ and @\)@.
--
-- A 'NestLevel' of 2 would mean that an 'Output' token is two levels of
-- brackets, etc.
newtype NestLevel = NestLevel { unNestLevel :: Int }
  deriving (Data, Eq, Generic, Num, Ord, Read, Show, Typeable)

-- | These are the output tokens that we will be printing to the screen.
data OutputType
  = OutputCloseBrace
  -- ^ This represents the @\}@ character.
  | OutputCloseBracket
  -- ^ This represents the @\]@ character.
  | OutputCloseParen
  -- ^ This represents the @\)@ character.
  | OutputComma
  -- ^ This represents the @\,@ character.
  | OutputIndent
  -- ^ This represents an indentation.
  | OutputNewLine
  -- ^ This represents the @\\n@ character.
  | OutputOpenBrace
  -- ^ This represents the @\{@ character.
  | OutputOpenBracket
  -- ^ This represents the @\[@ character.
  | OutputOpenParen
  -- ^ This represents the @\(@ character.
  | OutputOther !String
  -- ^ This represents some collection of characters that don\'t fit into any
  -- of the other tokens.
  | OutputStringLit !String
  -- ^ This represents a string literal.  For instance, @\"foobar\"@.
  | OutputCharLit !String
  -- ^ This represents a char literal.  For example, @'x'@ or @'\b'@
  | OutputNumberLit !String
  -- ^ This represents a numeric literal.  For example, @12345@ or @3.14159@.
  deriving (Data, Eq, Generic, Read, Show, Typeable)

-- | 'IsString' (and 'fromString') should generally only be used in tests and
-- debugging.  There is no way to represent 'OutputIndent', 'OutputNumberLit'
-- and 'OutputStringLit'.
instance IsString OutputType where
    fromString :: String -> OutputType
    fromString "}" = OutputCloseBrace
    fromString "]" = OutputCloseBracket
    fromString ")" = OutputCloseParen
    fromString "," = OutputComma
    fromString "\n" = OutputNewLine
    fromString "{" = OutputOpenBrace
    fromString "[" = OutputOpenBracket
    fromString "(" = OutputOpenParen
    fromString string = OutputOther string

-- | An 'OutputType' token together with a 'NestLevel'.  Basically, each
-- 'OutputType' keeps track of its own 'NestLevel'.
data Output = Output
  { outputNestLevel :: {-# UNPACK #-} !NestLevel
  , outputOutputType :: !OutputType
  } deriving (Data, Eq, Generic, Read, Show, Typeable)