{-# LANGUAGE MultiParamTypeClasses,NoMonomorphismRestriction,FlexibleContexts,FlexibleInstances,UndecidableInstances #-}

-- | Main datas and types for the editor
module Editor
where

import Control.Monad.State
import Control.Monad.Reader
import Control.Monad.Error

import Undo
import Engine


-- | Stato is parametrized on an Engine instance and hold the engine with the last regex entered , regex G and g are not implemented now
data Stato = Stato {
	file 		:: Engine,			-- ^ data holding the file 
	lastre 		:: String, 		-- ^ a regex
	filename	:: Maybe String,	-- ^ the file we are editing
	pending		:: Maybe Command,	-- ^ a sensible state for data lost
	lastsaved	:: Maybe Engine
	} deriving (Show,Eq)

zeroState = Stato empty "" Nothing Nothing Nothing
-- | the core editor runs under the state monad with state (Stato) .
-- Wrapped around a monad (IO mainly) to permit console input and output of commands with IO
-- and testing with State
type StatoE m = UndoT Stato m

liftStatoE	:: Ctx m => StatoE m a -> Editor m a
liftStatoE 	= lift  

-- | push a new file (data 'Engine' instance) in the core State, pushing the old state in the undo stack
hputfile 	:: Ctx m => Engine -> Editor m ()
hputfile x 	= get >>= \y -> liftStatoE $ hput y {file = x}

putfile x 	= get >>= \y -> put y {file = x}
putlastre x	= get >>= \y -> put y {lastre = x} 
setfilename x	= get >>= \y -> put y {filename = x} 
setpending x	= get >>= \y -> put y {pending = x} 
setlastsaved 	= get >>= \y -> put y {lastsaved = Just (file y)} 
unsetlastsaved	= get >>= \y -> put y {lastsaved = Nothing}


	

-- | placeholder for the two constraints
class (SIO m, HCtx m (Stato) ) => Ctx m 
instance (SIO m, HCtx m (Stato) ) => Ctx m 

-- | the errors (monad failers) which can break the monad flow
data Err 
	= StopErr 		-- ^ issued on ctrl-d or q command (q not implemented)
	| ParserErr String 	-- ^ command line was not parsed to a CompleteCommand
	| RegexUnmatched	-- ^ the regex doesn't match a line
	| EvalErr Err	 	-- ^ something bad happened in the evaluation process
	| BackendErr 		-- ^ lines were addressed out of file (see 'Engine')
	| Ahi String 		-- ^ uncontrolled errors
	| FileReadErr String	-- ^ io error trying to load a file
	| FileNameMissing	-- ^ filename is not set
	| FileWriteErr String	-- ^ io error trying to write the file
	| ExternalCommandErr String -- ^ io error executing an external program
	| PendingState Command 	-- ^ a sensible data discarding command has been entered
	| NoMoreUndo		-- ^ reached the first state remembered
	| NoMoreRedo		-- ^ reached the last state remembered
	| CommandHelpMissing	-- ^ a help for a missing command was asked
	| CommandHelpParseErr String  	-- ^ error parsing the help for commands
	deriving Show	

instance Error Err where
	noMsg 	= Ahi "nomsg"
	strMsg 	= Ahi

-- | a layer for IO simulation, see "Main" for the real program one and "Test" for tests
class (Monad m) => SIO m where
	-- | accepts a prompt and should return Nothing on eof else a line of input
	inputSio 	:: String -> m (Maybe String) 	
	outputSio 	:: String -> m ()		-- ^ output a normal string
	historySio 	:: String -> m ()		-- ^ put a line in the history (which is global)
	errorSIO 	:: String -> m ()		-- ^ output an error string
	readfileSio	:: String -> ErrorT String m String			-- ^ read a file
	writefileSio	:: String -> String -> ErrorT String m ()		-- ^ write a file
	-- | runs an external command , first arg is the command
	-- the output is returned or an error is signalled in the errort monad
	externalSio 	:: String  	  -> ErrorT String m String
	-- the path for the command help file
	commandhelpSIO	:: m FilePath

liftSio :: Ctx m => m a -> Editor m a
liftSio = lift . lift 

-- | commands for the editor	
data Command
	-- | get some text and add it after the addressed line
	= Append 
	-- | get some text and add it before the addressed line
	| Insert 
	-- | get some text and add it in place of some deleted lines
	| Change 
	-- | delete some lines
	| Delete 
	-- | print some lines
	| Print 
	-- | get some commands and  execute them on each line matching a regex
	| SmallG String 	-- not implemented
	-- | interactively execute commands on each line matching a regex
	| BigG String		-- not implemented
	-- | Change the addressed line
	| NoCommand
	-- | Load a file 
	| Edit String
	-- | Write the file
	| Write
	-- | Write a new file
	| WriteNew String 
	-- | Set filename
	| SetFilename String
	-- | Print filename
	| GetFilename
	-- | Load the output of an external command
	| EditExternal String
	-- | Revert the last change if ever
	| UndoChange
	-- | Restore via the last change
	| RedoChange
	-- | Asking help
	| HelpList
	-- | Spedific help
	| HelpTopic String
	deriving (Show,Eq)


-- | represents a line position in the file
data Offset 
	-- | beyond last line, the append line
	= LastLine 
	-- | the nth line
	| Absolute Int 
	-- | the line addressed by the engine
	| Current 
	-- | the nth line before the addressed one
	| Prev Int 
	-- | the nth line aftor the addressed one
	| Next Int 
	-- | the next line (wrapping around) matching a regex
	| ReNext String 
	-- | the next line matching the last learned regex 
	| LastReNext
	-- | the previous line (wrapping around) matching a regex
	| RePrev String
	-- | the previous matching the last learned regex 
	| LastRePrev
	-- | the line marked previously with a char
	| MarkedAs Char deriving Show
-- | a couple of Offsets
data Range = Range Offset Offset deriving Show

-- | wrapper a round the two possible addressing for a command Offset and Range
data OffsetOrRange 
	= ORO Offset 
	| ORR Range  
	| ORN deriving Show

-- | a complete command is a Command coupled with a Range or an Offset
data CompleteCommand = CC Command OffsetOrRange deriving Show


-- | main datatype for the program-- beyond the core state, a simulation layer 'SIO' can be read 
-- and errors 'Err' can be thrown to kill the monad flow
type Editor m	=  ErrorT Err (StatoE m)


-- | wrap a maybe action and throw a backend error on a Nothing 
backend		:: Ctx m  	
	=> Maybe a 	-- ^ maybe action
	-> Editor m a	-- ^ monading 
backend	= maybe (throwError BackendErr) return

-- | execute an action on the file
through	:: Ctx m
	=> (Engine -> Maybe a)	-- ^ an action from an engine w to a maybe
	-> Editor m a		-- ^ the result from Just in the Editor monad
through f = gets file >>= backend . f
 

-- | the inputSio action lifted to Editor
pinput	::  Ctx m  	=> String -> Editor m (Maybe String)	
pinput =  liftSio . inputSio

-- | the inputSio action lifted to Editor with empty prompt
input	:: Ctx m  	=> Editor m (Maybe String)
input 	= pinput ""

-- | the outputSio action lifted to Editor
output	:: Ctx m 	=> String -> Editor m () 
output = liftSio . outputSio 

-- | the historySIO action lifted to Editor 
history	:: Ctx m 	=> String -> Editor m ()
history	= liftSio . historySio 

-- | the errorSIO action lifted to Editor
errorlog :: Ctx m 	=> String -> Editor m ()
errorlog 	= liftSio . errorSIO 

-- | editor runner .
-- resolve the all monad from a core state to another
run	:: Ctx m  	
	=> Editor m a 	-- ^ the action to run
	-> Stato 		-- ^ the initial state 
	-> m (Stato)		-- ^ the final state wrapped in the monad choosen for the SIO

run editor w = flip execUndoT w $ runErrorT editor >>= \x -> 
	case x of 	Left err -> lift $ errorSIO (show err)
	          	Right _  -> return ()