{-# LANGUAGE DataKinds #-}

module Main (main) where

import           Control.Monad
import           Control.Monad.Trans
import           Data.Version                 (showVersion)
import           Language.Haskell.Interpreter hiding (get)
import           Options.Declarative
import           System.IO

import           Evaluator
import           Paths_hoe                    (version)

imports :: [String]
imports =
    [ "Prelude"

      -- from base
    , "Control.Applicative"
    , "Control.Arrow"
    , "Control.Monad"
    , "Data.Bits"
    , "Data.Char"
    , "Data.Complex"
    , "Data.Either"
    , "Data.Function"
    , "Data.List"
    , "Data.Maybe"
    , "Data.Monoid"
    , "Data.Ord"
    , "Data.Ratio"
    , "Numeric"
    , "System.IO"
    , "System.IO.Unsafe"
    , "System.Info"
    , "System.Random"
    , "Text.Printf"

      -- other common modules
    , "Data.List.Split"  -- from split
    , "Data.Time"        -- from time
    , "Text.Regex.Posix" -- from regex-posix
    ]

hoe :: Flag "i" '["inplace"] "EXT" "Edit files in-place (make backup if EXT is not null)" (Maybe String)
    -> Arg "SCRIPT" String
    -> Arg "[FILES]" [String]
    -> Flag "m" '["mod"] "MODULES" "Import modules before running the script" (Def "" String)
    -> Cmd "hoe: Haskell One-liner Evaluator" ()
hoe inplace script files modules = do
    compiled <- liftIO $ runInterpreter $ do
        reset
        setImportsQ $
            [ (m, Nothing) | m <- imports ] ++
            [ (m, Nothing) | m <- words $ get modules ]
        set [ installedModulesInScope := True ]
        compile $ get script

    case compiled of
        Left (WontCompile errs) ->
            liftIO $ hPutStr stderr $ "compile error: " ++ unlines (map errMsg errs)
        Left (UnknownError msg) ->
            liftIO $ hPutStrLn stderr msg
        Left err ->
            liftIO $ hPrint stderr err

        Right (ty, descr, f) -> do
            logStr 1 $ "Interpret as: " ++ ty ++ " :: " ++ descr
            liftIO $ exec (get files) (get inplace) f

exec :: [String] -> Maybe String -> Script -> IO ()
exec [] _ f = putStr =<< f =<< getContents
exec files mbext f =
    forM_ files $ \file -> do
        s <- readFile file
        case mbext of
            Nothing -> putStr =<< f s
            Just ext -> do
                when (ext /= "") $ writeFile (file ++ "." ++ ext) s
                length s `seq ` writeFile file =<< f s

main :: IO ()
main = run "hoe" (Just $ showVersion version) hoe
