{-# LANGUAGE TupleSections #-}
module Dodge.Creature.Impulse
    ( impulsiveAIBefore
    , followThenClearImpulses
    ) where
import Dodge.Data
import Dodge.Creature.Vocalization
import Dodge.Creature.Impulse.Movement
import Dodge.Creature.Impulse.UseItem
import Dodge.SoundLogic
import Geometry
import LensHelp

import qualified Data.IntMap.Strict as IM
import System.Random
import Control.Monad.State
import Data.Bifunctor
--import Data.Maybe
--
impulsiveAIBeforeAfter 
    :: (World -> Creature -> Creature)
    -> (World -> Creature -> Creature)
    -> Creature -> World -> World
impulsiveAIBeforeAfter startup endup cr w = w' & creatures . ix (_crID cr) .~ endup w' cr'
  where
    w' = g w
    (g,cr') = impulsiveAI startup cr w

impulsiveAIBefore :: (World -> Creature -> Creature)
    -> Creature -> World -> World
impulsiveAIBefore f cr w = g w & creatures . ix (_crID cr) .~ cr'
  where
    (g,cr') = impulsiveAI f cr w

impulsiveAI 
    :: (World -> Creature -> Creature) -- | internal creature update
    -> Creature
    -> World
    -> (World -> World , Creature)
impulsiveAI f cr w = followImpulses w $ f w cr


followThenClearImpulses :: Creature -> World -> World
followThenClearImpulses = impulsiveAIBeforeAfter (const id) (const $ crActionPlan . apImpulse .~ [])

followImpulses :: World -> Creature -> (World -> World, Creature)
followImpulses w cr = foldr f (id, cr) (_apImpulse $ _crActionPlan cr)
  where
    f imp (theupdate,cr') = first (. theupdate) $ followImpulse cr' w imp

followImpulse :: Creature -> World -> Impulse -> (World -> World , Creature)
followImpulse cr w imp = case imp of
    RandomImpulse rimp -> 
        let (newimp,newgen) = runState rimp (_randGen w)
        in first ((randGen .~ newgen) .) $ followImpulse cr w newimp
    Bark sid        -> (soundStart (CrMouth cid) cpos sid Nothing, resetCrVocCoolDown cr)
    Move p          -> crup $ crMvBy p cr
    MoveForward x   -> crup $ crMvForward x cr
    Turn a          -> crup $ creatureTurn a cr
    TurnToward p a  -> crup $ creatureTurnToward p a cr
    TurnTo p        -> crup $ creatureTurnTo p cr
    ChangePosture post  -> crup $ cr & crStance . posture .~ post
    UseItem         -> (useItem cr, cr)
    SwitchToItem i  -> crup $ cr & crInvSel .~ InvSel i NoInvSelAction
    Melee cid'      -> (hitCr cid'
        , crMvBy (10 *.* normalizeV (posFromID cid' -.- cpos)) $ cr & crMeleeCooldown .~ 20)
    RandomTurn a    -> (randGen .~ snd (rr a), creatureTurn (fst $ rr a) cr)
    MakeSound sid   -> ( soundStart (CrSound (_crID cr)) (_crPos cr) sid Nothing , cr )
    DropItem        -> undefined
    ChangeStrategy strat    -> crup $ cr & crActionPlan . apStrategy .~ strat
    AddGoal gl      -> crup $ cr & crActionPlan . apGoal .:~ gl 
    ArbitraryImpulseFunction f  -> crup $ f w cr
    ArbitraryImpulse f  -> followImpulse cr w (f cr w) 
    ArbitraryImpulseEffect f -> (f cr, cr)
    ImpulseUseTargetCID f   -> case cr ^? crIntention . targetCr . _Just of
        Just tcr -> followImpulse cr w (f $ _crID tcr)
        _        -> crup cr
    ImpulseUseTarget f -> case cr ^? crIntention . targetCr . _Just of
        Just tcr -> followImpulse cr w (f tcr)
        _        -> crup cr
    ImpulseUseAheadPos f -> followImpulse cr w (f (_crPos cr +.+ 20 *.* unitVectorAtAngle (_crDir cr)))
    MvForward       -> crup $ crMvForward speed cr
    MvTurnToward p  -> crup
        $ creatureTurnToward p (turnRad $ safeAngleVV (p -.- cpos) (unitVectorAtAngle cdir)) cr
  where
    crup = (id,)
    mvType = _crMvType cr
    speed = _mvSpeed mvType
    turnRad = _mvTurnRad mvType
    cpos = _crPos cr
    cdir = _crDir cr
    cid  = _crID cr
    posFromID cid' = _crPos $ _creatures w IM.! cid'
    rr a = randomR (-a,a) $ _randGen w
    hitCr i = (creatures . ix i . crState . csDamage 
        .:~ Damage BLUNT 100 cpos (posFromID i) (posFromID i) NoDamageEffect
        )
        . soundStart (CrSound cid) cpos hitS Nothing
