module HsDev.Tools.Base (
	Result, ToolM,
	runWait, runWait_,
	tool, tool_,
	match,
	at,
	inspect,
	-- * Read parse utils
	ReadM,
	readParse, parseReads, parseRead
	) where

import Control.Monad.Error
import Control.Monad.State
import Data.Maybe (fromMaybe, listToMaybe)
import System.Exit
import System.Process
import Text.RegexPR (matchRegexPR)

import HsDev.Symbols
import HsDev.Util (liftIOErrors)

type Result = Either String String
type ToolM a = ErrorT String IO a

-- | Run command and wait for result
runWait :: FilePath -> [String] -> String -> IO Result
runWait name args input = do
	(code, out, err) <- readProcessWithExitCode name args input
	return $ if code == ExitSuccess && not (null out) then Right out else Left err

-- | Run command with no input
runWait_ :: FilePath -> [String] -> IO Result
runWait_ name args = runWait name args ""

-- | Tool
tool :: FilePath -> [String] -> String -> ToolM String
tool name args input = liftIOErrors $ ErrorT $ runWait name args input

-- | Tool with no input
tool_ :: FilePath -> [String] -> ToolM String
tool_ name args = tool name args ""

match :: String -> String -> Maybe (Int -> Maybe String)
match pat str = do
	(_, groups) <- matchRegexPR pat str
	return $ \i -> lookup i groups

at :: (Int -> Maybe String) -> Int -> String
at g i = fromMaybe (error $ "Can't find group " ++ show i) $ g i

inspect :: Monad m => ModuleLocation -> ErrorT String m Inspection -> ErrorT String m Module -> ErrorT String m InspectedModule
inspect mloc insp act = lift $ execStateT inspect' (Inspected InspectionNone mloc (Left "not inspected")) where
	inspect' = runErrorT $ do
		i <- mapErrorT lift insp
		modify (\im -> im { inspection = i })
		v <- mapErrorT lift act
		modify (\im -> im { inspectionResult = Right v })
		`catchError`
		\e -> modify (\im -> im { inspectionResult = Left e })

type ReadM a = StateT String [] a

-- | Parse readable value
readParse :: Read a => ReadM a
readParse = StateT reads

-- | Run parser
parseReads :: String -> ReadM a -> [a]
parseReads = flip evalStateT

-- | Run parser and select first result
parseRead :: String -> ReadM a -> Maybe a
parseRead s = listToMaybe . parseReads s