{-# LANGUAGE MultiParamTypeClasses, BangPatterns #-}
module Text.Trifecta.Slice
  ( slice
  , sliced
  ) where

import Control.Monad.Trans.Class
import Data.Semigroup
import Data.Interned
import Data.Foldable (toList)
import Data.FingerTree as FingerTree
import Data.ByteString as Strict
import Data.ByteString.Lazy as Lazy
import Text.Trifecta.Hunk
import Text.Trifecta.Bytes
import Text.Trifecta.Rope as Rope
import Text.Trifecta.Delta
import Text.Trifecta.Strand
import Text.Trifecta.It
import Text.Parsec.Prim hiding ((<|>))

slice :: Delta -> Delta -> P u Strict.ByteString
slice !i !j = lift (Cont loop)
  where
    bi = bytes i
    bj = bytes j
    loop t eof
      | bj <= bytes t || eof = Done t eof $ go $ strands t
      | otherwise = Cont $ \t' eof' -> loop (t <> t') eof'
    go !t
      | required <= Strict.length first = Strict.take required first
      | otherwise = Strict.concat 
                  $ Lazy.toChunks 
                  $ Lazy.take (fromIntegral required) 
                  $ Lazy.fromChunks 
                  $ first : Prelude.foldr iter [] (toList r')
      where 
        iter (HunkStrand h) b = unintern h : b
        iter (PathStrand _) b = b
        (l,r) = FingerTree.split (\m -> bytes m > bi) t
        HunkStrand (Hunk _ _ a) :< r' = FingerTree.viewl r
        first = Strict.drop (bi - bytes l) a
        required = bj - bi

sliced :: (a -> Strict.ByteString -> r) -> P u a -> P u r
sliced f pa = do
  mark <- getInput
  a <- pa
  release <- getInput
  bs <- slice mark release
  return $ f a bs