module Data.Woot.Core
( integrate
, integrateAll
, makeDeleteOperation
, makeInsertOperation
) where
import Data.Maybe (fromJust)
import Data.Woot.WString
import Data.Woot.WChar
import Data.Woot.Operation
integrate :: Operation -> WString -> Maybe WString
integrate op ws = if canIntegrate op ws then Just $ integrateOp op ws else Nothing
integrateAll :: [Operation] -> WString -> ([Operation], WString)
integrateAll ops ws = if length ops == length newOps then result
else integrateAll newOps newString
where
result@(newOps, newString) = foldl integrate' ([], ws) ops
integrate' (ops', s) op = maybe (ops' ++ [op], s) (ops',) (integrate op s)
canIntegrate :: Operation -> WString -> Bool
canIntegrate (Operation Insert _ wc) ws = all (\(Just wid) -> wid `hasChar` ws) [wCharPrevId wc, wCharNextId wc]
canIntegrate (Operation Delete _ wc) ws = hasChar (wCharId wc) ws
integrateOp :: Operation -> WString -> WString
integrateOp (Operation Insert _ wc) ws = integrateInsert (wCharPrevId wc) (wCharNextId wc) wc ws
integrateOp (Operation Delete _ wc) ws = integrateDelete wc ws
integrateInsert :: Maybe WCharId -> Maybe WCharId -> WChar -> WString -> WString
integrateInsert _ _ wc ws | hasChar (wCharId wc) ws = ws
integrateInsert Nothing _ wc ws = insertChar 1 wc ws
integrateInsert _ Nothing wc ws = insertChar (length' ws 2) wc ws
integrateInsert (Just prevId) (Just nextId) wc ws = if isEmpty sub
then insertChar (fromJust $ indexOf nextId ws) wc ws
else compareIds $ map wCharId (toList sub) ++ [nextId]
where
sub = subsection prevId nextId ws
compareIds :: [WCharId] -> WString
compareIds (wid:_) | wCharId wc < wid = insertChar (fromJust $ indexOf wid ws) wc ws
compareIds (_:wid:_) = integrateInsert (Just wid) (Just nextId) wc ws
compareIds _ = ws
integrateDelete :: WChar -> WString -> WString
integrateDelete wc = hideChar (wCharId wc)
makeDeleteOperation :: ClientId -> Int -> WString -> Maybe Operation
makeDeleteOperation cid pos ws = Operation Delete cid <$> nthVisible pos ws
makeInsertOperation :: ClientId -> Int -> Int -> Char -> WString -> Maybe Operation
makeInsertOperation cid clock pos a ws = Operation Insert cid <$> do
let numVis = length' $ visibleChars ws
prev <- if pos == 0 then ws !? 0 else nthVisible (pos 1) ws
next <- if pos >= numVis then ws !? (length' ws 1) else nthVisible pos ws
return $ WChar (WCharId cid clock) True a (Just $ wCharId prev) (Just $ wCharId next)