module Data.Attoparsec.Internal
    (
    
      Parser
    , Result(..)
    
    , parse
    , parseOnly
    
    , (<?>)
    , try
    , module Data.Attoparsec.Combinator
    
    , satisfy
    , satisfyWith
    , anyWord8
    , skip
    , word8
    , notWord8
    
    , inClass
    , notInClass
    
    , storable
    
    , skipWhile
    , string
    , stringTransform
    , take
    , scan
    , takeWhile
    , takeWhile1
    , takeTill
    
    , takeByteString
    , takeLazyByteString
    
    , endOfInput
    , atEnd
    , ensure
    
    , endOfLine
    ) where
import Control.Applicative (Alternative(..), Applicative(..), (<$>))
import Control.Monad (MonadPlus(..), when)
import Data.Attoparsec.Combinator
import Data.Attoparsec.FastSet (charClass, memberWord8)
import Data.Monoid (Monoid(..))
import Data.Word (Word8)
import Foreign.ForeignPtr (withForeignPtr)
import Foreign.Ptr (castPtr, plusPtr)
import Foreign.Storable (Storable(peek, sizeOf), peekByteOff)
import Prelude hiding (getChar, take, takeWhile)
import System.IO.Unsafe (unsafePerformIO)
import qualified Data.ByteString as B8
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Internal as B
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Lazy as L
data Result r = Fail B.ByteString [String] String
              
              
              
              
              
              | Partial (B.ByteString -> Result r)
              
              
              
              | Done B.ByteString r
              
              
              
instance Show r => Show (Result r) where
    show (Fail bs stk msg) =
        "Fail " ++ show bs ++ " " ++ show stk ++ " " ++ show msg
    show (Partial _)       = "Partial _"
    show (Done bs r)       = "Done " ++ show bs ++ " " ++ show r
fmapR :: (a -> b) -> Result a -> Result b
fmapR _ (Fail st stk msg) = Fail st stk msg
fmapR f (Partial k)       = Partial (fmapR f . k)
fmapR f (Done bs r)       = Done bs (f r)
instance Functor Result where
    fmap = fmapR
newtype Input = I {unI :: B.ByteString}
newtype Added = A {unA :: B.ByteString}
newtype Parser a = Parser {
      runParser :: forall r. Input -> Added -> More
                -> Failure   r
                -> Success a r
                -> Result r
    }
type Failure   r = Input -> Added -> More -> [String] -> String -> Result r
type Success a r = Input -> Added -> More -> a -> Result r
data More = Complete | Incomplete
            deriving (Eq, Show)
addS :: Input -> Added -> More
     -> Input -> Added -> More
     -> (Input -> Added -> More -> r) -> r
addS i0 a0 m0 _i1 a1 m1 f =
    let !i = I (unI i0 +++ unA a1)
        a  = A (unA a0 +++ unA a1)
        m  = m0 <> m1
    in f i a m
  where
    Complete <> _ = Complete
    _ <> Complete = Complete
    _ <> _        = Incomplete
bindP :: Parser a -> (a -> Parser b) -> Parser b
bindP m g =
    Parser $ \i0 a0 m0 kf ks -> runParser m i0 a0 m0 kf $
                                \i1 a1 m1 a -> runParser (g a) i1 a1 m1 kf ks
returnP :: a -> Parser a
returnP a = Parser (\i0 a0 m0 _kf ks -> ks i0 a0 m0 a)
instance Monad Parser where
    return = returnP
    (>>=)  = bindP
    fail   = failDesc
noAdds :: Input -> Added -> More
       -> (Input -> Added -> More -> r) -> r
noAdds i0 _a0 m0 f = f i0 (A B.empty) m0
plus :: Parser a -> Parser a -> Parser a
plus a b = Parser $ \i0 a0 m0 kf ks ->
           let kf' i1 a1 m1 _ _ = addS i0 a0 m0 i1 a1 m1 $
                                  \ i2 a2 m2 -> runParser b i2 a2 m2 kf ks
           in  noAdds i0 a0 m0 $ \i2 a2 m2 -> runParser a i2 a2 m2 kf' ks
instance MonadPlus Parser where
    mzero = failDesc "mzero"
    
    mplus = plus
fmapP :: (a -> b) -> Parser a -> Parser b
fmapP p m = Parser $ \i0 a0 m0 f k ->
            runParser m i0 a0 m0 f $ \i1 a1 s1 a -> k i1 a1 s1 (p a)
instance Functor Parser where
    fmap = fmapP
apP :: Parser (a -> b) -> Parser a -> Parser b
apP d e = do
  b <- d
  a <- e
  return (b a)
