{-# LANGUAGE ExplicitForAll      #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE ScopedTypeVariables #-}

module HaskellWorks.Data.Dsv.Strict.Cursor.Internal where

import Control.Monad.ST
import Data.Bits                                  (popCount)
import Data.Word
import HaskellWorks.Data.AtIndex
import HaskellWorks.Data.Bits.BitWise
import HaskellWorks.Data.Dsv.Internal.Bits
import HaskellWorks.Data.Dsv.Internal.Broadword
import HaskellWorks.Data.Dsv.Strict.Cursor.Type
import HaskellWorks.Data.Positioning
import HaskellWorks.Data.RankSelect.Base.Rank1
import HaskellWorks.Data.RankSelect.Base.Select1
import Prelude

import qualified Data.Vector.Storable                       as DVS
import qualified Data.Vector.Storable.Mutable               as DVSM
import qualified HaskellWorks.Data.Dsv.Internal.Char.Word64 as CW

{-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}

makeIndexes :: Word8 -> DVS.Vector Word64 -> (DVS.Vector Word64, DVS.Vector Word64)
makeIndexes delimiter ws = case DVS.createT $ makeIndexes' CW.doubleQuote CW.newline (fillWord64 delimiter) ws of
  [markers, newlines] -> (markers, newlines)
  _                   -> error "This should not happen"

makeIndexes' :: forall s.
     Word64
  -> Word64
  -> Word64
  -> DVS.Vector Word64
  -> ST s [DVS.MVector s Word64]
makeIndexes' rdqs rnls rdls ws = do
  (markers  :: DVSM.MVector s Word64) <- DVSM.unsafeNew ((DVS.length ws + 7) `div` 8)
  (newlines :: DVSM.MVector s Word64) <- DVSM.unsafeNew ((DVS.length ws + 7) `div` 8)
  go markers newlines 0 0
  return [markers, newlines]
  where go :: DVSM.MVector s Word64 -> DVSM.MVector s Word64 -> Position -> Count -> ST s ()
        go markers newlines wsi numQuotes = let ui = wsi `div` 8 in if wsi >= 0 && wsi + 8 <= end ws
          then do
            let w0    = ws !!!  wsi
            let w0Dqs = testWord8s (w0 .^. rdqs)
            let w0Nls = testWord8s (w0 .^. rnls)
            let w0Dls = testWord8s (w0 .^. rdls)
            let w1    = ws !!! (wsi + 1)
            let w1Dqs = testWord8s (w1 .^. rdqs)
            let w1Nls = testWord8s (w1 .^. rnls)
            let w1Dls = testWord8s (w1 .^. rdls)
            let w2    = ws !!! (wsi + 2)
            let w2Dqs = testWord8s (w2 .^. rdqs)
            let w2Nls = testWord8s (w2 .^. rnls)
            let w2Dls = testWord8s (w2 .^. rdls)
            let w3    = ws !!! (wsi + 3)
            let w3Dqs = testWord8s (w3 .^. rdqs)
            let w3Nls = testWord8s (w3 .^. rnls)
            let w3Dls = testWord8s (w3 .^. rdls)
            let w4    = ws !!! (wsi + 4)
            let w4Dqs = testWord8s (w4 .^. rdqs)
            let w4Nls = testWord8s (w4 .^. rnls)
            let w4Dls = testWord8s (w4 .^. rdls)
            let w5    = ws !!! (wsi + 5)
            let w5Dqs = testWord8s (w5 .^. rdqs)
            let w5Nls = testWord8s (w5 .^. rnls)
            let w5Dls = testWord8s (w5 .^. rdls)
            let w6    = ws !!! (wsi + 6)
            let w6Dqs = testWord8s (w6 .^. rdqs)
            let w6Nls = testWord8s (w6 .^. rnls)
            let w6Dls = testWord8s (w6 .^. rdls)
            let w7    = ws !!! (wsi + 7)
            let w7Dqs = testWord8s (w7 .^. rdqs)
            let w7Nls = testWord8s (w7 .^. rnls)
            let w7Dls = testWord8s (w7 .^. rdls)
            let wDqs  = (w7Dqs  .<. 56) .|. (w6Dqs .<. 48) .|. (w5Dqs .<. 40) .|. (w4Dqs .<. 32) .|. (w3Dqs .<. 24) .|. (w2Dqs .<. 16) .|. (w1Dqs .<. 8) .|. w0Dqs
            let wNls  = (w7Nls  .<. 56) .|. (w6Nls .<. 48) .|. (w5Nls .<. 40) .|. (w4Nls .<. 32) .|. (w3Nls .<. 24) .|. (w2Nls .<. 16) .|. (w1Nls .<. 8) .|. w0Nls
            let wDls  = (w7Dls  .<. 56) .|. (w6Dls .<. 48) .|. (w5Dls .<. 40) .|. (w4Dls .<. 32) .|. (w3Dls .<. 24) .|. (w2Dls .<. 16) .|. (w1Dls .<. 8) .|. w0Dls
            let numWordQuotes = comp wDqs
            let wMask = toggle64 numQuotes numWordQuotes
            let newNumQuotes = numQuotes + fromIntegral (popCount numWordQuotes)
            DVSM.unsafeWrite markers  (fromIntegral ui) (comp (wNls .&. wDls) .&. wMask)
            DVSM.unsafeWrite newlines (fromIntegral ui) (comp  wNls           .&. wMask)
            go markers newlines (wsi + 8) newNumQuotes

          else do
            let w0    = atIndexOr 0 ws  wsi
            let w0Dqs = testWord8s (w0 .^. rdqs)
            let w0Nls = testWord8s (w0 .^. rnls)
            let w0Dls = testWord8s (w0 .^. rdls)
            let w1    = atIndexOr 0 ws (wsi + 1)
            let w1Dqs = testWord8s (w1 .^. rdqs)
            let w1Nls = testWord8s (w1 .^. rnls)
            let w1Dls = testWord8s (w1 .^. rdls)
            let w2    = atIndexOr 0 ws (wsi + 2)
            let w2Dqs = testWord8s (w2 .^. rdqs)
            let w2Nls = testWord8s (w2 .^. rnls)
            let w2Dls = testWord8s (w2 .^. rdls)
            let w3    = atIndexOr 0 ws (wsi + 3)
            let w3Dqs = testWord8s (w3 .^. rdqs)
            let w3Nls = testWord8s (w3 .^. rnls)
            let w3Dls = testWord8s (w3 .^. rdls)
            let w4    = atIndexOr 0 ws (wsi + 4)
            let w4Dqs = testWord8s (w4 .^. rdqs)
            let w4Nls = testWord8s (w4 .^. rnls)
            let w4Dls = testWord8s (w4 .^. rdls)
            let w5    = atIndexOr 0 ws (wsi + 5)
            let w5Dqs = testWord8s (w5 .^. rdqs)
            let w5Nls = testWord8s (w5 .^. rnls)
            let w5Dls = testWord8s (w5 .^. rdls)
            let w6    = atIndexOr 0 ws (wsi + 6)
            let w6Dqs = testWord8s (w6 .^. rdqs)
            let w6Nls = testWord8s (w6 .^. rnls)
            let w6Dls = testWord8s (w6 .^. rdls)
            let w7    = atIndexOr 0 ws (wsi + 7)
            let w7Dqs = testWord8s (w7 .^. rdqs)
            let w7Nls = testWord8s (w7 .^. rnls)
            let w7Dls = testWord8s (w7 .^. rdls)
            let wDqs  = (w7Dqs  .<. 56) .|. (w6Dqs .<. 48) .|. (w5Dqs .<. 40) .|. (w4Dqs .<. 32) .|. (w3Dqs .<. 24) .|. (w2Dqs .<. 16) .|. (w1Dqs .<. 8) .|. w0Dqs
            let wNls  = (w7Nls  .<. 56) .|. (w6Nls .<. 48) .|. (w5Nls .<. 40) .|. (w4Nls .<. 32) .|. (w3Nls .<. 24) .|. (w2Nls .<. 16) .|. (w1Nls .<. 8) .|. w0Nls
            let wDls  = (w7Dls  .<. 56) .|. (w6Dls .<. 48) .|. (w5Dls .<. 40) .|. (w4Dls .<. 32) .|. (w3Dls .<. 24) .|. (w2Dls .<. 16) .|. (w1Dls .<. 8) .|. w0Dls
            let numWordQuotes = comp wDqs
            let wMask = toggle64 numQuotes numWordQuotes
            DVSM.unsafeWrite markers  (fromIntegral ui) (comp (wNls .&. wDls) .&. wMask)
            DVSM.unsafeWrite newlines (fromIntegral ui) (comp  wNls           .&. wMask)

nextCursor :: (Rank1 s, Select1 s) => DsvCursor t s -> DsvCursor t s
nextCursor cursor = cursor
  { dsvCursorPosition = newPos
  }
  where currentRank = rank1   (dsvCursorMarkers cursor) (dsvCursorPosition cursor)
        newPos      = select1 (dsvCursorMarkers cursor) (currentRank + 1)