{- |
Module testing for properties of creatures, whether they are reloading, etc.
Each function takes the world and a creature and returns a bool.
Note that the creature NEED NOT be the same as the creature with that id in the world, 
in fact in some cases a creature with that id may not even exist.
-}
module Dodge.Creature.Test
    where
import Dodge.Data
import Dodge.Base.Collide
import Dodge.Creature.Stance.Data
import Geometry
import SameConstr

import Data.List (find)
import Data.Maybe
import qualified Data.IntMap.Strict as IM
import Control.Lens
import Control.Monad.Reader

andTest :: (a -> Bool) -> (a -> Bool) -> a -> Bool
andTest f g a = f a && g a

onBoth :: (a -> b -> c) -> (d -> a) -> (d -> b) -> d -> c
onBoth f g h x = f (g x) (h x)

crIsReloading :: (World, Creature) -> Bool
crIsReloading (_,cr) = case cr ^? crInv . ix (_crInvSel cr) . wpReloadState of
    Just t -> t > 0
    _ -> False

crIsReloadingR :: Creature -> Reader World Bool
crIsReloadingR cr = return $ case cr ^? crInv . ix (_crInvSel cr) . wpReloadState of
    Just t -> t > 0
    _ -> False

crCanSeeCr :: Creature -> (World, Creature) -> Bool
crCanSeeCr tcr (w,cr) = hasLOS (_crPos cr) (_crPos tcr) w

crCanSeeR :: Creature -> Creature -> Reader World Bool
crCanSeeR tcr cr = reader $ \w -> hasLOS (_crPos cr) (_crPos tcr) w

crCanSeeCID :: Int -> (World, Creature) -> Bool
crCanSeeCID cid (w,cr) = hasLOS (_crPos cr) (_crPos $ _creatures w IM.! cid) w

crCanSeeCIDR :: Int -> Creature -> Reader World Bool
crCanSeeCIDR cid cr = reader $ \w -> hasLOS (_crPos cr) (_crPos $ _creatures w IM.! cid) w

crIsAiming :: (World,Creature) -> Bool
crIsAiming (_,cr) = _posture (_crStance cr) == Aiming

crIsAiming' :: Creature -> Bool
crIsAiming' cr = _posture (_crStance cr) == Aiming

crIsAimingR :: Creature -> Reader World Bool
crIsAimingR cr = return $ _posture (_crStance cr) == Aiming

crHasTarget :: (World,Creature) -> Bool
crHasTarget (_,cr) = isJust $ cr ^? crIntention . targetCr . _Just

crHasTargetR :: Creature -> Reader World Bool
crHasTargetR cr = return $ isJust $ cr ^? crIntention . targetCr . _Just

crHasTargetLOS :: (World,Creature) -> Bool
crHasTargetLOS (w,cr) = case cr ^? crIntention . targetCr . _Just of
    Just i -> crCanSeeCr i (w,cr) 
    Nothing -> False

crHasTargetLOSR :: Creature -> Reader World Bool
crHasTargetLOSR cr = reader $ \w -> case cr ^? crIntention . targetCr . _Just of
    Just i -> crCanSeeCr i (w,cr) 
    Nothing -> False

crSafeDistFromTarg :: Float -> (World,Creature) -> Bool
crSafeDistFromTarg d (_,cr) = case cr ^? crIntention . targetCr . _Just of
    Just tcr -> dist (_crPos cr) (_crPos tcr) > d
    Nothing -> True

crSafeDistFromTargR :: Float -> Creature -> Reader World Bool
crSafeDistFromTargR d cr = return $ case cr ^? crIntention . targetCr . _Just of
    Just tcr -> dist (_crPos cr) (_crPos tcr) > d
    Nothing -> True

crStratConMatches :: Strategy -> (World,Creature) -> Bool
crStratConMatches strat (_,cr) = eqConstr strat (_crStrategy $ _crActionPlan cr)

crStratConMatchesR :: Strategy -> Creature -> Reader World Bool
crStratConMatchesR strat cr = return $ eqConstr strat (_crStrategy $ _crActionPlan cr)

crAwayFromPost :: (World,Creature) -> Bool
crAwayFromPost (_,cr) = case find sentinelGoal $ _crGoal $ _crActionPlan cr of
    Just (SentinelAt p _) -> dist p (_crPos cr) > 15
    _ -> False
  where
    sentinelGoal (SentinelAt _ _) = True
    sentinelGoal _ = False

crAwayFromPostR :: Creature -> Reader World Bool
crAwayFromPostR cr = return $ case find sentinelGoal $ _crGoal $ _crActionPlan cr of
    Just (SentinelAt p _) -> dist p (_crPos cr) > 15
    _ -> False
  where
    sentinelGoal (SentinelAt _ _) = True
    sentinelGoal _ = False

crHasAmmo :: (World,Creature) -> Bool
crHasAmmo (_,cr) = maybe False (> 0) $ cr ^? crInv . ix (_crInvSel cr) . wpLoadedAmmo

crCanShoot :: (World,Creature) -> Bool
crCanShoot p = crIsAiming p && crHasAmmo p
