module Collision.Projectiles
    ( checkProjectilesCollisions
    ) where

import qualified Data.Set as S

import Collision
import Enemy
import Level.Room
import Level.Room.Item as RI
import Msg
import Player
import Projectile
import Util
import World.Surface as S

checkProjectilesCollisions :: Player -> [Some Enemy] -> Room -> [Some Projectile] -> [Msg ThinkCollisionMsgsPhase]
checkProjectilesCollisions player enemies room projectiles = foldr checkCollisions [] projectiles
    where
        checkCollisions :: Some Projectile -> [Msg ThinkCollisionMsgsPhase] -> [Msg ThinkCollisionMsgsPhase]
        checkCollisions (Some proj) !msgs = (_processCollisions proj) collisions proj ++ msgs
            where
                surfaceCollisions  = checkProjectileSurfaceCollisions proj (roomSurfaces room)
                enemyCollisions    = checkProjectileEnemyCollisions proj enemies
                playerCollisions   = checkProjectilePlayerCollisions proj player
                roomItemCollisions = checkProjectileRoomItemCollisions proj (_items room)
                collisions         = concat
                    [ surfaceCollisions
                    , enemyCollisions
                    , playerCollisions
                    , roomItemCollisions
                    ]

notRegistered :: ProjectileRegisteredCollision -> Projectile d -> Bool
notRegistered registeredCollision proj = registeredCollision `S.notMember` _registeredCollisions proj

registered :: ProjectileRegisteredCollision -> Projectile d -> Bool
registered registeredCollision proj = registeredCollision `S.member` _registeredCollisions proj

checkProjectileSurfaceCollisions :: Projectile d -> [Surface] -> [ProjectileCollision]
checkProjectileSurfaceCollisions proj surfaces
    | ProjRegisteredSurfaceCollision `notRegistered` proj = []
    | otherwise                                           =
        let projHbx = projectileHitbox proj
        in
            [ ProjSurfaceCollision (S._hitbox surface) (S._type surface)
            | surface <- filter (intersectsHitbox projHbx . S._hitbox) surfaces
            ]

checkProjectileEnemyCollisions :: Projectile d -> [Some Enemy] -> [ProjectileCollision]
checkProjectileEnemyCollisions proj enemies
    | ProjRegisteredEnemyCollision `registered` proj     = enemyCollisions enemies
    | ProjRegisteredEnemyRealCollision `registered` proj =
        let enemies' = filter (\(Some e) -> _dummyType e == EnemyRealType) enemies
        in enemyCollisions enemies'
    | otherwise                                          = []
    where
        enemyCollisions :: [Some Enemy] -> [ProjectileCollision]
        enemyCollisions es = [ProjEnemyCollision e | Some e <- collidingEs]
            where collidingEs = [Some e | Some e <- es, projectileHitbox proj `intersectsHitbox` enemyHitbox e]

checkProjectilePlayerCollisions :: Projectile d -> Player -> [ProjectileCollision]
checkProjectilePlayerCollisions proj player
    | ProjRegisteredPlayerCollision `notRegistered` proj = []
    | otherwise                                          = if
        | projectileHitbox proj `intersectsHitbox` playerHitbox player -> [ProjPlayerCollision player]
        | otherwise                                                    -> []

checkProjectileRoomItemCollisions :: Projectile d -> [Some RoomItem] -> [ProjectileCollision]
checkProjectileRoomItemCollisions proj roomItems
    | ProjRegisteredRoomItemCollision `notRegistered` proj = []
    | otherwise                                            =
        let
            collidingItems =
                [ Some ri
                | Some ri <- roomItems
                , _isAttackable ri
                , projectileHitbox proj `intersectsHitbox` RI._hitbox ri
                ]
        in [ProjRoomItemCollision (Some roomItem) | Some roomItem <- collidingItems]

module Collision.Projectiles
    ( checkProjectilesCollisions
    ) where

import qualified Data.Set as S

import Collision
import Enemy
import Level.Room
import Level.Room.Item as RI
import Msg
import Player
import Projectile
import Util
import World.Surface as S

checkProjectilesCollisions :: Player -> [Some Enemy] -> Room -> [Some Projectile] -> [Msg ThinkCollisionMsgsPhase]
checkProjectilesCollisions player enemies room projectiles = foldr checkCollisions [] projectiles
    where
        checkCollisions :: Some Projectile -> [Msg ThinkCollisionMsgsPhase] -> [Msg ThinkCollisionMsgsPhase]
        checkCollisions (Some proj) !msgs = (_processCollisions proj) collisions proj ++ msgs
            where
                surfaceCollisions  = checkProjectileSurfaceCollisions proj (roomSurfaces room)
                enemyCollisions    = checkProjectileEnemyCollisions proj enemies
                playerCollisions   = checkProjectilePlayerCollisions proj player
                roomItemCollisions = checkProjectileRoomItemCollisions proj (_items room)
                collisions         = concat
                    [ surfaceCollisions
                    , enemyCollisions
                    , playerCollisions
                    , roomItemCollisions
                    ]

notRegistered :: ProjectileRegisteredCollision -> Projectile d -> Bool
notRegistered registeredCollision proj = registeredCollision `S.notMember` _registeredCollisions proj

registered :: ProjectileRegisteredCollision -> Projectile d -> Bool
registered registeredCollision proj = registeredCollision `S.member` _registeredCollisions proj

checkProjectileSurfaceCollisions :: Projectile d -> [Surface] -> [ProjectileCollision]
checkProjectileSurfaceCollisions proj surfaces
    | ProjRegisteredSurfaceCollision `notRegistered` proj = []
    | otherwise                                           =
        let projHbx = projectileHitbox proj
        in
            [ ProjSurfaceCollision (S._hitbox surface) (S._type surface)
            | surface <- filter (intersectsHitbox projHbx . S._hitbox) surfaces
            ]

checkProjectileEnemyCollisions :: Projectile d -> [Some Enemy] -> [ProjectileCollision]
checkProjectileEnemyCollisions proj enemies
    | ProjRegisteredEnemyCollision `registered` proj     = enemyCollisions enemies
    | ProjRegisteredEnemyRealCollision `registered` proj =
        let enemies' = filter (\(Some e) -> _dummyType e == EnemyRealType) enemies
        in enemyCollisions enemies'
    | otherwise                                          = []
    where
        enemyCollisions :: [Some Enemy] -> [ProjectileCollision]
        enemyCollisions es = [ProjEnemyCollision e | Some e <- collidingEs]
            where collidingEs = [Some e | Some e <- es, projectileHitbox proj `intersectsHitbox` enemyHitbox e]

checkProjectilePlayerCollisions :: Projectile d -> Player -> [ProjectileCollision]
checkProjectilePlayerCollisions proj player
    | ProjRegisteredPlayerCollision `notRegistered` proj = []
    | otherwise                                          = if
        | projectileHitbox proj `intersectsHitbox` playerHitbox player -> [ProjPlayerCollision player]
        | otherwise                                                    -> []

checkProjectileRoomItemCollisions :: Projectile d -> [Some RoomItem] -> [ProjectileCollision]
checkProjectileRoomItemCollisions proj roomItems
    | ProjRegisteredRoomItemCollision `notRegistered` proj = []
    | otherwise                                            =
        let
            collidingItems =
                [ Some ri
                | Some ri <- roomItems
                , _isAttackable ri
                , projectileHitbox proj `intersectsHitbox` RI._hitbox ri
                ]
        in [ProjRoomItemCollision (Some roomItem) | Some roomItem <- collidingItems]
