-- | Generate an item based on level.
newItem :: Kind.COps -> FlavourMap -> DiscoveryKindRev -> UniqueSet
        -> Freqs ItemKind -> Int -> LevelId -> AbsDepth -> AbsDepth
        -> Rnd (Maybe ( ItemKnown, ItemFull, ItemDisco
                      , ItemSeed, GroupName ItemKind ))
newItem Kind.COps{coitem=Kind.Ops{ofoldrGroup}}
        flavour discoRev uniqueSet itemFreq lvlSpawned jlid
        ldepth@(AbsDepth ldAbs) totalDepth@(AbsDepth depth) = do
  -- Effective generation depth of actors (not items) increases with spawns.
  let scaledDepth = ldAbs * 10 `div` depth
      numSpawnedCoeff = lvlSpawned `div` 2
      ldSpawned = max ldAbs  -- the first fast spawns are of the nominal level
                  $ min depth
                  $ ldAbs + numSpawnedCoeff - scaledDepth
      findInterval _ x1y1 [] = (x1y1, (11, 0))
      findInterval ld x1y1 ((x, y) : rest) =
        if fromIntegral ld * 10 <= x * fromIntegral depth
        then (x1y1, (x, y))
        else findInterval ld (x, y) rest
      linearInterpolation ld dataset =
        -- We assume @dataset@ is sorted and between 0 and 10.
        let ((x1, y1), (x2, y2)) = findInterval ld (0, 0) dataset
        in ceiling
           $ fromIntegral y1
             + fromIntegral (y2 - y1)
               * (fromIntegral ld * 10 - x1 * fromIntegral depth)
               / ((x2 - x1) * fromIntegral depth)
      f _ _ _ ik _ acc | ik `ES.member` uniqueSet = acc
      f itemGroup q p ik kind acc =
        -- Don't consider lvlSpawned for uniques.
        let ld = if IK.Unique `elem` IK.iaspects kind then ldAbs else ldSpawned
            rarity = linearInterpolation ld (IK.irarity kind)
        in (q * p * rarity, ((ik, kind), itemGroup)) : acc
      g (itemGroup, q) = ofoldrGroup itemGroup (f itemGroup q) []
      freqDepth = concatMap g itemFreq
      freq = toFreq ("newItem ('" <> tshow ldSpawned <> ")") freqDepth
  if nullFreq freq then return Nothing
  else do
    ((itemKindId, itemKind), itemGroup) <- frequency freq
    -- Number of new items/actors unaffected by number of spawned actors.
    itemN <- castDice ldepth totalDepth (IK.icount itemKind)
    seed <- fmap toEnum random
    let itemBase = buildItem flavour discoRev itemKindId itemKind jlid
        itemK = max 1 itemN
        itemTimer = []
        itemDiscoData = ItemDisco {itemKindId, itemKind, itemAE = Just iae}
        itemDisco = Just itemDiscoData
        -- Bonuses on items/actors unaffected by number of spawned actors.
        iae = seedToAspectsEffects seed itemKind ldepth totalDepth
        itemFull = ItemFull {..}
    return $ Just ( (jkindIx itemBase, iae)
                  , itemFull
                  , itemDiscoData
                  , seed
                  , itemGroup )

-- | Flavours assigned by the server to item kinds, in this particular game.
newItem :: Kind.COps -> FlavourMap -> DiscoveryKindRev -> UniqueSet
        -> Freqs ItemKind -> Int -> LevelId -> AbsDepth -> AbsDepth
        -> Rnd (Maybe ( ItemKnown, ItemFull, ItemDisco
                      , ItemSeed, GroupName ItemKind ))
