module Language.CalDims.Misc (parseLine) where

import Text.ParserCombinators.Parsec
import qualified Data.List as List
import Control.Monad

import Language.CalDims.Expression
import Language.CalDims.Expr ()
import Language.CalDims.Action
import Language.CalDims.Types


eof' :: MyParser ()
eof' = do
	spaces
	eof

parseConversion :: MyParser Conversion
parseConversion = do
	spaces
	char '|'
	spaces
	c <- try (pr parseEinh Explicit) <|> try (pr parseExpr InTermsOf) <|>
		(char '-' >> return Basic) <|>
		(char '*' >> return Keep) <|>
		(char '?' >> return Minimal)
	spaces
	return c
	where pr f rc = f >>= return . rc

parseFilename :: MyParser String
parseFilename = many1 (oneOf (['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ ['_', '.', '-']))

parseShort :: String -> MyParser String
parseShort s = do
	res <- string $ "\\" ++ s
	(char ' ' >> return ()) <|> eof
	spaces
	return res

parseGetState, parseWriteState, parseDebugName, parseDebugExpr, parseDebugDependencies, parseEval,  parseHelp, parseRemove, parseRemoveCascade, parseEcho, parseAddUnit, parseAddBasicUnit, parseAddFunction :: MyParser Command

parseComment, parseEmpty :: MyParser ()

parseGetState = do
	parseShort "s"
	eof'
	return GetState

parseWriteState = do
	parseShort "s"
	name <- parseFilename
	eof'
	return $ WriteState name

parseDebugExpr = do
	try (parseShort "de") <|> parseShort "d"
	e <- parseExpr
	eof'
	return $ DebugExpr e

parseDebugName = do
	parseShort "dn"
	n <- parseName
	eof'
	return $ DebugName n

parseDebugDependencies = do
	parseShort "dp"
	n <- parseName
	eof'
	return $ DebugDependencies n	

parseEval = do
	e <- parseExpr
	conversion <- option Keep parseConversion
	eof'
	return $ Eval e conversion

parseComment = do
	spaces
	char '#'
	flush
	return ()

parseEmpty = do
	spaces
	eof'
	return ()

parseHelp = do
	parseShort "?"
	eof'
	return Help

parseRemove = do
	parseShort "r"
	name <- parseName
	eof'
	return $ Remove name

parseRemoveCascade = do
	parseShort "rc"
	name <- parseName
	eof'
	return $ RemoveCascade name

parseEcho = do
	parseShort "p"
	s <- many1 anyChar
	eof'
	return $ Echo s

parseAddBasicUnit = do
	parseShort "u"
	name <- parseName
	eof'
	return $ AddBasicUnit name

parseAddUnit = do
	fn <- parseName
	parseBindE
	expr <- parseNonRecursiveExpr fn
	return $ AddUnit fn expr

parseAddFunction = do
	oState <- getState
	fn <- parseName
	args' <- option [] (brackets (sepBy (do
		n <- parseName
		e <- option noDims (do
			parseColon
			parseEinh)
		return $ Arg (unName n) 0 e) parseComma))
	let args = zipWith (\ (Arg s _ d) n -> Arg s n d) args' [0..]
	parseBind
	updateState (\x -> x {getArgs = args, getArgValues = map (\ (Arg _ _ d) -> (undefined, d)) args})
	expr <- parseNonRecursiveExpr fn
	when (getArgRefs expr /= (List.nub . List.sort $ args)) (fail "one/some of the parameters are not used in the expression")
	updateState (\_ -> oState)
	return $ AddFunction fn  args  expr

getArgRefs', getArgRefs :: Expr -> [Arg]

getArgRefs = List.nub . List.sort . getArgRefs'

getArgRefs' (Bin _ e1 e2) = getArgRefs e1 ++ getArgRefs e2
getArgRefs' (Uni _ e) = getArgRefs e
getArgRefs' (ArgRef r) = [r]
getArgRefs' (Call _ es) = foldl (++) [] (map getArgRefs es)
getArgRefs' (Evaled _) = []

parseLine :: MyParser (Maybe Command)
parseLine  = do

	let all_ =
		map try 
		[ parseGetState
		, parseWriteState
		, parseDebugName
		, parseDebugExpr
		, parseDebugDependencies
		, parseHelp
		, parseRemove
		, parseRemoveCascade
		, parseEcho
		, parseAddBasicUnit
		, parseEval
		, parseAddUnit
		, parseAddFunction

		]
	spaces
	command <- (do parseComment <|> parseEmpty; return Nothing) <|> (do res <- foldl1 (<|>) all_; return $ Just res)
	eof'
	return command