{-# OPTIONS_HADDOCK show-extensions #-}

-- |
-- Module      :  Yi.Rope
-- License     :  GPL-2
-- Maintainer  :  yi-devel@googlegroups.com
-- Stability   :  experimental
-- Portability :  portable
--
-- A work in progress module that aims to be a more efficient
-- replacement for "Yi.OldRope". For now, please import "Yi.OldRope"!
module Yi.Rope (Rope, fromString, toString, toReverseString, null, empty,
                Yi.Rope.take, Yi.Rope.drop, Yi.Rope.length, reverse,
                countNewLines, Yi.Rope.split, Yi.Rope.splitAt,
                Yi.Rope.splitAtLine, Yi.Rope.append, Yi.Rope.concat,
                Yi.Rope.readFile, Yi.Rope.writeFile)
       where

import qualified Codec.Binary.UTF8.Generic as G
import           Data.Binary
import qualified Data.ByteString.Lazy as LB (readFile, split, count)
import           Data.Monoid
import           Data.Rope
import qualified Prelude as P
import           Prelude hiding (null, take, drop, reverse)
import           System.IO.Cautious (writeFileL)

toReverseString :: Rope -> String
toReverseString = P.reverse . toString

reverse :: Rope -> Rope
reverse = fromString . P.reverse . toString

countNewLines :: Rope -> Int
countNewLines = fromIntegral . LB.count 10 . toLazyByteString

split :: Word8 -> Rope -> [Rope]
split c = map fromLazyByteString . LB.split c . toLazyByteString

splitAt :: Int -> Rope -> (Rope, Rope)
splitAt = G.splitAt

-- | Split before the specified line. Lines are indexed from 0.
splitAtLine :: Int -> Rope -> (Rope, Rope)
splitAtLine n r | n <= 0     = (mempty, r)
                | otherwise = splitAtLine' (n - 1) r

-- | Split after the specified line. Lines are indexed from 0.
splitAtLine' :: Int -> Rope -> (Rope, Rope)
splitAtLine' n r = let ls = P.take (n + 1) (G.lines' r)
                   in G.splitAt (sum $ map G.length ls) r

append :: Rope -> Rope -> Rope
append = (<>)

concat :: [Rope] -> Rope
concat = mconcat

writeFile :: FilePath -> Rope -> IO ()
writeFile f = writeFileL f . toLazyByteString

readFile :: FilePath -> IO Rope
readFile f = fromLazyByteString `fmap` LB.readFile f

drop, take :: Int -> Rope -> Rope
take i = fst . G.splitAt i
drop i = snd . G.splitAt i

length :: Rope -> Int
length = G.length