module Patterns (patternTerm, patternExpr, makeFunction, flattenPatterns) where

import Ast
import Data.Char (isUpper)
import Control.Applicative ((<$>))
import Control.Monad
import Text.Parsec
import ParseLib

patternBasic :: Monad m => ParsecT [Char] u m Pattern
patternBasic =
    choice [ char '_' >> return PAnything
           , do x@(c:_) <- var
                if isUpper c then PData x <$> spacePrefix patternTerm
                             else return $ PVar x
           ]

patternTuple :: Monad m => ParsecT [Char] u m Pattern
patternTuple = do ps <- parens (commaSep patternExpr)
                  return $ case ps of { [p] -> p; _ -> ptuple ps }

patternList :: Monad m => ParsecT [Char] u m Pattern
patternList = plist <$> braces (commaSep patternExpr)

patternTerm :: Monad m => ParsecT [Char] u m Pattern
patternTerm = patternTuple <|> patternList <|> patternBasic <?> "pattern"

patternExpr :: Monad m => ParsecT [Char] u m Pattern
patternExpr = foldl1 pcons <$> consSep1 patternTerm <?> "pattern"



makeFunction args body = foldr func body args
    where func PAnything e = Lambda "_" e
          func (PVar x)  e = Lambda x e
          func p e = "t" `Lambda` Case (Var "t") [(p,e)]

flattenPatterns (PVar f : args) exp = return [(f, makeFunction args exp)]
flattenPatterns [p] exp = return $ matchSingle p exp p
flattenPatterns ps _ = 
    fail $ "Pattern (" ++ unwords (map show ps) ++
           ") cannot be used on the left-hand side of an assign statement."

matchSingle pat exp p@(PData _ ps) =
    (v, exp) : concatMap (matchSingle p $ Var v) ps
        where v = "'" ++ getName p
matchSingle pat exp (PVar x)  = [ (x, Case exp [(pat,Var x)]) ]
matchSingle pat exp PAnything = []

getName (PData n ps) = n ++ concatMap getName ps
getName (PAnything)  = "_"
getName (PVar x)     = x