-- ------------------------------------------------------------

{- |
   Module     : Text.XML.HXT.Arrow.XmlState.TraceHandling
   Copyright  : Copyright (C) 2010 Uwe Schmidt
   License    : MIT

   Maintainer : Uwe Schmidt (uwe@fh-wedel.de)
   Stability  : stable
   Portability: portable

   the trace arrows

-}

-- ------------------------------------------------------------

module Text.XML.HXT.Arrow.XmlState.TraceHandling
where

import Control.Arrow                            -- arrow classes
import Control.Arrow.ArrowList
import Control.Arrow.ArrowIf
import Control.Arrow.ArrowTree
import Control.Arrow.ArrowIO

import System.IO                        ( hPutStrLn
                                        , hFlush
                                        , stderr
                                        )

import Text.XML.HXT.DOM.Interface

import Text.XML.HXT.Arrow.XmlArrow
import Text.XML.HXT.Arrow.XmlState.TypeDefs
import Text.XML.HXT.Arrow.XmlState.SystemConfig

import Text.XML.HXT.Arrow.Edit          ( addHeadlineToXmlDoc
                                        , treeRepOfXmlDoc
                                        , indentDoc
                                        )

-- ------------------------------------------------------------

-- | set the global trace level

setTraceLevel           :: Int -> IOStateArrow s b b
setTraceLevel l         = configSysVar $ withTrace l

-- | read the global trace level

getTraceLevel           :: IOStateArrow s b Int
getTraceLevel           = getSysVar theTraceLevel

-- | set the global trace command. This command does the trace output

setTraceCmd             :: (Int -> String -> IO ()) -> IOStateArrow s b b
setTraceCmd c           = configSysVar $ setS theTraceCmd c

-- | acces the command for trace output

getTraceCmd             :: IOStateArrow a b (Int -> String -> IO ())
getTraceCmd             = getSysVar theTraceCmd

-- | run an arrow with a given trace level, the old trace level is restored after the arrow execution

withTraceLevel          :: Int -> IOStateArrow s b c -> IOStateArrow s b c
withTraceLevel level f  = localSysEnv $ setTraceLevel level >>> f

-- | apply a trace arrow and issue message to stderr

trace                   :: Int -> IOStateArrow s b String -> IOStateArrow s b b
trace level trc         = perform ( trc
                                    >>>
                                    ( getTraceCmd &&& this )
                                    >>>
                                    arrIO (\ (cmd, msg) -> cmd level msg)
                                  )
                          `when` ( getTraceLevel
                                   >>>
                                   isA (>= level)
                                 )

-- | trace the current value transfered in a sequence of arrows.
--
-- The value is formated by a string conversion function. This is a substitute for
-- the old and less general traceString function

traceValue              :: Int -> (b -> String) -> IOStateArrow s b b
traceValue level trc    = trace level (arr $ (('-' : "- (" ++ show level ++ ") ") ++) . trc)

-- | an old alias for 'traceValue'

traceString             :: Int -> (b -> String) -> IOStateArrow s b b
traceString             = traceValue

-- | issue a string message as trace

traceMsg                :: Int -> String -> IOStateArrow s b b
traceMsg level msg      = traceValue level (const msg)

-- | issue the source representation of a document if trace level >= 3
--
-- for better readability the source is formated with indentDoc

traceSource             :: IOStateArrow s XmlTree XmlTree
traceSource             = trace 3 $
                          xshow $
                          choiceA [ isRoot :-> ( indentDoc
                                                 >>>
                                                 getChildren
                                               )
                                  , isElem :-> ( root [] [this]
                                                 >>> indentDoc
                                                 >>> getChildren
                                                 >>> isElem
                                               )
                                  , this   :-> this
                                  ]

-- | issue the tree representation of a document if trace level >= 4
traceTree               :: IOStateArrow s XmlTree XmlTree
traceTree               = trace 4 $
                          xshow $
                          treeRepOfXmlDoc
                          >>>
                          addHeadlineToXmlDoc
                          >>>
                          getChildren

-- | trace a main computation step
-- issue a message when trace level >= 1, issue document source if level >= 3, issue tree when level is >= 4

traceDoc                :: String -> IOStateArrow s XmlTree XmlTree
traceDoc msg            = traceMsg 1 msg
                          >>>
                          traceSource
                          >>>
                          traceTree

-- ----------------------------------------------------------

traceOutputToStderr     :: Int -> String -> IO ()
traceOutputToStderr _level msg
                        = do
                          hPutStrLn stderr msg
                          hFlush stderr

-- ----------------------------------------------------------