{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE CPP #-}
module Text.Haiji.Syntax.Expression
       ( Expression(..)
       , expression
       , Expr(..)
       , External
       ) where

import Prelude hiding (filter)
#if MIN_VERSION_base(4,8,0)
#else
import Control.Applicative
#endif
import Data.Attoparsec.Text
import Data.Bool
import Data.List hiding (filter)
import Data.Scientific
import Text.Haiji.Syntax.Identifier
import Text.Haiji.Syntax.Filter

-- $setup
-- >>> import Control.Arrow (left)

data Z
data S a

type Level0 = Z
type Level1 = S Level0
type Level2 = S Level1
type Level3 = S Level2
type Level4 = S Level3
type Level5 = S Level4
type Level6 = S Level5
type Level7 = S Level6
type Level8 = S Level7

type LevelMax = Level8

data MulDiv = Mul | DivF | DivI | Mod deriving Eq

instance Show MulDiv where
  show Mul = "*"
  show DivF = "/"
  show DivI = "//"
  show Mod = "%"

data AddSub = Add | Sub deriving Eq

instance Show AddSub where
  show Add = "+"
  show Sub = "-"

data Internal
data External

data Expr visibility level where
  ExprLift :: Expr visibility lv -> Expr visibility (S lv)
  ExprIntegerLiteral :: Int -> Expr visibility Level0
  ExprBooleanLiteral :: Bool -> Expr visibility Level0
  ExprVariable :: Identifier -> Expr visibility Level0
  ExprParen :: Expr visibility LevelMax -> Expr visibility Level0
  ExprRange :: [Expr visibility LevelMax] -> Expr visibility Level0
  ExprAttributed :: Expr visibility Level0 -> [Identifier] -> Expr visibility Level1
  ExprFiltered :: Expr visibility Level1 -> [Filter] -> Expr visibility Level2
  ExprInternalPow :: Expr Internal Level2 -> [Expr Internal Level2] -> Expr Internal Level3
  ExprPow :: Expr External Level3 -> Expr External Level2 -> Expr External Level3
  ExprInternalMulDiv :: Expr Internal Level3 -> [(MulDiv, Expr Internal Level3)] -> Expr Internal Level4
  ExprMul :: Expr External Level4 -> Expr External Level3 -> Expr External Level4
  ExprDivF :: Expr External Level4 -> Expr External Level3 -> Expr External Level4
  ExprDivI :: Expr External Level4 -> Expr External Level3 -> Expr External Level4
  ExprMod :: Expr External Level4 -> Expr External Level3 -> Expr External Level4
  ExprInternalAddSub :: Expr Internal Level4 -> [(AddSub, Expr Internal Level4)] -> Expr Internal Level5
  ExprAdd :: Expr External Level5 -> Expr External Level4 -> Expr External Level5
  ExprSub :: Expr External Level5 -> Expr External Level4 -> Expr External Level5
  ExprEQ :: Expr visibility Level5 -> Expr visibility Level5 -> Expr visibility Level6
  ExprNEQ :: Expr visibility Level5 -> Expr visibility Level5 -> Expr visibility Level6
  ExprGT :: Expr visibility Level5 -> Expr visibility Level5 -> Expr visibility Level6
  ExprGE :: Expr visibility Level5 -> Expr visibility Level5 -> Expr visibility Level6
  ExprLT :: Expr visibility Level5 -> Expr visibility Level5 -> Expr visibility Level6
  ExprLE :: Expr visibility Level5 -> Expr visibility Level5 -> Expr visibility Level6
  ExprInternalAnd :: Expr Internal Level6 -> [Expr Internal Level6] -> Expr Internal Level7
  ExprAnd :: Expr External Level7 -> Expr External Level6 -> Expr External Level7
  ExprInternalOr :: Expr Internal Level7 -> [Expr Internal Level7] -> Expr Internal Level8
  ExprOr :: Expr External Level8 -> Expr External Level7 -> Expr External Level8

toExternal :: Expr Internal level -> Expr External level
toExternal (ExprLift e) = ExprLift $ toExternal e
toExternal (ExprIntegerLiteral n) = ExprIntegerLiteral n
toExternal (ExprBooleanLiteral b) = ExprBooleanLiteral b
toExternal (ExprVariable i) = ExprVariable i
toExternal (ExprParen e) = ExprParen $ toExternal e
toExternal (ExprRange args) = ExprRange $ map toExternal args
toExternal (ExprAttributed e attrs) = ExprAttributed (toExternal e) attrs
toExternal (ExprFiltered e filters) = ExprFiltered (toExternal e) filters
toExternal (ExprInternalPow e []) = ExprLift $ toExternal e
toExternal (ExprInternalPow e es) = ExprPow (toExternal (ExprInternalPow e $ init es)) (toExternal $ last es)
toExternal (ExprInternalMulDiv e []) = ExprLift $ toExternal e
toExternal (ExprInternalMulDiv e es) = case last es of
  (Mul , e') -> ExprMul  (toExternal (ExprInternalMulDiv e $ init es)) (toExternal e')
  (DivF, e') -> ExprDivF (toExternal (ExprInternalMulDiv e $ init es)) (toExternal e')
  (DivI, e') -> ExprDivI (toExternal (ExprInternalMulDiv e $ init es)) (toExternal e')
  (Mod , e') -> ExprMod  (toExternal (ExprInternalMulDiv e $ init es)) (toExternal e')
toExternal (ExprInternalAddSub e []) = ExprLift $ toExternal e
toExternal (ExprInternalAddSub e es) = case last es of
  (Add, e') -> ExprAdd (toExternal (ExprInternalAddSub e $ init es)) (toExternal e')
  (Sub, e') -> ExprSub (toExternal (ExprInternalAddSub e $ init es)) (toExternal e')
toExternal (ExprEQ e1 e2) = ExprEQ (toExternal e1) (toExternal e2)
toExternal (ExprNEQ e1 e2) = ExprNEQ (toExternal e1) (toExternal e2)
toExternal (ExprGT e1 e2) = ExprGT (toExternal e1) (toExternal e2)
toExternal (ExprGE e1 e2) = ExprGE (toExternal e1) (toExternal e2)
toExternal (ExprLT e1 e2) = ExprLT (toExternal e1) (toExternal e2)
toExternal (ExprLE e1 e2) = ExprLE (toExternal e1) (toExternal e2)
toExternal (ExprInternalAnd e []) = ExprLift $ toExternal e
toExternal (ExprInternalAnd e es) = ExprAnd (toExternal (ExprInternalAnd e $ init es)) (toExternal $ last es)
toExternal (ExprInternalOr e []) = ExprLift $ toExternal e
toExternal (ExprInternalOr e es) = ExprOr (toExternal (ExprInternalOr e $ init es)) (toExternal $ last es)

deriving instance Eq (Expr visibility level)

instance Show (Expr visibility phase) where
  show (ExprLift e) = show e
  show (ExprIntegerLiteral n) = show n
  show (ExprBooleanLiteral b) = if b then "true" else "false"
  show (ExprVariable v) = show v
  show (ExprParen e) = '(' : shows e ")"
  show (ExprRange args) = "range(" ++ intercalate ", " [ show e | e <- args ] ++ ")"
  show (ExprAttributed e attrs) = shows e $ concat [ '.' : show a | a <- attrs ]
  show (ExprFiltered v filters) = shows v $ filters >>= show
  show (ExprInternalPow e es) = intercalate " ** " $ map show $ e:es
  show (ExprPow e1 e2) = shows e1 " ** " ++ show e2
  show (ExprInternalMulDiv e es) = concat $ show e : concat [ [ ' ' : shows op " ", show e' ] | (op, e') <- es ]
  show (ExprMul e1 e2) = shows e1 " * " ++ show e2
  show (ExprDivF e1 e2) = shows e1 " / " ++ show e2
  show (ExprDivI e1 e2) = shows e1 " // " ++ show e2
  show (ExprMod e1 e2) = shows e1 " % " ++ show e2
  show (ExprInternalAddSub e es) = concat $ show e : concat [ [ ' ' : shows op " ", show e' ] | (op, e') <- es ]
  show (ExprAdd e1 e2) = shows e1 " + " ++ show e2
  show (ExprSub e1 e2) = shows e1 " - " ++ show e2
  show (ExprEQ e1 e2) = shows e1 " == " ++ show e2
  show (ExprNEQ e1 e2) = shows e1 " != " ++ show e2
  show (ExprGT e1 e2) = shows e1 " > " ++ show e2
  show (ExprGE e1 e2) = shows e1 " >= " ++ show e2
  show (ExprLT e1 e2) = shows e1 " < " ++ show e2
  show (ExprLE e1 e2) = shows e1 " <= " ++ show e2
  show (ExprInternalAnd e es) = intercalate " and " $ map show $ e:es
  show (ExprAnd e1 e2) = shows e1 " and " ++ show e2
  show (ExprInternalOr e es) = intercalate " or " $ map show $ e:es
  show (ExprOr e1 e2) = shows e1 " or " ++ show e2

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprIntegerLiteral
-- >>> eval "1"
-- Right 1
-- >>> eval "2"
-- Right 2
exprIntegerLiteral :: Parser (Expr Internal Level0)
exprIntegerLiteral = either (error . (show :: Double -> String)) ExprIntegerLiteral . floatingOrInteger <$> Data.Attoparsec.Text.scientific

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprBooleanLiteral
-- >>> eval "true"
-- Right true
-- >>> eval "false"
-- Right false
exprBooleanLiteral :: Parser (Expr Internal Level0)
exprBooleanLiteral = ExprBooleanLiteral <$> choice [ string "true" *> return True, string "false" *> return False ]

exprVariable :: Parser (Expr Internal Level0)
exprVariable = ExprVariable <$> identifier

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprParen
-- >>> eval "(1)"
-- Right (1)
-- >>> eval "(foo)"
-- Right (foo)
-- >>> eval "(true)"
-- Right (true)
-- >>> eval "(foo )"
-- Right (foo)
-- >>> eval "( foo)"
-- Right (foo)
exprParen :: Parser (Expr Internal Level0)
exprParen = ExprParen <$> (char '(' *> skipSpace *> exprLevelMax <* skipSpace <* char ')')

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprRange
-- >>> eval "range(1)"
-- Right range(1)
-- >>> eval "range (1)"
-- Right range(1)
-- >>> eval "range(1, 2)"
-- Right range(1, 2)
-- >>> eval "range (1,2)"
-- Right range(1, 2)
-- >>> eval "range(1 ,2 , 3)"
-- Right range(1, 2, 3)
exprRange :: Parser (Expr Internal Level0)
exprRange = ExprRange <$> args where
  args = do
    es <- string "range" *> skipSpace *> char '(' *> skipSpace *> (exprLevelMax `sepBy1` (skipSpace *> char ',' *> skipSpace)) <* skipSpace <* char ')'
    bool (fail "too many args") (return es) $ length es < 4

exprLevel0 :: Parser (Expr Internal Level0)
exprLevel0 = choice [ exprIntegerLiteral
                    , exprBooleanLiteral
                    , exprRange
                    , exprVariable
                    , exprParen
                    ]

exprAttributed :: Parser (Expr Internal Level1)
exprAttributed = ExprAttributed <$> exprLevel0 <*> many' (skipSpace *> char '.' *> skipSpace *> identifier)

exprLevel1 :: Parser (Expr Internal Level1)
exprLevel1 = choice [ exprAttributed
                    ]

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprFiltered
-- >>> eval "foo|abs"
-- Right foo|abs
-- >>> eval "foo| abs"
-- Right foo|abs
-- >>> eval "foo |abs"
-- Right foo|abs
-- >>> eval "foo | abs"
-- Right foo|abs
exprFiltered :: Parser (Expr Internal Level2)
exprFiltered = ExprFiltered <$> exprLevel1 <*> many' (skipSpace *> filter)

exprLevel2 :: Parser (Expr Internal Level2)
exprLevel2 = choice [ exprFiltered
                    ]

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprPow
-- >>> eval "1**2"
-- Right 1 ** 2
-- >>> eval "1**  2"
-- Right 1 ** 2
-- >>> eval "1  **2"
-- Right 1 ** 2
exprPow :: Parser (Expr Internal Level3)
exprPow = ExprInternalPow <$> exprLevel2 <*> many' (skipSpace *> string "**" *> skipSpace *> exprLevel2)

exprLevel3 :: Parser (Expr Internal Level3)
exprLevel3 = choice [ exprPow
                    ]

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprMulDiv
-- >>> eval "1*2//3"
-- Right 1 * 2 // 3
-- >>> eval "1 * 2 // 3"
-- Right 1 * 2 // 3
-- >>> eval "1//2*3"
-- Right 1 // 2 * 3
-- >>> eval "1*2/3"
-- Right 1 * 2 / 3
-- >>> eval "1 * 2 % 3"
-- Right 1 * 2 % 3
-- >>> eval "1%2*3"
-- Right 1 % 2 * 3
exprMulDiv :: Parser (Expr Internal Level4)
exprMulDiv = ExprInternalMulDiv <$> exprLevel3 <*> many' ((,) <$> (skipSpace *> op) <*> (skipSpace *> exprLevel3)) where
  op = choice [ string "//" *> return DivI
              , string "/" *> return DivF
              , string "*" *> return Mul
              , string "%" *> return Mod
              ]

exprLevel4 :: Parser (Expr Internal Level4)
exprLevel4 = choice [ exprMulDiv
                    ]

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprAddSub
-- >>> eval "1+2-3"
-- Right 1 + 2 - 3
-- >>> eval "1 + 2 - 3"
-- Right 1 + 2 - 3
-- >>> eval "1-2+3"
-- Right 1 - 2 + 3
exprAddSub :: Parser (Expr Internal Level5)
exprAddSub = ExprInternalAddSub <$> exprLevel4 <*> many' ((,) <$> (skipSpace *> op) <*> (skipSpace *> exprLevel4)) where
  op = choice [ string "+" *> return Add
              , string "-" *> return Sub
              ]

exprLevel5 :: Parser (Expr Internal Level5)
exprLevel5 = choice [ exprAddSub
                    ]

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprEQ
-- >>> eval "1==1"
-- Right 1 == 1
-- >>> eval "1== 1"
-- Right 1 == 1
-- >>> eval "1 ==1"
-- Right 1 == 1
exprEQ :: Parser (Expr Internal Level6)
exprEQ = ExprEQ <$> exprLevel5 <*> (skipSpace *> string "==" *> skipSpace *> exprLevel5)

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprNEQ
-- >>> eval "1!=1"
-- Right 1 != 1
-- >>> eval "1!= 1"
-- Right 1 != 1
-- >>> eval "1 !=1"
-- Right 1 != 1
exprNEQ :: Parser (Expr Internal Level6)
exprNEQ = ExprNEQ <$> exprLevel5 <*> (skipSpace *> string "!=" *> skipSpace *> exprLevel5)

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprGT
-- >>> eval "1>1"
-- Right 1 > 1
-- >>> eval "1> 1"
-- Right 1 > 1
-- >>> eval "1 >1"
-- Right 1 > 1
exprGT :: Parser (Expr Internal Level6)
exprGT = ExprGT <$> exprLevel5 <*> (skipSpace *> string ">" *> skipSpace *> exprLevel5)

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprGE
-- >>> eval "1>=1"
-- Right 1 >= 1
-- >>> eval "1>= 1"
-- Right 1 >= 1
-- >>> eval "1 >=1"
-- Right 1 >= 1
exprGE :: Parser (Expr Internal Level6)
exprGE = ExprGE <$> exprLevel5 <*> (skipSpace *> string ">=" *> skipSpace *> exprLevel5)

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprLT
-- >>> eval "1<1"
-- Right 1 < 1
-- >>> eval "1< 1"
-- Right 1 < 1
-- >>> eval "1 <1"
-- Right 1 < 1
exprLT :: Parser (Expr Internal Level6)
exprLT = ExprLT <$> exprLevel5 <*> (skipSpace *> string "<" *> skipSpace *> exprLevel5)

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprLE
-- >>> eval "1<=1"
-- Right 1 <= 1
-- >>> eval "1<= 1"
-- Right 1 <= 1
-- >>> eval "1 <=1"
-- Right 1 <= 1
exprLE :: Parser (Expr Internal Level6)
exprLE = ExprLE <$> exprLevel5 <*> (skipSpace *> string "<=" *> skipSpace *> exprLevel5)

exprLevel6 :: Parser (Expr Internal Level6)
exprLevel6 = choice [ exprEQ
                    , exprNEQ
                    , exprGE
                    , exprGT
                    , exprLE
                    , exprLT
                    , ExprLift <$> exprLevel5
                    ]

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprAnd
-- >>> eval "true and false"
-- Right true and false
exprAnd :: Parser (Expr Internal Level7)
exprAnd = ExprInternalAnd <$> exprLevel6 <*> many' (skipSpace *> string "and" *> skipSpace *> exprLevel6)

exprLevel7 :: Parser (Expr Internal Level7)
exprLevel7 = choice [ exprAnd
                    , ExprLift <$> exprLevel6
                    ]

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly exprOr
-- >>> eval "true or false"
-- Right true or false
exprOr :: Parser (Expr Internal Level8)
exprOr = ExprInternalOr <$> exprLevel7 <*> many' (skipSpace *> string "or" *> skipSpace *> exprLevel7)

exprLevel8 :: Parser (Expr Internal Level8)
exprLevel8 = choice [ exprOr
                    , ExprLift <$> exprLevel7
                    ]

exprLevelMax :: Parser (Expr Internal LevelMax)
exprLevelMax = exprLevel8

newtype Expression = Expression (Expr External LevelMax) deriving Eq

instance Show Expression where
  show (Expression e) = show e

-- |
--
-- >>> let eval = left (const "parse error") . parseOnly expression
-- >>> eval "foo"
-- Right foo
-- >>> eval "(foo)"
-- Right (foo)
-- >>> eval "1"
-- Right 1
-- >>> eval "true"
-- Right true
-- >>> eval "false"
-- Right false
-- >>> eval "foo.bar"
-- Right foo.bar
-- >>> eval "(foo).bar"
-- Right (foo).bar
-- >>> eval "(foo.bar)"
-- Right (foo.bar)
-- >>> eval "foo.b}}ar"
-- Right foo.b
-- >>> eval "foo.b ar"
-- Right foo.b
-- >>> eval "foo.b }ar"
-- Right foo.b
-- >>> eval " foo.bar"
-- Left "parse error"
-- >>> eval "foo.  bar"
-- Right foo.bar
-- >>> eval "foo  .bar"
-- Right foo.bar
-- >>> eval "foo.bar  "
-- Right foo.bar
-- >>> eval "foo.bar  "
-- Right foo.bar
-- >>> eval "foo.bar.baz"
-- Right foo.bar.baz
--
expression :: Parser Expression
expression = Expression . toExternal <$> exprLevelMax