module Data.FilePlow.Ordered
    ( SeekTarget(..), seekTo )
where

import Data.FilePlow

import Numeric.Search

data SeekTarget b
    = StLargerThan !b
      -- ^ put the handle to the first entry that is larger than the provided value.
      -- Useful for files with entries in ASCending order
    | StSmallerThan !b
      -- ^ put the handle to the first entry that is smaller than the provided value.
      -- Useful for files with entries in DESCending order
    deriving (Show, Eq)

-- ^ place the file handling to a specific 'SeekTarget' provided a function that
-- extracts a value/entry at a specific position
seekTo ::
    (Ord b, Eq b, PlowHandle hdl)
    => hdl
    -> SeekTarget b
    -> (hdl -> IO b)
    -> IO Bool
seekTo hdl st extract =
    do s <- pFileSize hdl
       ranges <- searchM (fromTo 0 (s - 1)) divForever $ \pos ->
           do case st of
                StLargerThan _ -> pSeek hdl AbsoluteSeek pos
                StSmallerThan _ -> pSeek hdl AbsoluteSeek (s - 1 - pos)
              val <- extract hdl
              pure $
                  case st of
                    StLargerThan x -> (val >= x)
                    StSmallerThan x -> (val <= x)
       let tgtVal =
               case st of
                 StSmallerThan _ -> largest True ranges
                 StLargerThan _ -> smallest True ranges
       case tgtVal of
         Just pos ->
             case st of
               StLargerThan _ -> pSeek hdl AbsoluteSeek pos >> pure True
               StSmallerThan _ -> pSeek hdl AbsoluteSeek (s - 1 - pos) >> pure True
         Nothing -> pure False