-- | Like Engine, but merely checks to see whether any match at all is found.
-- 
module Text.Regex.TDFA.NewDFA.Tester(matchTest) where

import Control.Monad(MonadPlus(..))
import qualified Data.IntMap.CharMap2 as CMap(findWithDefault)
import qualified Data.IntMap as IMap
import qualified Data.IntSet as ISet(null)

import Data.Sequence(Seq,ViewL(..),viewl)
import qualified Data.Sequence as Seq
import qualified Data.ByteString.Char8 as SBS
import qualified Data.ByteString.Lazy.Char8 as LBS

import Text.Regex.Base()
import Text.Regex.TDFA.Common hiding (indent)
import Text.Regex.TDFA.NewDFA.Uncons (Uncons(uncons))

{-# SPECIALIZE matchTest :: Regex -> ([] Char) -> Bool #-}
{-# SPECIALIZE matchTest :: Regex -> (Seq Char) -> Bool #-}
{-# SPECIALIZE matchTest :: Regex -> SBS.ByteString -> Bool #-}
{-# SPECIALIZE matchTest :: Regex -> LBS.ByteString -> Bool #-}
matchTest :: Uncons text => Regex -> text -> Bool
matchTest (Regex { regex_dfa = dfaIn
                 , regex_isFrontAnchored = ifa
                 , regex_compOptions = CompOption { multiline = newline } } )
          inputIn = ans where

  ans = case ifa of
          True -> single0 (d_dt dfaIn) inputIn
          False -> multi0 (d_dt dfaIn) inputIn

  {-# NOINLINE test0 #-}
  {-# NOINLINE test #-}
  !test0 = mkTest0 newline
  !test = mkTest newline         

  multi0 (Testing' {dt_test=wt,dt_a=a,dt_b=b}) input =
    if test0 wt input
      then multi0 a input
      else multi0 b input
  multi0 (Simple' {dt_win=w,dt_trans=t, dt_other=o}) input
    | IMap.null w =
        case uncons input of
          Nothing -> False
          Just (c,input') ->
            case CMap.findWithDefault o c t of
              Transition {trans_many=DFA {d_dt=dt'}} -> multi dt' c input'
    | otherwise = True

  multi (Testing' {dt_test=wt,dt_a=a,dt_b=b}) prev input =
    if test wt prev input
      then multi a prev input
      else multi b prev input
  multi (Simple' {dt_win=w,dt_trans=t, dt_other=o}) _prev input
    | IMap.null w =
        case uncons input of
          Nothing -> False
          Just (c,input') ->
            case CMap.findWithDefault o c t of
              Transition {trans_many=DFA {d_dt=dt'}} -> multi dt' c input'
    | otherwise = True

  single0 (Testing' {dt_test=wt,dt_a=a,dt_b=b}) input =
    if testFA0 wt input
      then single0 a input
      else single0 b input
  single0 (Simple' {dt_win=w,dt_trans=t, dt_other=o}) input
    | IMap.null w =
        case uncons input of
             Nothing -> False
             Just (c,input') ->
               case CMap.findWithDefault o c t of
                 Transition {trans_single=DFA {d_id=did',d_dt=dt'}}
                   | ISet.null did' -> False
                   | otherwise -> single dt' input'
    | otherwise = True

  single (Testing' {dt_test=wt,dt_a=a,dt_b=b}) input =
    if testFA wt input
      then single a input
      else single b input
  single (Simple' {dt_win=w,dt_trans=t, dt_other=o}) input
    | IMap.null w =
        case uncons input of
             Nothing -> False
             Just (c,input') ->
               case CMap.findWithDefault o c t of
                 Transition {trans_single=DFA {d_id=did',d_dt=dt'}}
                   | ISet.null did' -> False
                   | otherwise -> single dt' input'
    | otherwise = True

testFA0,testFA :: Uncons text => WhichTest -> text -> Bool
testFA0 Test_BOL _input = True
testFA0 Test_EOL input = case uncons input of
                           Nothing -> True
                           _ -> False
testFA Test_BOL _input = False
testFA Test_EOL input = case uncons input of
                          Nothing -> True
                          _ -> False

{-# INLINE mkTest0 #-}
mkTest0 :: Uncons text => Bool -> WhichTest -> text -> Bool
mkTest0 isMultiline = if isMultiline then test_multiline else test_singleline
  where test_multiline Test_BOL _input = True
        test_multiline Test_EOL input = case uncons input of
                                          Nothing -> True
                                          Just (next,_) -> next == '\n'
        test_singleline Test_BOL _input = True
        test_singleline Test_EOL input = case uncons input of
                                           Nothing -> True
                                           _ -> False

{-# INLINE mkTest #-}
mkTest :: Uncons text => Bool -> WhichTest -> Char -> text -> Bool
mkTest isMultiline = if isMultiline then test_multiline else test_singleline
  where test_multiline Test_BOL prev _input = prev == '\n'
        test_multiline Test_EOL _prev input = case uncons input of
                                                Nothing -> True
                                                Just (next,_) -> next == '\n'
        test_singleline Test_BOL _prev _input = False
        test_singleline Test_EOL _prev input = case uncons input of
                                                Nothing -> True
                                                _ -> False