{-# LANGUAGE TypeSynonymInstances #-}

-- | When the output of a command is long, keeping it as a `String` is a bad idea.
module BuildBox.Data.Log
        ( Log
        , Line
        , empty
        , null
        , toString
        , fromString
        , (<|)
        , (|>)
        , (><)
        , firstLines
        , lastLines)
where
import Data.Sequence                    (Seq)
import Data.Text                        (Text)
import qualified Data.Text              as Text
import qualified Data.Sequence          as Seq
import qualified Data.Foldable          as F
import Prelude                          hiding (null)

-- | A sequence of lines, without newline charaters on the end.
type Log        = Seq Line
type Line       = Text


-- | O(1) No logs here.
empty :: Log
empty = Seq.empty

-- | O(1) Check if the log is empty.
null :: Log -> Bool
null  = Seq.null

-- | O(n) Convert a `Log` to a `String`.
toString :: Log -> String
toString ll     
        = Text.unpack 
        $ Text.intercalate (Text.pack "\n") 
        $ F.toList ll

-- | O(n) Convert a `String` to a `Log`.
fromString :: String -> Log
fromString str  
        = Seq.fromList 
        $ Text.lines 
        $ Text.pack str


-- | O(1) Add a `Line` to the start of a `Log`.
(<|):: Line -> Log -> Log
(<|)    = (Seq.<|)

-- | O(1) Add a `Line` to the end of a `Log`.
(|>)    :: Log -> Line -> Log
(|>)    = (Seq.|>)

-- | O(log(min(n1,n2))) Concatenate two `Log`s.
(><)    :: Log -> Log -> Log
(><)    = (Seq.><)


-- | O(n) Take the first m lines from a log
firstLines :: Int -> Log -> Log
firstLines m ll
        = Seq.take m ll

-- | O(n) Take the last m lines from a log
lastLines :: Int -> Log -> Log
lastLines m ll
        = Seq.drop (Seq.length ll - m) ll