{-# LANGUAGE LambdaCase #-}

module Test.Tasty.Patterns.Printer
  ( printAwkExpr
  )
  where

import Prelude hiding (LT, GT, EQ)
import Test.Tasty.Patterns.Types

-- | @since 1.4.2
printAwkExpr :: Expr -> String
printAwkExpr e = go 0 e ""

go :: Int -> Expr -> ShowS
go p = \case
  NF -> showString "NF"
  IntLit n -> showsPrec p n
  StringLit xs -> showChar '"' . showString (escapeString xs) . showChar '"'
  ERE xs -> showChar '/' . showString (escapeERE xs) . showChar '/'

  Field x -> showParen (p >= 9) $ showChar '$' . go 9 x

  -- Cf. comment for Test.Tasty.Patterns.Parser.expr2 to understand
  -- why we put showParens when precedence is 6 not 8.
  Neg x -> showParen (p >= 6) $ showChar '-' . go 8 x
  Not x -> showParen (p >= 8) $ showChar '!' . go 8 x

  Add x y -> showParen (p >= 7) $ go 7 x . showChar '+' . go 7 y
  Sub x y -> showParen (p >= 7) $ go 7 x . showChar '-' . go 7 y

  Concat x y -> showParen (p >= 6) $ go 6 x . showChar ' ' . go 6 y

  LT x y -> showParen (p >= 5) $ go 5 x . showChar '<'    . go 5 y
  LE x y -> showParen (p >= 5) $ go 5 x . showString "<=" . go 5 y
  GT x y -> showParen (p >= 5) $ go 5 x . showChar '>'    . go 5 y
  GE x y -> showParen (p >= 5) $ go 5 x . showString ">=" . go 5 y
  EQ x y -> showParen (p >= 5) $ go 5 x . showString "==" . go 5 y
  NE x y -> showParen (p >= 5) $ go 5 x . showString "!=" . go 5 y

  Match x y   -> showParen (p >= 4) $ go 4 x . showChar '~'    . go 4 (ERE y)
  NoMatch x y -> showParen (p >= 4) $ go 4 x . showString "!~" . go 4 (ERE y)

  And x y -> showParen (p >= 2) $ go 2 x . showString "&&" . go 2 y

  Or x y -> showParen (p >= 1) $ go 1 x . showString "||" . go 1 y

  If c t f -> showParen (p >= 0) $ go 0 c . showChar '?' . go 0 t . showChar ':' . go 0 f

  ToUpperFn x -> showString "toupper(" . go 0 x . showChar ')'
  ToLowerFn x -> showString "tolower(" . go 0 x . showChar ')'

  LengthFn Nothing  -> showString "length()"
  LengthFn (Just x) -> showString "length(" . go 0 x . showChar ')'

  SubstrFn x y Nothing  -> showString "substr(" . go 0 x . showChar ',' . go 0 y . showChar ')'
  SubstrFn x y (Just z) -> showString "substr(" . go 0 x . showChar ',' . go 0 y . showChar ',' . go 0 z . showChar ')'

  MatchFn x y -> showString "match(" . go 0 x . showChar ',' . go 0 (ERE y) . showChar ')'

escapeString :: String -> String
escapeString = concatMap $ \c -> (if c `elem` "\\\"" then ('\\' :) else id) [c]

escapeERE :: String -> String
escapeERE = concatMap $ \c -> (if c `elem` "\\/" then ('\\' :) else id) [c]