{-
 - 
 -  Copyright 2005-2007, Robert Dockins.
 -  
 -}

{- | This module defines the Shellac interface for shell backends.  A shell backend
     is required to provide sensible implementations for 'outputString', 'flushOutput',
     'getSingleChar', 'getInput', and 'getWordBreakChars'.  All other operations may 
     be noops (however, they must not denote bottom!).  
-}

module System.Console.Shell.Backend
( CompletionFunction
, BackendOutput (..)
, ShellBackend (..)
, defaultWordBreakChars
, templateBackend
) where


-- | The type of completion functions.  The argument is a triple
--   consisting of (before,word,after), where \'word\' is a string
--   of non-word-break characters which contains the cursor position.
--   \'before\' is all characters on the line before \'word\' and \'after\'
--   is all characters on the line after word.  The return value should
--   be \'Nothing\' if no completions can be generated, or
--   \'Just (newWord,completions)\' if completions can be generated.  \'newWord\'
--   is a new string to replace \'word\' on the command line and \'completions\'
--   is a list of all possible completions of \'word\'.  To achieve the standard
--   \"complete-as-far-as-possible\" behavior, \'newWord\' should be the longest possible
--   prefix of all words in \'completions\'.

type CompletionFunction = (String,String,String) 
                        -> IO (Maybe (String, [String]))

-- | A datatype representing ouput to be printed.  The different categories of
--   output are distinguished to that shell backends can, for example, apply
--   different colors or send output to different places (stderr versus stdout).

data BackendOutput
   = RegularOutput String      -- ^ The most regular way to produce output
   | InfoOutput String         -- ^ An informative output string, such as command help
   | ErrorOutput String        -- ^ An string generated by an error


-- | This record type contains all the functions that Shellac allows the pluggable
--   backend to provide.  Most of these operations are optional andn relate to
--   advanced features like command completion and history.  However, a shell backend
--   is required to provide sensible implementations for 'outputString', 'flushOutput',
--   'getSingleChar', 'getInput', and 'getWordBreakChars'.

data ShellBackend bst
   = ShBackend
     { initBackend                    :: IO bst
         -- ^ Provides the backend a way to perform any necessary initilization
	 --   before the shell starts.  This function is called once for each
         --   shell instance.  The generated value will be passed back in to each call of the
         --   other methods in this record.

     , shutdownBackend                :: bst -> IO ()
         -- ^ Called when the shell exits to allow the backend to perform any necessary
         --   cleanup actions.

     , outputString                   :: bst -> BackendOutput -> IO ()
         -- ^ Causes the string to be sent to the underlying console device.

     , flushOutput                    :: bst -> IO ()
         -- ^ Perform any operations necessary to clear any output buffers.  After this
         --   operation, the user should be able to view any output sent to this backend.

     , getSingleChar                  :: bst -> String -> IO (Maybe Char)
         -- ^ Retrive a single character from the user without waiting for carriage return.

     , getInput                       :: bst -> String -> IO (Maybe String)
         -- ^ Print the prompt and retrive a line of input from the user.

     , addHistory                     :: bst -> String -> IO ()
         -- ^ Add a string to the history list.

     , setWordBreakChars              :: bst -> String -> IO ()
         -- ^ Set the characters which define word boundaries.  This is mostly used
         --   for defining where completions occur.

     , getWordBreakChars              :: bst -> IO String
         -- ^ Get the current set of word break characters.

     , onCancel                       :: bst -> IO ()
         -- ^ A callback to run whenever evaluation or a command is cancled
         --   by the keyboard signal

     , setAttemptedCompletionFunction :: bst -> CompletionFunction -> IO ()
         -- ^ A completion function that is tried first.

     , setDefaultCompletionFunction   :: bst -> Maybe (String -> IO [String]) -> IO ()
         -- ^ An alternate function to generate completions.  The function given takes the
         --   word as an argument and generates all possible completions.  This function is called
         --   (if set) after the attemptedCompletionFunction if it returns \'Nothing\'.

     , completeFilename               :: bst -> String -> IO [String]
         -- ^ A backend-provided method to complete filenames.

     , completeUsername               :: bst -> String -> IO [String]
         -- ^ A backend-provided method to complete usernames.

     , clearHistoryState              :: bst -> IO ()
         -- ^ An operation to clear the history buffer.

     , setMaxHistoryEntries           :: bst -> Int -> IO ()
         -- ^ Sets the maximum number of entries managed by the history buffer.

     , getMaxHistoryEntries           :: bst -> IO Int
         -- ^ Gets the maximum number of entries managed by the history buffer.

     , readHistory                    :: bst -> FilePath -> IO ()
         -- ^ Read the history buffer from a file.  The file should be formatted
         --   as plain-text, with each line in the file representing a single command
         --   entered, most recent commands at the bottom. (This format is what readline
         --   produces)

     , writeHistory                   :: bst -> FilePath -> IO ()
         -- ^ Write the history buffer to a file.  The file should be formatted in the
         --   same way as in the description for 'readHistory'.
     }

-- | Provides a sane default set of characters to use when breaking
--   lines into \'words\'.  If a backend does not have configuratble
--   word break characters, the 'getWordBreakCharacters' can just
--   return this default set.
defaultWordBreakChars :: [Char]
defaultWordBreakChars = " \t\n\r\v`~!@#$%^&*()=[]{};\\\'\",<>"

-- | This backend template is useful for defining custom backends.
--   The idea is that you will use 'templateBackend' to generate a
--   bare-bones backend implemenation and only fill in the methods
--   that you wish to define using the record update syntax.
--   The parameter to 'templateBackend'
--   becomes the backend state associated with the backend and is
--   passed into to each of the operation methods.
templateBackend :: a -> ShellBackend a
templateBackend bst = ShBackend
     { initBackend                    = return bst
     , shutdownBackend                = \_ -> return ()
     , outputString                   = \_ _ -> return ()
     , flushOutput                    = \_ -> return ()
     , getSingleChar                  = \_ _ -> return Nothing
     , getInput                       = \_ _ -> return Nothing
     , addHistory                     = \_ _ -> return ()
     , setWordBreakChars              = \_ _ -> return ()
     , getWordBreakChars              = \_ -> return defaultWordBreakChars
     , onCancel                       = \_ -> return ()
     , setAttemptedCompletionFunction = \_ _ -> return ()
     , setDefaultCompletionFunction   = \_ _ -> return ()
     , completeFilename               = \_ _ -> return []
     , completeUsername               = \_ _ -> return []
     , clearHistoryState              = \_ -> return ()
     , setMaxHistoryEntries           = \_ _ -> return ()
     , getMaxHistoryEntries           = \_ -> return 0
     , readHistory                    = \_ _ -> return ()
     , writeHistory                   = \_ _ -> return ()
     }