instance Applicative Parser where
    pure   = returnP
    (<*>)  = apP
    
    
    
    (*>)   = (>>)
    x <* y = x >>= \a -> y >> return a
instance Monoid (Parser a) where
    mempty  = failDesc "mempty"
    
    mappend = plus
instance Alternative Parser where
    empty = failDesc "empty"
    
    (<|>) = plus
failDesc :: String -> Parser a
failDesc err = Parser (\i0 a0 m0 kf _ks -> kf i0 a0 m0 [] msg)
    where msg = "Failed reading: " ++ err
ensure :: Int -> Parser B.ByteString
ensure !n = Parser $ \i0 a0 m0 kf ks ->
    if B.length (unI i0) >= n
    then ks i0 a0 m0 (unI i0)
    else runParser (demandInput >> ensure n) i0 a0 m0 kf ks
prompt :: Input -> Added -> More
       -> (Input -> Added -> More -> Result r)
       -> (Input -> Added -> More -> Result r)
       -> Result r
prompt i0 a0 _m0 kf ks = Partial $ \s ->
    if B.null s
    then kf i0 a0 Complete
    else ks (I (unI i0 +++ s)) (A (unA a0 +++ s)) Incomplete
demandInput :: Parser ()
demandInput = Parser $ \i0 a0 m0 kf ks ->
    if m0 == Complete
    then kf i0 a0 m0 ["demandInput"] "not enough bytes"
    else let kf' i a m = kf i a m ["demandInput"] "not enough bytes"
             ks' i a m = ks i a m ()
         in prompt i0 a0 m0 kf' ks'
wantInput :: Parser Bool
wantInput = Parser $ \i0 a0 m0 _kf ks ->
  case () of
    _ | not (B.null (unI i0)) -> ks i0 a0 m0 True
      | m0 == Complete  -> ks i0 a0 m0 False
      | otherwise       -> let kf' i a m = ks i a m False
                               ks' i a m = ks i a m True
                           in prompt i0 a0 m0 kf' ks'
get :: Parser B.ByteString
get  = Parser $ \i0 a0 m0 _kf ks -> ks i0 a0 m0 (unI i0)
put :: B.ByteString -> Parser ()
put s = Parser $ \_i0 a0 m0 _kf ks -> ks (I s) a0 m0 ()
(+++) :: B.ByteString -> B.ByteString -> B.ByteString
(+++) = B.append
try :: Parser a -> Parser a
try p = Parser $ \i0 a0 m0 kf ks ->
        noAdds i0 a0 m0 $ \i1 a1 m1 ->
            let kf' i2 a2 m2 = addS i0 a0 m0 i2 a2 m2 kf
            in runParser p i1 a1 m1 kf' ks
satisfy :: (Word8 -> Bool) -> Parser Word8
satisfy p = do
  s <- ensure 1
  let w = B.unsafeHead s
  if p w
    then put (B.unsafeTail s) >> return w
    else fail "satisfy"
skip :: (Word8 -> Bool) -> Parser ()
skip p = do
  s <- ensure 1
  if p (B.unsafeHead s)
    then put (B.unsafeTail s)
    else fail "skip"
satisfyWith :: (Word8 -> a) -> (a -> Bool) -> Parser a
satisfyWith f p = do
  s <- ensure 1
  let c = f (B.unsafeHead s)
  if p c
    then put (B.unsafeTail s) >> return c
    else fail "satisfyWith"
storable :: Storable a => Parser a
storable = hack undefined
 where
  hack :: Storable b => b -> Parser b
  hack dummy = do
    (fp,o,_) <- B.toForeignPtr `fmapP` take (sizeOf dummy)
    return . B.inlinePerformIO . withForeignPtr fp $ \p ->
        peek (castPtr $ p `plusPtr` o)
takeWith :: Int -> (B.ByteString -> Bool) -> Parser B.ByteString
takeWith n p = do
  s <- ensure n
  let (h,t) = B.splitAt n s
  if p h
    then put t >> return h
    else failDesc "takeWith"
take :: Int -> Parser B.ByteString
take n = takeWith n (const True)
string :: B.ByteString -> Parser B.ByteString
string s = takeWith (B.length s) (==s)
stringTransform :: (B.ByteString -> B.ByteString) -> B.ByteString
                -> Parser B.ByteString
stringTransform f s = takeWith (B.length s) ((==f s) . f)
skipWhile :: (Word8 -> Bool) -> Parser ()
skipWhile p = go
 where
  go = do
    t <- B8.dropWhile p <$> get
    put t
    when (B.null t) $ do
      input <- wantInput
      when input go
