module Dodge.Item.Weapon.Grenade
     where
import Dodge.Data
import Dodge.Picture.Layer
import Dodge.WorldEvent.Explosion
import Dodge.Item.Weapon.TriggerType
import Dodge.Item.Weapon.Remote
import Dodge.Default
import Dodge.Default.Weapon
import Dodge.Base
import Dodge.Zone
import Dodge.SoundLogic
import Dodge.Item.Draw
import Dodge.Item.Weapon.InventoryDisplay
import Dodge.Item.Weapon.ExtraEffect
import Dodge.Item.Attachment
import Picture
import Geometry
import ShapePicture
import Shape
import qualified IntMapHelp as IM

import Data.Maybe
import Control.Lens

grenade :: Item
grenade = Throwable
    { _itName = "GRENADE " ++ show fuseTime
    , _itDimension = defaultItemDimension
    , _itCurseStatus = Uncursed
    , _itIdentity = Grenade
    , _itMaxStack = 8
    , _itAmount = 1
    , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 [(-3,-3),(-3,3),(3,3),(3,-3)]
    , _twMaxRange = 150
    , _twAccuracy = 30
    , _itUse = \_ -> throwGrenade makeExplosionAt
    , _itUseModifiers =
        [ useTimeCheckI
        ]
    , _itAimingSpeed = 1
    , _itAimingRange = 0
    , _itZoom       = defaultItZoom
    , _itAimZoom    = defaultItZoom {_itZoomMax = f fuseTime, _itZoomMin = f fuseTime}
    , _itEquipPict = pictureWeaponOnAim' $ \_ -> grenadePic fuseTime
    , _itID = Nothing
    , _itUseRate = 25 
    , _itUseTime = 0
    , _itAttachment = ItFuse fuseTime
    , _itInvColor = white
    , _itInvDisplay = basicWeaponDisplay
    , _itEffect = wpRecock
    , _itHammer = HammerUp
    , _itScroll = changeFuse
    , _itAimStance = OneHand
    }
  where 
    fuseTime = 50
    f x = 50 / fromIntegral x

moveGrenade :: Prop -> World -> World
moveGrenade pj w
    | _pjTimer pj <= 0 = w 
        & props %~ IM.delete pID 
        & _pjPayload pj (_pjPos (_props w IM.! pID)) 
    | otherwise = case collideCircWalls oldPos newPos 4 $ wallsNearPoint newPos w of
        Nothing -> w & props . ix pID %~ normalUpdate
        Just (p,v)  -> w
            & soundStart (Tap 0) p tapQuietS Nothing
            & props . ix pID %~ ( ( pjPos .~ p ) . ( pjTimer -~ 1 ) . (pjVel .~ v) )
  where 
    pID = _pjID pj
    normalUpdate = (pjTimer -~ 1) 
        . (pjZ %~ (max 0 . (+ _pjVelZ pj))) 
        . (pjVelZ -~ 0.1)
        . ( pjPos .~ newPos )
    oldPos = _pjPos pj
    newPos = _pjVel pj +.+ oldPos

grenadePic :: Int -> SPic
grenadePic time = 
    ( colorSH (dark $ dark green) $ upperPrismPolyHalf 5 $ polyCirc 3 5
    , pictures 
        [ color green $ arc (degToRad $ (179 * fromIntegral time / 50) - 180 ) 
                            (degToRad $ 180 - (179 * fromIntegral time / 50) ) 
                            5
        ]
    )

grenadeDraw :: Float -> Prop -> SPic
grenadeDraw dir prop = translateSPz z $ uncurryV translateSPf p $ rotateSP dir $ grenadePic time
  where
    p = _pjPos prop
    time = _pjTimer prop
    z = _pjZ prop

throwGrenade 
    :: (Point2 -> World -> World)   -- ^ Payload
    -> Creature
    -> World 
    -> World
throwGrenade thePayload cr w = setWp $ removePict $ over props addG w
  where 
    n = _crID cr
    addG = IM.insert i $ Bomb 
        { _pjPos = p
        , _pjZ = 10
        , _pjVelZ = 2
        , _pjVel = v
        , _prDraw = grenadeDraw dir
        , _pjID = i
        , _pjUpdate = moveGrenade
        , _pjPayload = thePayload
        , _pjTimer   = fuseTime
        }
    j = _crInvSel cr
    removePict = set (creatures . ix n . crInv . ix j . itEquipPict) $ \ _ _ -> (,) emptySH blank
    i = newProjectileKey w
    v' = 1 / (fromIntegral fuseTime * _cameraZoom w) *.* rotateV (_cameraRot w) ( _mousePos w)
    v | magV v' > 6 = 6 *.* normalizeV v'
      | otherwise   = v'
    p' = _crPos cr +.+ rotateV (_crDir cr) (V2 (_crRad cr) 0)
    p | circOnSomeWall p' 4 w = _crPos cr +.+ rotateV (_crDir cr) (V2 (_crRad cr-4) 0)
      | otherwise             = p'
    dir = argV v 
    setWp :: World -> World
    setWp w' = w' & creatures . ix n . crInv . ix j . itEffect .~ throwArmReset 20
    fuseTime = _itFuseTime $ _itAttachment $ _crInv cr IM.! j

