-- | Command line manager
-- This manager will listen to the standard input as soon as the MVar token is filled.
-- Then, it will fill another MVar (input) with the parsed command.
-- Autocompletion is supported throught haskeline configuration.
module Shaker.Cli(
  getInput
  ,listActions
  ,InputState(..)
)
 where

import Control.Concurrent
import Control.Monad.Reader
import Data.Char
import Data.List
import qualified Data.Map as M
import Shaker.CommonUtil
import Shaker.Parser
import Shaker.Type
import System.Console.Haskeline

-- | Listen to keyboard input and parse command
getInput :: Shaker IO( IO() )
getInput = do
  shIn <- ask
  return $ runInputT (myDefaultSettings shIn) $ withInterrupt $ processInput shIn

-- | Execute the entered command
processInput :: ShakerInput -> InputT IO()
processInput shIn  = do
  let (InputState inputMv tokenMv) = shakerInputState shIn
  _ <- lift $ takeMVar tokenMv
  minput <-  handleInterrupt (return (Just "quit"))
               $ getInputLine "% "
  case minput of
     Nothing -> lift $ tryPutMVar inputMv (Just exitCommand ) >> return ()
     Just str -> either error_action normal_action (parseCommand str shIn)
                 where error_action err = lift $ print err >> tryPutMVar inputMv Nothing >> return()
                       normal_action val = lift $ tryPutMVar inputMv (Just val) >> return()

-- * Auto-completion management

-- | Settings for haskeline
myDefaultSettings :: MonadIO m => ShakerInput-> Settings m
myDefaultSettings shIn = Settings {
  complete = completeAction shIn,
  historyFile = Just ".haskelineHistory",
  autoAddHistory = True
}

completeAction :: Monad m => ShakerInput -> CompletionFunc m
completeAction shIn = completeWord (Just '\\') "\"'~" (listActions shIn)

listActions :: Monad m => ShakerInput -> String -> m [Completion]
listActions shIn str = return $ autocompleteFunction (shakerCommandMap shIn) str

autocompleteFunction :: CommandMap  -> String -> [Completion]
autocompleteFunction cmdMap [] = map simpleCompletion $ M.keys cmdMap
autocompleteFunction cmdMap cliInput = map simpleCompletion  compleListProp
  where inpWords = (words . map toLower) cliInput
        lastWord = last inpWords
        listProp = filter (lastWord `isPrefixOf`) $ M.keys cmdMap
        commonPref = unwords (init inpWords)
        compleListProp = trimList $ map  (\a -> commonPref ++ " " ++ a) listProp