module Network.QUIC.Types.Ack where

import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet

type PacketNumber = Int

type Range = Int
type Gap = Int

data AckInfo = AckInfo PacketNumber Range [(Gap, Range)]
    deriving (Eq, Show)

ackInfo0 :: AckInfo
ackInfo0 = AckInfo (-1) 0 []

-- |
-- >>> toAckInfo [9]
-- AckInfo 9 0 []
-- >>> toAckInfo [9,8,7]
-- AckInfo 9 2 []
-- >>> toAckInfo [8,7,3,2]
-- AckInfo 8 1 [(2,1)]
-- >>> toAckInfo [9,8,7,5,4]
-- AckInfo 9 2 [(0,1)]
toAckInfo :: [PacketNumber] -> AckInfo
toAckInfo [] = error "toAckInfo"
toAckInfo [l] = AckInfo l 0 []
toAckInfo (l : ls) = ack l ls 0
  where
    ack _ [] fr = AckInfo l fr []
    ack p (x : xs) fr
        | p - 1 == x = ack x xs (fr + 1)
        | otherwise = AckInfo l fr $ ranges x xs (fromIntegral (p - x) - 2) 0
    ranges _ [] g r = [(g, r)]
    ranges p (x : xs) g r
        | p - 1 == x = ranges x xs g (r + 1)
        | otherwise = (g, r) : ranges x xs (fromIntegral (p - x) - 2) 0

-- |
-- >>> fromAckInfo $ AckInfo 9 0 []
-- [9]
-- >>> fromAckInfo $ AckInfo 9 2 []
-- [7,8,9]
-- >>> fromAckInfo $ AckInfo 8 1 [(2,1)]
-- [2,3,7,8]
-- >>> fromAckInfo $ AckInfo 9 2 [(0,1)]
-- [4,5,7,8,9]
fromAckInfo :: AckInfo -> [PacketNumber]
fromAckInfo (AckInfo lpn fr grs) = loop grs [stt .. lpn]
  where
    stt = lpn - fromIntegral fr
    loop _ [] = error "loop"
    loop [] acc = acc
    loop ((g, r) : xs) acc@(s : _) = loop xs ([z - fromIntegral r .. z] ++ acc)
      where
        z = s - fromIntegral g - 2

-- |
-- >>> fromAckInfoWithMin (AckInfo 9 0 []) 1
-- [9]
-- >>> fromAckInfoWithMin (AckInfo 9 2 []) 8
-- [8,9]
-- >>> fromAckInfoWithMin (AckInfo 8 1 [(2,1)]) 3
-- [3,7,8]
-- >>> fromAckInfoWithMin (AckInfo 9 2 [(0,1)]) 8
-- [8,9]
fromAckInfoWithMin :: AckInfo -> PacketNumber -> [PacketNumber]
fromAckInfoWithMin (AckInfo lpn fr grs) lim
    | stt < lim = [lim .. lpn]
    | otherwise = loop grs [stt .. lpn]
  where
    stt = lpn - fromIntegral fr
    loop _ [] = error "loop"
    loop [] acc = acc
    loop ((g, r) : xs) acc@(s : _)
        | z < lim = acc
        | otherwise = loop xs ([r' .. z] ++ acc)
      where
        z = s - fromIntegral g - 2
        r' = max lim (z - fromIntegral r)

fromAckInfoToPred :: AckInfo -> (PacketNumber -> Bool)
fromAckInfoToPred (AckInfo lpn fr grs) =
    \x -> any (f x) $ loop grs [(stt, lpn)]
  where
    f x (l, u) = l <= x && x <= u
    stt = lpn - fromIntegral fr
    loop _ [] = error "loop"
    loop [] acc = acc
    loop ((g, r) : xs) acc@((s, _) : _) = loop xs $ (z - fromIntegral r, z) : acc
      where
        z = s - fromIntegral g - 2

----------------------------------------------------------------

newtype PeerPacketNumbers = PeerPacketNumbers IntSet
    deriving (Eq, Show)

emptyPeerPacketNumbers :: PeerPacketNumbers
emptyPeerPacketNumbers = PeerPacketNumbers IntSet.empty
