module PulseLambda.CodeStream
( CodeStream
, getInQueue
, getStreamLocation
, fromString
, getCurrChar
, goNext
, isEof
, skipWhile
, readWhile
, readWhileWithMax
) where
import PulseLambda.Location
import Data.Maybe


data CodeStream
    = StringStream
        { getInQueue :: String
        , getStreamLocation :: Location
        }
    deriving (Show)

fromString :: String -> FilePath -> CodeStream
fromString str file = StringStream str $! Location file 1 1

getCurrChar :: CodeStream -> Maybe Char
getCurrChar (StringStream [] _) = Nothing
getCurrChar (StringStream (x:_) _) = Just x

goNext :: CodeStream -> CodeStream
goNext (StringStream (x:xs) (Location file line col)) = if x == '\n'
    then StringStream xs (Location file (line+1) 1)
    else StringStream xs (Location file line (col+1))
goNext stream = stream

isEof :: CodeStream -> Bool
isEof (StringStream [] _) = True
isEof _ = False

skipWhile :: (Char -> Bool) -> CodeStream -> CodeStream
skipWhile cond stream = if isJust curr && (cond . fromJust) curr
    then skipWhile cond (goNext stream)
    else stream
    where curr = getCurrChar stream

readWhile :: (Char -> Bool) -> CodeStream -> (String, CodeStream)
readWhile cond stream = if isJust curr && (cond . fromJust) curr
    then
        let (str, newstream) = readWhile cond (goNext stream)
        in (fromJust curr:str, newstream)
    else ("", stream)
    where
        curr = getCurrChar stream

readWhileWithMax :: (Char -> Bool) -> Int -> CodeStream -> (String, CodeStream)
readWhileWithMax cond maxCount stream = _readWhileWithMax stream 0 where
    _readWhileWithMax _stream count = if isNothing curr || (not . cond . fromJust) curr || count >= maxCount
        then ("", stream)
        else
            let (str, newstream) = readWhile cond (goNext stream)
            in (fromJust curr:str, newstream)
        where
            curr = getCurrChar stream