newItem Kind.COps{coitem=Kind.Ops{ofoldrGroup}}
        flavour discoRev uniqueSet itemFreq lvlSpawned jlid
        ldepth@(AbsDepth ldAbs) totalDepth@(AbsDepth depth) = do
  -- Effective generation depth of actors (not items) increases with spawns.
  let scaledDepth = ldAbs * 10 `div` depth
      numSpawnedCoeff = lvlSpawned `div` 2
      ldSpawned = max ldAbs  -- the first fast spawns are of the nominal level
                  $ min depth
                  $ ldAbs + numSpawnedCoeff - scaledDepth
      findInterval _ x1y1 [] = (x1y1, (11, 0))
      findInterval ld x1y1 ((x, y) : rest) =
        if fromIntegral ld * 10 <= x * fromIntegral depth
        then (x1y1, (x, y))
        else findInterval ld (x, y) rest
      linearInterpolation ld dataset =
        -- We assume @dataset@ is sorted and between 0 and 10.
        let ((x1, y1), (x2, y2)) = findInterval ld (0, 0) dataset
        in ceiling
           $ fromIntegral y1
             + fromIntegral (y2 - y1)
               * (fromIntegral ld * 10 - x1 * fromIntegral depth)
               / ((x2 - x1) * fromIntegral depth)
      f _ _ _ ik _ acc | ik `ES.member` uniqueSet = acc
      f itemGroup q p ik kind acc =
        -- Don't consider lvlSpawned for uniques.
        let ld = if IK.Unique `elem` IK.iaspects kind then ldAbs else ldSpawned
            rarity = linearInterpolation ld (IK.irarity kind)
        in (q * p * rarity, ((ik, kind), itemGroup)) : acc
      g (itemGroup, q) = ofoldrGroup itemGroup (f itemGroup q) []
      freqDepth = concatMap g itemFreq
      freq = toFreq ("newItem ('" <> tshow ldSpawned <> ")") freqDepth
  if nullFreq freq then return Nothing
  else do
    ((itemKindId, itemKind), itemGroup) <- frequency freq
    -- Number of new items/actors unaffected by number of spawned actors.
    itemN <- castDice ldepth totalDepth (IK.icount itemKind)
    seed <- fmap toEnum random
    let itemBase = buildItem flavour discoRev itemKindId itemKind jlid
        itemK = max 1 itemN
        itemTimer = []
        itemDiscoData = ItemDisco {itemKindId, itemKind, itemAE = Just iae}
        itemDisco = Just itemDiscoData
        -- Bonuses on items/actors unaffected by number of spawned actors.
        iae = seedToAspectsEffects seed itemKind ldepth totalDepth
        itemFull = ItemFull {..}
    return $ Just ( (jkindIx itemBase, iae)
                  , itemFull
                  , itemDiscoData
                  , seed
                  , itemGroup )

-- | Flavours assigned by the server to item kinds, in this particular game.
newItem Kind.COps{coitem=Kind.Ops{ofoldrGroup}}
        flavour discoRev uniqueSet itemFreq lvlSpawned jlid
        ldepth@(AbsDepth ldAbs) totalDepth@(AbsDepth depth) = do
  -- Effective generation depth of actors (not items) increases with spawns.
  let scaledDepth = ldAbs * 10 `div` depth
      numSpawnedCoeff = lvlSpawned `div` 2
      ldSpawned = max ldAbs  -- the first fast spawns are of the nominal level
                  $ min depth
                  $ ldAbs + numSpawnedCoeff - scaledDepth
      findInterval _ x1y1 [] = (x1y1, (11, 0))
      findInterval ld x1y1 ((x, y) : rest) =
        if fromIntegral ld * 10 <= x * fromIntegral depth
        then (x1y1, (x, y))
        else findInterval ld (x, y) rest
      linearInterpolation ld dataset =
        -- We assume @dataset@ is sorted and between 0 and 10.
        let ((x1, y1), (x2, y2)) = findInterval ld (0, 0) dataset
        in ceiling
           $ fromIntegral y1
             + fromIntegral (y2 - y1)
               * (fromIntegral ld * 10 - x1 * fromIntegral depth)
               / ((x2 - x1) * fromIntegral depth)
      f _ _ _ ik _ acc | ik `ES.member` uniqueSet = acc
      f itemGroup q p ik kind acc =
        -- Don't consider lvlSpawned for uniques.
        let ld = if IK.Unique `elem` IK.iaspects kind then ldAbs else ldSpawned
            rarity = linearInterpolation ld (IK.irarity kind)
        in (q * p * rarity, ((ik, kind), itemGroup)) : acc
      g (itemGroup, q) = ofoldrGroup itemGroup (f itemGroup q) []
      freqDepth = concatMap g itemFreq
      freq = toFreq ("newItem ('" <> tshow ldSpawned <> ")") freqDepth
  if nullFreq freq then return Nothing
  else do
    ((itemKindId, itemKind), itemGroup) <- frequency freq
    -- Number of new items/actors unaffected by number of spawned actors.
    itemN <- castDice ldepth totalDepth (IK.icount itemKind)
    seed <- fmap toEnum random
    let itemBase = buildItem flavour discoRev itemKindId itemKind jlid
        itemK = max 1 itemN
        itemTimer = []
        itemDiscoData = ItemDisco {itemKindId, itemKind, itemAE = Just iae}
        itemDisco = Just itemDiscoData
        -- Bonuses on items/actors unaffected by number of spawned actors.
        iae = seedToAspectsEffects seed itemKind ldepth totalDepth
        itemFull = ItemFull {..}
    return $ Just ( (jkindIx itemBase, iae)
                  , itemFull
                  , itemDiscoData
                  , seed
                  , itemGroup )

-- | Flavours assigned by the server to item kinds, in this particular game.