module Erb.Parser where

import Text.Parsec.String
import Text.Parsec.Prim
import Text.Parsec.Char
import Text.Parsec.Combinator
import Text.Parsec.Language (emptyDef)
import Erb.Ruby
import Text.Parsec.Expr
import qualified Text.Parsec.Token as P


def = emptyDef
    { P.commentStart   = "/*"
    , P.commentEnd     = "*/"
    , P.commentLine    = "#"
    , P.nestedComments = True
    , P.identStart     = letter
    , P.identLetter    = alphaNum <|> oneOf "_"
    , P.reservedNames  = ["if", "else", "case", "elsif"]
    , P.reservedOpNames= ["=>","=","+","-","/","*","+>","->","~>","!"]
    , P.caseSensitive  = True
    }

lexer       = P.makeTokenParser def
parens      = P.parens lexer
braces      = P.braces lexer
operator    = P.operator lexer
symbol      = P.symbol lexer
reservedOp  = P.reservedOp lexer
whiteSpace  = P.whiteSpace lexer
naturalOrFloat     = P.naturalOrFloat lexer
identifier  = P.identifier lexer

rubyexpression = buildExpressionParser table term <?> "expression"
        
table =     [ [ Infix ( reservedOp "+" >> return PlusOperation ) AssocLeft 
              , Infix ( reservedOp "-" >> return MinusOperation ) AssocLeft ]
            , [ Infix ( reservedOp "/" >> return DivOperation ) AssocLeft 
              , Infix ( reservedOp "*" >> return MultiplyOperation ) AssocLeft ]
            , [ Infix ( reservedOp "<<" >> return ShiftLeftOperation ) AssocLeft 
              , Infix ( reservedOp ">>" >> return ShiftRightOperation ) AssocLeft ]
            , [ Infix ( reservedOp "and" >> return AndOperation ) AssocLeft 
              , Infix ( reservedOp "or" >> return OrOperation ) AssocLeft ]
            , [ Infix ( reservedOp "==" >> return EqualOperation ) AssocLeft 
              , Infix ( reservedOp "!=" >> return DifferentOperation ) AssocLeft ]
            , [ Infix ( reservedOp ">" >> return AboveOperation ) AssocLeft 
              , Infix ( reservedOp ">=" >> return AboveEqualOperation ) AssocLeft
              , Infix ( reservedOp "<=" >> return UnderEqualOperation ) AssocLeft 
              , Infix ( reservedOp "<" >> return UnderOperation ) AssocLeft ]
            , [ Infix ( reservedOp "=~" >> return RegexpOperation ) AssocLeft 
              , Infix ( reservedOp "!~" >> return NotRegexpOperation ) AssocLeft ]
            , [ Prefix ( symbol "!" >> return NotOperation ) ]
            , [ Prefix ( symbol "-" >> return NegOperation ) ]
            , [ Infix ( reservedOp "?" >> return ConditionalValue ) AssocLeft ]
            , [ Infix ( reservedOp "." >> return MethodCall ) AssocLeft ]
            ]
term
    =   parens rubyexpression
    <|> objectterm
    <|> variablereference

blockinfo = many1 $ noneOf "}"

objectterm = do
    methodname <- identifier >>= return . Value . Literal
    isblock <- optionMaybe (symbol "{")
    case isblock of
        Just _ -> do
            b <- blockinfo
            symbol "}"
            return $ MethodCall methodname (BlockOperation b)
        Nothing -> return $ Object methodname

variablereference = identifier >>= return . Object . Value . Literal

rubystatement = rubyexpression >>= return . Puts

textblock :: Parser [RubyStatement]
textblock = do
    s <- many (noneOf "<")
    let returned = Puts $ Value $ Literal s
    isend <- optionMaybe eof
    case isend of
        Just _  -> return [returned]
        Nothing -> do
            char '<'
            isrub <- optionMaybe (char '%')
            let nextrun = case isrub of
                    Just _  -> rubyblock
                    Nothing -> textblock
            n <- nextrun
            return (returned : n)


rubyblock :: Parser [RubyStatement]
rubyblock = do
    isequal <- optionMaybe (char '=')
    parsed <- case isequal of
        Just _  -> spaces >> rubyexpression >>= return . Puts
        Nothing -> spaces >> rubystatement
    string "%>"
    n <- textblock
    return (parsed : n)

erbparser :: Parser [RubyStatement]
erbparser = textblock

parseErbFile fname = do
    input <- readFile fname
    return (runParser erbparser () fname input)