takeTill :: (Word8 -> Bool) -> Parser B.ByteString
takeTill p = takeWhile (not . p)
takeWhile :: (Word8 -> Bool) -> Parser B.ByteString
takeWhile p = (B.concat . reverse) `fmap` go []
 where
  go acc = do
    (h,t) <- B8.span p <$> get
    put t
    if B.null t
      then do
        input <- wantInput
        if input
          then go (h:acc)
          else return (h:acc)
      else return (h:acc)
takeRest :: Parser [B.ByteString]
takeRest = go []
 where
  go acc = do
    input <- wantInput
    if input
      then do
        s <- get
        put B.empty
        go (s:acc)
      else return (reverse acc)
takeByteString :: Parser B.ByteString
takeByteString = B.concat `fmap` takeRest
takeLazyByteString :: Parser L.ByteString
takeLazyByteString = L.fromChunks `fmap` takeRest
scan :: s -> (s -> Word8 -> Maybe s) -> Parser B.ByteString
scan s0 p = do
  chunks <- go [] s0
  case chunks of
    [x] -> return x
    xs  -> return . B.concat . reverse $ xs
 where
  go acc s1 = do
    let scanner (B.PS fp off len) =
          withForeignPtr fp $ \ptr -> do
            let inner !i !s | i == off+len = done (ioff) s
                            | otherwise = do
                                        w <- peekByteOff ptr i
                                        case p s w of
                                          Just s' -> inner (i+1) s'
                                          Nothing -> done (ioff) s
                done !i !s = return (B.PS fp off i, B.PS fp (off+i) (leni),s)
            inner off s1
    (h,t,s') <- (unsafePerformIO . scanner) <$> get
    put t
    if B.null t
      then do
        input <- wantInput
        if input
          then go (h:acc) s'
          else return (h:acc)
      else return (h:acc)
takeWhile1 :: (Word8 -> Bool) -> Parser B.ByteString
takeWhile1 p = do
  (`when` demandInput) =<< B.null <$> get
  (h,t) <- B8.span p <$> get
  when (B.null h) $ failDesc "takeWhile1"
  put t
  if B.null t
    then (h+++) `fmapP` takeWhile p
    else return h
inClass :: String -> Word8 -> Bool
inClass s = (`memberWord8` mySet)
    where mySet = charClass s
notInClass :: String -> Word8 -> Bool
notInClass s = not . inClass s
anyWord8 :: Parser Word8
anyWord8 = satisfy $ const True
word8 :: Word8 -> Parser Word8
word8 c = satisfy (== c) <?> show c
notWord8 :: Word8 -> Parser Word8
notWord8 c = satisfy (/= c) <?> "not " ++ show c
endOfInput :: Parser ()
endOfInput = Parser $ \i0 a0 m0 kf ks ->
             if B.null (unI i0)
             then if m0 == Complete
                  then ks i0 a0 m0 ()
                  else let kf' i1 a1 m1 _ _ = addS i0 a0 m0 i1 a1 m1 $
                                              \ i2 a2 m2 -> ks i2 a2 m2 ()
                           ks' i1 a1 m1 _   = addS i0 a0 m0 i1 a1 m1 $
                                              \ i2 a2 m2 -> kf i2 a2 m2 []
                                                            "endOfInput"
                       in  runParser demandInput i0 a0 m0 kf' ks'
             else kf i0 a0 m0 [] "endOfInput"
atEnd :: Parser Bool
atEnd = not <$> wantInput
endOfLine :: Parser ()
endOfLine = (word8 10 >> return ()) <|> (string "\r\n" >> return ())
(<?>) :: Parser a
      -> String                 
      -> Parser a
p <?> msg0 = Parser $ \i0 a0 m0 kf ks ->
             let kf' i a m strs msg = kf i a m (msg0:strs) msg
             in runParser p i0 a0 m0 kf' ks
infix 0 <?>
failK :: Failure a
failK i0 _a0 _m0 stack msg = Fail (unI i0) stack msg
successK :: Success a a
successK i0 _a0 _m0 a = Done (unI i0) a
parse :: Parser a -> B.ByteString -> Result a
parse m s = runParser m (I s) (A B.empty) Incomplete failK successK
parseOnly :: Parser a -> B.ByteString -> Either String a
parseOnly m s = case runParser m (I s) (A B.empty) Complete failK successK of
                  Fail _ _ err -> Left err
                  Done _ a     -> Right a
                  _            -> error "parseOnly: impossible error!"