module Dodge.WorldEvent.Shockwave
    ( makeShockwaveAt
    , inverseShockwaveAt
    , drawShockwave
    , mvShockwave
    ) where
import Dodge.Data
import Dodge.Wall.Damage
import Dodge.Base
import Dodge.Zone
import Geometry
import Picture
import LensHelp

--import Data.Foldable
import qualified Data.IntMap.Strict as IM
import qualified Streaming.Prelude as S
-- currently very effective against walls
makeShockwaveAt 
    :: [Int]    -- ^ IDs of invulnerable creatures.
    -> Point2   -- ^ Center of shockwave.
    -> Float    -- ^ Maximal radius.
    -> Int      -- ^ Damage caused per frame.
    -> Float    -- ^ Amount of pushback per frame.
    -> Color    -- ^ Color of shockwave.
    -> World    -- ^ Start world.
    -> World
makeShockwaveAt is p rad dam push col = instantParticles .:~ Shockwave
    { _ptDraw       = drawShockwave
    , _ptUpdate     = mvShockwave is
    , _ptColor      = col
    , _ptPos       = p
    , _ptRad       = rad
    , _ptDam       = dam
    , _ptPush      = push
    , _ptMaxTime   = 10
    , _ptTimer     = 10
    }
{- Shockwave picture.  -}
drawShockwave :: Particle -> Picture
drawShockwave pt = setDepth 20 
    . setLayer BloomLayer
    . uncurryV translate (_ptPos pt) 
    . color (_ptColor pt) 
    $ thickCircle rad thickness
  where
    r    = _ptRad pt
    thickness = tFraction**2 * r 
    rad = r - (3/4) * r * tFraction
    tFraction = fromIntegral (_ptTimer pt) / fromIntegral (_ptMaxTime pt)

mvShockwave
    :: [Int]    -- ^ IDs of invulnerable creatures.
    -> World -> Particle -> (World, Maybe Particle)
mvShockwave is w pt 
    | _ptTimer pt <= 0 = (        w , Nothing)
    | otherwise         = (doDams w , Just $ pt & ptTimer -~ 1)
  where 
    r    = _ptRad pt
    p    = _ptPos pt
    push = _ptPush pt
    dam  = _ptDam pt
    t    = _ptTimer pt
    tFraction = fromIntegral t / fromIntegral (_ptMaxTime pt)
    rad = r - (3/4) * r * tFraction
    doDams w' = over creatures (IM.map damCr) 
        . runIdentity
        $ S.fold_
            (flip $ damageWall (Damage EXPLOSIVE 10000 p p p NoDamageEffect))
            w'
            id
            hitBlocks
    hitBlocks = S.map snd . overlapCircWalls p rad $ wlsInsideCirc p rad w 
        -- this is not expansive enough
    damCr cr 
        | _crID cr `elem` is || dist (_crPos cr) p >= rad + _crRad cr  = cr
        | otherwise = cr & crState . csDamage .:~
            Damage PUSHDAM dam cpos cpos cpos 
                (PushBackDamage (25 * push *.* squashNormalizeV (_crPos cr -.- p)))
          where
            cpos = _crPos cr
{- Create a shockwave going from an outside circle into a center point.  -}
inverseShockwaveAt 
    :: Point2   -- Center position
    -> Float    -- Radius
    -> Int      -- Damage
    -> Float    -- Push amount
    -> World 
    -> World
inverseShockwaveAt p rad dam push = instantParticles .:~ Shockwave
    { _ptDraw = drawInverseShockwave
    , _ptUpdate = moveInverseShockwave
    , _ptColor      = cyan
    , _ptPos       = p
    , _ptRad       = rad
    , _ptDam       = dam
    , _ptPush      = push
    , _ptMaxTime   = 10
    , _ptTimer     = 10
    }
moveInverseShockwave 
    :: World
    -> Particle 
    -> (World, Maybe Particle)
moveInverseShockwave w pt 
    | t <= 0    = (     w, Nothing)
    | otherwise = (dams w, Just $ ptTimer -~ 1 $ pt )
  where 
    p = _ptPos pt
    r = _ptRad pt
    t = _ptTimer pt
    rad = r - 0.1 * r * fromIntegral (10 - t)
    dams = over creatures (IM.map damCr) -- . flip (foldr (damageBlocksBy 1)) hitBlocks
--    hitBlocks = wallsOnCirc' p rad $ wallsNearPoint' p w 
    damCr cr 
        | dist (_crPos cr) p >= rad + _crRad cr = cr
        | otherwise = cr & crState . csDamage .:~
            Damage PUSHDAM 1 cpos cpos cpos (PushBackDamage $ 25 *.* squashNormalizeV (p -.- cpos))
          where
            cpos = _crPos cr

drawInverseShockwave :: Particle -> Picture
drawInverseShockwave  pt 
    = setLayer BloomLayer $ setDepth 20 $ uncurryV translate p 
                          $ color cyan $ thickCircle rad thickness
  where
    p = _ptPos pt
    r = _ptRad pt
    t = _ptTimer pt
    rad = r - 0.1 * r * fromIntegral (10 - t)
    thickness = fromIntegral (10 - t) **2 * rad / 40