throwArmReset :: Int -> ItEffect
throwArmReset x = ItInvEffect {_itInvEffect = f ,_itEffectCounter = x }
  where 
    f _ cr i = creatures . ix (_crID cr) . crInv %~ IM.adjust counterDown i
    counterDown it 
        | _itEffectCounter (_itEffect it) == 0 = it 
            & itHammer .~ HammerUp 
            & itEquipPict .~ pictureWeaponOnAim' (\_ -> grenadePic 50)
        | otherwise = it & itEffect . itEffectCounter -~ 1

--flameGrenade :: Item
--flameGrenade = grenade {
--     _itName = "FLMGREN " ++ show fuseTime
--     , _itUse = \_ -> throwGrenade makeFlameExplosionAt
--    }
--  where 
--    fuseTime = 50 :: Int
--
--teslaGrenade :: Item
--teslaGrenade = grenade {
--     _itName = "TLSGREN " ++ show fuseTime
--     , _itUse = \_ -> throwGrenade makeTeslaExplosionAt
--    }
--  where 
--    fuseTime = 50 :: Int
--
--retireRemoteBomb :: Int -> Int -> Int -> World -> World
--retireRemoteBomb itid 0 pjid w = w
--    & pointToItem (_itemPositions w IM.! itid) %~ 
--        ( (itAttachment . scopePos .~ V2 0 0)
--        . (itZoom .~ defaultItZoom)
--        . (itUse .~ const throwRemoteBomb)
--        )
--    & props %~ IM.delete pjid
--retireRemoteBomb itid t pjid w = setScope w 
--    & props . ix pjid . pjUpdate .~ (\_ -> retireRemoteBomb itid (t-1) pjid)
--  where
--    setScope w' = case _itemPositions w' IM.! itid of
--        InInv cid invid -> w' 
--            & creatures . ix cid . crInv . ix invid . itAttachment 
--                    . scopePos .~ (pos -.- _crPos (_creatures w' IM.! cid))
--        _   -> w'
--    pos = fromMaybe (V2 0 0) $ w ^? props . ix pjid . pjPos

moveRemoteBomb :: Int -> Int -> Int -> World -> World
moveRemoteBomb itid time pID w 
    | time < -4 = updatePicture
                $ set (props .ix pID.pjUpdate) (\_ -> moveRemoteBomb itid (f time) pID)
                    w
    | time < 2 = case hitWl of
            Just (p,_) -> soundStart (Tap 0) p tapQuietS Nothing $ halfV updatedWorld
            _      -> halfV updatedWorld
    | otherwise = case hitWl of
            Just (p,_) -> soundStart (Tap 0) p tapQuietS Nothing updatedWorld
            _      -> updatedWorld
  where 
    updatedWorld 
        = updateV $ set (props . ix pID . pjPos) finalPos
            $ updatePicture
            $ set (props .ix pID.pjUpdate) (\_ -> moveRemoteBomb itid (time-1) pID)
                w
    pj     = _props w IM.! pID
    oldPos = _pjPos pj
    newPos = _pjVel pj +.+ oldPos
    -- this is hacky, should use a version of collidePointWalls' that collides
    -- circles and walls
    invShift x = x -.- 5 *.* normalizeV (_pjVel pj)
    hitWl  = collideCircWalls oldPos newPos 4 $ wallsNearPoint newPos w
    finalPos = maybe newPos (invShift . fst) hitWl
    setV v = set (props . ix pID . pjVel) v
    updateV = maybe id (setV . snd) hitWl
    halfV = props . ix pID . pjVel %~ (0.5 *.*)
    f x | x < -369 = -10
        | otherwise = x - 1
    updatePicture = 
        set (props . ix pID . prDraw)
            (\_ -> (,) mempty $ onLayer PtLayer $ uncurryV translate newPos $ remoteBombPic time)


setRemoteBombScope :: Int -> Prop -> World -> World
setRemoteBombScope itid pj w' = case _itemPositions w' IM.! itid of
    InInv cid invid 
        -> w' & creatures . ix cid . crInv . ix invid . itAttachment 
                . scopePos .~ (_pjPos pj -.- _crPos (_creatures w' IM.! cid))
              & creatures . ix cid . crInv . ix invid . itAimZoom 
                    .~ (defaultItZoom {_itZoomMax = 0.5, _itZoomMin = 0.5})
    _   -> w'

defaultThrowable :: Item
defaultThrowable = grenade
remoteBomb :: Item
remoteBomb = defaultThrowable
    { _itName = "REMOTEBOMB"
    , _itIdentity = RemoteBomb
    , _itMaxStack = 1
    , _itAmount = 1
    , _itFloorPict = \_ -> (,) emptySH $ onLayer FlItLayer $ polygon $ map toV2 
        [(-3,-3),(-3,3),(3,3),(3,-3)]
    , _twMaxRange = 150
    , _twAccuracy = 30
    , _itUse = const throwRemoteBomb
    , _itUseModifiers =
        [ hammerCheckI
        ]
    , _itAttachment = ItScope (V2 0 0) 0 1 True
    , _itEquipPict = pictureWeaponOnAim' $ \_ -> (,) emptySH remoteBombUnarmedPic
    }


throwRemoteBomb :: Creature -> World -> World
throwRemoteBomb cr w = setLocation 
    $ removePict 
    $ resetFire 
    $ resetName 
    $ over props addG w
  where 
    cid = _crID cr
    addG = IM.insert i $ Projectile 
        { _pjPos = p
        , _pjStartPos = p
        , _pjVel = v
        , _prDraw = const mempty
        , _pjID = i
        , _pjUpdate = \_ -> moveRemoteBomb itid 50 i
        }
    i = newProjectileKey w
    v' = 0.02 / _cameraZoom w *.* rotateV (_cameraRot w) ( _mousePos w)
    v   | magV v' > 6 = 6 *.* normalizeV v'
        | otherwise   = v'
    j = _crInvSel cr
    resetName = set (creatures . ix cid . crInv . ix j . itName) "REMOTE"
    removePict = set (creatures . ix cid . crInv . ix j . itEquipPict) $ \ _ _ -> emptyBlank
    resetFire = set (creatures . ix cid . crInv . ix j . itUse) 
        $ \_ -> explodeRemoteBomb itid i
    p' = _crPos cr +.+ rotateV (_crDir cr) (V2 (_crRad cr) 0)
    p | circOnSomeWall p' 4 w = _crPos cr +.+ rotateV (_crDir cr) (V2 (_crRad cr-4) 0)
      | otherwise             = p'
    maybeitid = w ^? creatures . ix cid . crInv . ix j . itID . _Just
    setLocation :: World -> World
    setLocation w' = case maybeitid of
        Nothing -> w' & creatures . ix cid . crInv . ix j . itID ?~ newitid
                      & itemPositions %~ IM.insert newitid (InInv cid j)
        _ -> w'
    newitid = IM.newKey $ _itemPositions w
    itid = fromMaybe newitid maybeitid

explodeRemoteBomb :: Int -> Int -> Creature -> World -> World
explodeRemoteBomb itid pjid cr w
    = set (props . ix pjid . pjUpdate) (\_ -> retireRemoteBomb itid 30 pjid)
--        $ set (props . ix pjid . pjDraw) (\_ -> blank)
        $ set (creatures . ix cid . crInv . ix j . itUse) (\_ -> const id)
        $ resetName
        $ resetPict
--        $ resetScope
        $ makeExplosionAt (_pjPos (_props w IM.! pjid)) w
        -- - $ makeShrapnelBombAt (_pjPos (_props w IM.! pjid)) w
  where 
    cid = _crID cr
    resetName = set (creatures . ix cid . crInv . ix j . itName) "REMOTEBOMB"
    resetPict = set (creatures . ix cid . crInv . ix j . itEquipPict ) 
                    (pictureWeaponOnAim' $ \_ -> (,) emptySH remoteBombUnarmedPic)
--    resetScope = creatures . ix cid . crInv . ix j . itScope . _Just . scopePos .~ (0,0)
    j = _crInvSel $ _creatures w IM.! cid
remoteBombPic 
    :: Int -- ^ time
    -> Picture
remoteBombPic _ = pictures 
    [ color (dark $ dark orange) $ circleSolid 5
    ]

remoteBombUnarmedPic :: Picture
remoteBombUnarmedPic = color (dark $ dark orange) $ circleSolid 5

retireRemoteBomb :: Int -> Int -> Int -> World -> World
retireRemoteBomb itid 0 pjid w = w
    & pointToItem (_itemPositions w IM.! itid) %~ 
        ( (itAttachment . scopePos .~ V2 0 0)
        . (itZoom .~ defaultItZoom)
        . (itUse .~ const throwRemoteBomb)
        )
    & props %~ IM.delete pjid
retireRemoteBomb itid t pjid w = setScope w 
    & props . ix pjid . pjUpdate .~ (\_ -> retireRemoteBomb itid (t-1) pjid)
  where
    setScope w' = case _itemPositions w' IM.! itid of
        InInv cid invid -> w' 
            & creatures . ix cid . crInv . ix invid . itAttachment 
                    . scopePos .~ (pos -.- _crPos (_creatures w' IM.! cid))
        _   -> w'
    pos = fromMaybe (V2 0 0) $ w ^? props . ix pjid . pjPos
