module Data.Text.Markup
  ( Markup
  , markupToList
  , markupSet
  , fromList
  , fromText
  , toText
  , (@@)
  )
where
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative ((<$>))
import Data.Monoid
#endif
import Data.String (IsString(..))
import qualified Data.Text as T
data Markup a = Markup [(Char, a)]
              deriving Show
instance Monoid (Markup a) where
    mempty = Markup mempty
    mappend (Markup t1) (Markup t2) =
        Markup (t1 `mappend` t2)
instance (Monoid a) => IsString (Markup a) where
    fromString = fromText . T.pack
(@@) :: T.Text -> a -> Markup a
t @@ val = Markup [(c, val) | c <- T.unpack t]
fromText :: (Monoid a) => T.Text -> Markup a
fromText = (@@ mempty)
toText :: (Eq a) => Markup a -> T.Text
toText = T.concat . (fst <$>) . concat . markupToList
markupSet :: (Eq a) => (Int, Int) -> a -> Markup a -> Markup a
markupSet (start, len) val m@(Markup l) = if start < 0 || start + len > length l
                                          then m
                                          else newM
    where
        newM = Markup $ theHead ++ theNewEntries ++ theTail
        (theHead, theLongTail) = splitAt start l
        (theOldEntries, theTail) = splitAt len theLongTail
        theNewEntries = zip (fst <$> theOldEntries) (repeat val)
markupToList :: (Eq a) => Markup a -> [[(T.Text, a)]]
markupToList (Markup thePairs) = toList <$> toLines [] [] thePairs
    where
        toLines ls cur [] = ls ++ [cur]
        toLines ls cur ((ch, val):rest)
            | ch == '\n' = toLines (ls ++ [cur]) [] rest
            | otherwise = toLines ls (cur ++ [(ch, val)]) rest
        toList [] = []
        toList ((ch, val):rest) = (T.pack $ ch : (fst <$> matching), val) : toList remaining
            where
                (matching, remaining) = break (\(_, v) -> v /= val) rest
fromList :: [(T.Text, a)] -> Markup a
fromList pairs = Markup $ concatMap (\(t, val) -> [(c, val) | c <- T.unpack t]) pairs