{-# LANGUAGE MultiWayIf #-}
module FRP.Titan.Debug.History where
import Control.Monad
import Data.Maybe
import FRP.Yampa (SF, FutureSF, DTime)
import Data.Extra
import FRP.Titan.Debug.Comm
data History a b = History
{ getInputHistory :: Maybe (Stream a (DTime, a))
, getSFHistory :: Stream (SF a b) (FutureSF a b)
, getPos :: Int
, getCurSF :: Either (SF a b) (FutureSF a b)
, getLastInput :: Maybe a
}
type Stream a a' = (a, [a'])
getCurSF' :: History a b -> Either (SF a b) (FutureSF a b)
getCurSF' history = fromMaybe (Left $ fst $ getSFHistory history) (getSampleAt (getSFHistory history) (getPos (history)))
mkEmptyHistory :: SF a b -> History a b
mkEmptyHistory sf = History Nothing (sf, []) 0 (Left sf) Nothing
mkHistory :: (a, SF a b) -> FutureSF a b -> History a b
mkHistory (a0, sf0) sf' =
History (Just (a0, [])) (sf0, [sf']) 1 (Right sf') (Just a0)
historyIsRunning :: History a b -> Bool
historyIsRunning history = ((>0) . getPos) (history)
historyReplaceInputAt :: History a b -> Int -> a -> History a b
historyReplaceInputAt history f a
| ns < f = history
| f == 0 = if isNothing hs
then history
else history { getInputHistory = Just (a, ps)
, getSFHistory = ((\(x,y) -> (x, [])) $ getSFHistory history)
}
| otherwise = history { getInputHistory = Just (a0, appAt (f-1) (\(dt, _) -> (dt, a)) ps)
, getSFHistory = ((\(x,y) -> (x, take f y)) $ getSFHistory history)
}
where
hs = getInputHistory history
Just (a0, ps) = hs
ns = maybe 0 (length.snd) hs
historyReplaceDTimeAt :: History a b -> Int -> DTime -> History a b
historyReplaceDTimeAt history f dt =
let Just (a0, ps) = getInputHistory history
dts = 0 : map (\(dt,_) -> dt) ps
in if length dts >= f
then history
else if f == 0
then history
else history { getInputHistory = Just (a0, appAt (f-1) (\(_,a) -> (dt, a)) ps)
, getSFHistory = ((\(x,y) -> (x, take f y)) $ getSFHistory history)
}
historyReplaceInputDTimeAt :: History a b -> Int -> DTime -> a -> History a b
historyReplaceInputDTimeAt history f dt a =
let (Just (a0, ps)) = getInputHistory history
as = a0 : map (\(_, a) -> a) ps
in if length as >= f
then history
else if f == 0
then history { getInputHistory = Just (a, ps)
, getSFHistory = ((\(x,y) -> (x, [])) $ getSFHistory history)}
else history { getInputHistory = Just (a0, appAt (f-1) (\(_,_) -> (dt, a)) ps)
, getSFHistory = ((\(x,y) -> (x, take f y)) $ getSFHistory history)}
historyGetMaxTime :: History a b -> DTime
historyGetMaxTime history =
case getInputHistory history of
Nothing -> 0
Just (a0, ps) -> sum $ map (\(dt,_) -> dt) ps
historyGetGTime :: History a b -> Int -> Maybe DTime
historyGetGTime history f =
case getInputHistory history of
Nothing -> Nothing
Just (a0, ps) -> let dts = 0 : map fst ps
l = length dts
e = if l < f then Nothing else Just (sum (drop (l-f) dts))
in e
historyGetDTime :: History a b -> Int -> Maybe DTime
historyGetDTime history f =
case getInputHistory history of
Nothing -> Nothing
Just (a0, ps) -> let dts = 0 : map fst ps
e = if length dts < f || f < 0 then Nothing else Just (dts !! f)
in e
historyGetInput :: History a b -> Int -> Maybe a
historyGetInput history f =
case getInputHistory history of
Nothing -> Nothing
Just (a0, ps) -> let as = a0 : map snd ps
e = if length as < f || f < 0then Nothing else Just (as !! f)
in e
historyGetCurrentTime :: History t b -> DTime
historyGetCurrentTime history =
case getInputHistory history of
Just (a0, ps) -> sum $ map (\(dt,_) -> dt) (take (getPos history) ps)
Nothing -> 0
historyGetCurrentFrame :: History a b -> Int
historyGetCurrentFrame history = getPos history
historyRecordFrame1 :: History a b -> (a, DTime, FutureSF a b) -> History a b
historyRecordFrame1 history (a', dt, sf') = historySF
where
historyInput = case getInputHistory history of
Nothing -> history
Just (a0, ps) -> if | pos > 0 && pos < length ps -> history { getInputHistory = Just (a0, appAt pos (const (dt, a')) ps) }
| pos > 0 -> history { getInputHistory = Just (a0, ps ++ [(dt, a')]) }
| otherwise -> history
historySF = let (s0, ss) = getSFHistory historyInput
in if getPos history <= 0
then historyInput
else historyInput { getSFHistory = (s0, take (getPos history) ss ++ [sf'])
, getPos = pos + 1
}
pos = getPos history
historyGetNumFrames :: History t b -> Int
historyGetNumFrames history =
case getInputHistory history of
Just (a0, ps) -> length ps + 1
Nothing -> 0
historyGetCurFrame :: History a b -> (a, Maybe DTime, Maybe (Either (SF a b) (FutureSF a b)))
historyGetCurFrame history =
case curInput of
Just (Left a0) -> (a0, Nothing, curSF)
Just (Right (dt, a)) -> (a, Just dt, curSF)
Nothing -> error "No current frame"
where
curInput = (`getSampleAt` (getPos history)) =<< getInputHistory history
curSF = (`getSampleAt` (getPos history)) (getSFHistory history)
getSampleAt :: Stream a a' -> Int -> Maybe (Either a a')
getSampleAt (s0, ss) 0 = Just (Left s0)
getSampleAt (s0, ss) n
| n <= length ss = Just (Right (ss!!(n-1)))
| otherwise = Nothing
historyBack :: History a b -> History a b
historyBack history = history { getPos = max 0 (getPos history - 1)}
historyJumpTo :: History a b -> Int -> History a b
historyJumpTo history n =
case getInputHistory history of
Nothing -> history
Just (a0, ps) ->
if length ps + 1 > n
then history { getPos = n }
else history
historyDiscardFuture :: History a b -> Int -> History a b
historyDiscardFuture history n =
case getInputHistory history of
Nothing -> history
Just (a0, ps) ->
if length ps + 1 > n
then if n > 0
then history { getInputHistory = Just (a0, take n ps)
, getSFHistory = (\(s0, ss) -> (s0, take n ss)) (getSFHistory history)
, getPos = min n (getPos history)
}
else History { getInputHistory = Just (a0, [])
, getSFHistory = (\(s0, ss) -> (s0, [])) (getSFHistory history)
, getPos = 0
}
else history
getCommand :: (Read a, Show a) => ExternalBridge -> [a] -> IO (Maybe a, [a])
getCommand bridge cmds = do
mLines <- filter (not . null) <$> getAllMessages bridge
let cmLines = map maybeRead mLines
cLines = catMaybes cmLines
unless (null mLines) $ do
ebPrint bridge (show mLines)
ebPrint bridge (show cmLines)
case cmds ++ cLines of
[] -> return (Nothing, [])
(c:cs) -> return (Just c, cs)
pushCommand :: [a] -> a -> [a]
pushCommand cs c = c:cs
appendCommand :: [a] -> a -> [a]
appendCommand cs c = cs ++ [c]