module Dodge.Item.Weapon.BatteryGuns
    ( lasGun
    , lasDual
    , lasWide
    , lasWidePulse
    , lasSway
    , lasSwing
    , lasFocus
    , lasPulse
    , sparkGun
    , teslaGun
    , teslaParams
    , tractorGun
    , lasRayAt
    , flameBeamCombine
    , teslaBeamCombine
    , splitBeamCombine
    ) where
import Dodge.Data
import Dodge.Reloading.Action
import Dodge.Tesla
import Dodge.Beam
import Dodge.Item.Weapon.LaserPath
import Dodge.Item.Location
import Dodge.Creature.HandPos
--import Dodge.Particle.TeslaArc
--import Dodge.Particle.Flame
import Dodge.SoundLogic.LoadSound
import Dodge.WorldEvent.Flash
--import Dodge.WorldEvent.Damage
--import Dodge.Default
--import Dodge.Item.Weapon.InventoryDisplay
import Dodge.Item.Weapon.TriggerType
import Dodge.Item.Weapon.AmmoParams
import Dodge.Default.Weapon
--import Dodge.Item.Attachment
--import Dodge.WorldEvent.HelperParticle
import Dodge.Base
--import Dodge.Zone
import Geometry
import Picture
import Shape
import ShapePicture
import LensHelp
--import Dodge.RandomHelp
--import MonadHelp

--import Data.Maybe
--import Data.List (sortOn)
--import System.Random
--import Data.Tuple
import qualified Data.IntMap.Strict as IM
import qualified Data.Map.Strict as M
--import Control.Monad.State

defaultBatteryGun :: Item
defaultBatteryGun = defaultWeapon
    & itType . iyModules .~ batteryModules

batteryModules :: M.Map ModuleSlot ItemModuleType
batteryModules = M.fromList 
    [(ModBattery,  EMPTYMODULE)
    ,(ModTeleport, EMPTYMODULE)
    ]

defaultAutoBatteryGun :: Item
defaultAutoBatteryGun = defaultAutoGun
    & itType . iyModules .~ batteryModules

sparkGun :: Item
sparkGun = teslaGun
    & itType . iyBase .~ SPARKGUN
    & itParams . arcSize .~ 10
teslaGun :: Item
teslaGun = defaultBatteryGun 
    { _itConsumption = defaultLoadable
        & laMax .~ 200
        & laCycle .~ [loadEject 10, loadInsert 10 , loadPrime 60]
    , _itUse = ruseInstant shootTeslaArc NoHammer
        [ ammoCheckI
        , withTempLight 1 100 (V3 0 0 1)
        , withSoundForI elecCrackleS 1
        , useAmmoAmount 1
        ]
        & useAim . aimWeight .~ 6
        & useAim . aimStance .~ TwoHandFlat
    , _itDimension = ItemDimension
        { _dimRad = 9
        , _dimCenter = V3 4 0 0
        , _dimPortage = HeldItem
            {_handlePos = 4
            ,_muzPos    = 4
            }
        , _dimSPic = teslaGunPic
        }
    , _itParams = teslaParams
    }
    & itType . iyBase .~ TESLAGUN
teslaGunPic :: Item -> SPic
teslaGunPic _ = noPic $ colorSH blue $
       upperPrismPoly 5 (rectNESW xb    8 xa    0)
    <> upperPrismPoly 5 (rectNESW (-xa) 8 (-xb) 0)
  where
    xa = 1
    xb = 9
lasPulse :: Item
lasPulse = lasGun
    & itType . iyBase .~ LASPULSE
    & itUse . useMods .~
        [ ammoCheckI
        , withItem $ \it -> withTempLight 1 (100 * frac it) (xyzV4 (_lasColor $ _itParams it))
        , withItem $ \it -> withSoundForVol (frac it) tone440sawtoothquietS 2
        , withItemUpdate itup 
            $ \it -> withMuzPos $ flareCircleAt (_lasColor $ _itParams it) (frac it * 0.8)
        , useAmmoAmount 1
        ]
  where
    itup it = it
        & itParams . lasCycle %~ (\i -> (i+1) `mod` 20)
        & itParams . lasDamage .~ ceiling ((22 :: Float) * frac it)
        & itParams . lasColor .~ frac it * orange
    frac it = 0.5 * (1 + sin (pi * fromIntegral (_lasCycle $ _itParams it) * 0.1))
    

lasWide :: Int -> Item
lasWide n = lasGun
    & itType . iyBase .~ LASGUNWIDE n
    & itParams . lasColor .~ orange
    & itParams . lasDamage .~ 2
    & itUse .~ ( ruseInstant shootLaser NoHammer
        [ ammoCheckI
        , withItem $ \it -> withTempLight 1 100 (xyzV4 (_lasColor $ _itParams it))
        , withItem $ \it -> withMuzPos $ flareCircleAt (_lasColor $ _itParams it) 0.8
        , withSoundForI tone440sawtoothquietS 2
        , useAmmoAmount 1
        , duplicateOffsets xs
        ]
        & useAim . aimWeight .~ 6
        & useAim . aimRange .~ 1
        & useAim . aimStance .~ TwoHandTwist
        )
  where
    n' = 9 * n
    xs = [ 0.25 * (fromIntegral x - fromIntegral (n'-1) /2)  | x <- [0..n'-1] ]
lasWidePulse :: Item
lasWidePulse = lasGun
    & itType . iyBase .~ LASGUNWIDEPULSE
    & itParams . lasColor .~ orange
    & itParams . lasDamage .~ 2
    & itUse .~ ( ruseInstant shootLaser NoHammer
        [ ammoCheckI
        , withItem $ \it -> withTempLight 1 (100 * frac it) (xyzV4 (_lasColor $ _itParams it))
        , withItem $ \it -> withSoundForVol (frac it) tone440sawtoothquietS 2
        , withItemUpdate itup 
            $ \it -> withMuzPos $ flareCircleAt (_lasColor $ _itParams it) (frac it * 0.8)
        , useAmmoAmount 1
        , withItemUpdate itup $ \it -> duplicateOffsets (xs it)
        ]
        & useAim . aimWeight .~ 6
        & useAim . aimRange .~ 1
        & useAim . aimStance .~ TwoHandTwist
        )
  where
    itup it = it
        & itParams . lasCycle %~ (\i -> (i+1) `mod` 40)
        & itParams . lasDamage .~ ceiling ((22 :: Float) * frac it)
        & itParams . lasColor .~ frac it * orange
    frac it = 0.5 * (1 + sin (pi * fromIntegral (_lasCycle $ _itParams it) * 0.05))
    xs it = [ 0.25 * (fromIntegral x - fromIntegral (n'-1) /2)  | x <- [0..n'-1] ]
      where
        n' = (ceiling $ (150 :: Float) * frac it) :: Int
lasSway :: Item
lasSway = lasGun
    & itType . iyBase .~ LASGUNSWAY
    & itParams . lasColor .~ orange
    & itParams . lasDamage .~ 11
    & itUse .~ ( ruseInstant shootLaser NoHammer
        [ ammoCheckI
        , withItem $ \it -> withTempLight 1 100 (xyzV4 (_lasColor $ _itParams it))
        , withItem $ \it -> withMuzPos $ flareCircleAt (_lasColor $ _itParams it) 0.8
        , withSoundForI tone440sawtoothquietS 2
        , useAmmoAmount 1
        , withItemUpdate itup $ \it -> duplicateOffsets (x it)
        ]
        & useAim . aimWeight .~ 6
        & useAim . aimRange .~ 1
        & useAim . aimStance .~ TwoHandTwist
        )
  where
    itup = itParams . lasCycle %~ (\i -> (i+1) `mod` 40)
    x it = [ 15 * sin (pi * fromIntegral x' * 0.05) ]
      where
        x' = _lasCycle $ _itParams it
lasFocus :: Int -> Item
lasFocus n = lasWide n
    & itType . iyBase .~ LASGUNFOCUS n
    & itParams . lasColor .~ red
    & itUse . useMods .~
        [ ammoCheckI
        , withItem $ \it -> withTempLight 1 100 (xyzV4 (_lasColor $ _itParams it))
        , withItem $ \it -> withMuzPos $ flareCircleAt (_lasColor $ _itParams it) 0.8
        , withSoundForI tone440sawtoothquietS 2
        , useAmmoAmount 1
        , duplicateOffsetsFocus xs
        ]
  where
    n' = 9 * n
    xs = [ 0.25 * (fromIntegral x - fromIntegral (n'-1) /2)  | x <- [0..n'-1] ]

lasDual :: Item
lasDual = lasGun
    & itType . iyBase .~ LASGUNDUAL
    & itParams .~ DualBeam
        { _phaseV = 1
        , _lasColor = orange
        , _lasColor2 = blue
        , _lasCycle = 0
        , _lasDamage = 11
        , _lasBeam = BeamCombine lasBeamCombine
        , _subParams = Nothing
        }
    & itUse .~ (ruseInstant shootDualLaser NoHammer
        [ ammoCheckI
--        , withItem $ \it -> withTempLight 1 100 (xyzV4 (_lasColor $ _itParams it))
        , withItem $ \it -> withMuzPosShift (V2 0 10)    $ flareCircleAt (_lasColor $ _itParams it) 0.8
        , withItem $ \it -> withMuzPosShift (V2 0 (-10)) $ flareCircleAt (_lasColor2 $ _itParams it) 0.8
        , withSoundForI tone440sawtoothquietS 2
        , useAmmoAmount 1
        ]
        & useAim . aimWeight .~ 6
        & useAim . aimRange .~ 1
        & useAim . aimStance .~ TwoHandTwist
        )
    & itType . iyModules . at ModDualBeam ?~ EMPTYMODULE
lasSwing :: Item
lasSwing = lasGun
    & itType . iyBase .~ LASGUNSWING
    & itParams . lasColor .~ orange
    & itUse . useMods .~
        [ ammoCheckI
        , withItem $ \it -> withTempLight 1 100 (xyzV4 (_lasColor $ _itParams it))
        , withItem $ \it -> withMuzPos $ flareCircleAt (_lasColor $ _itParams it) 0.8
        , withSoundForI tone440sawtoothquietS 2
        , useAmmoAmount 1
        , withItemUpdate itup $ \it -> duplicateOffsetsFocus (x it)
        ]
  where
    itup = itParams . lasCycle %~ (\i -> (i+1) `mod` 200)
    x it = [-y,y]
      where
        y = 15 * sin (pi * fromIntegral x' * 0.01)
        x' = _lasCycle $ _itParams it
lasGun :: Item
lasGun = defaultAutoBatteryGun 
    { _itConsumption = defaultLoadable
        & laMax .~ 200
        & laCycle .~ [loadEject 10, loadInsert 10 , loadPrime 60]
    , _itUse = ruseInstant shootLaser NoHammer
        [ ammoCheckI
        , withItem $ \it -> withTempLight 1 100 (xyzV4 (_lasColor $ _itParams it))
        , withSoundForI tone440sawtoothquietS 2
        , withItem $ \it -> withMuzPos $ flareCircleAt (_lasColor $ _itParams it) 0.8
        , useAmmoAmount 1
        ]
        & useAim . aimWeight .~ 6
        & useAim . aimRange .~ 1
        & useAim . aimStance .~ TwoHandTwist
    , _itParams = Refracting  
        { _phaseV = 1
        , _lasColor = yellow
        , _lasColor2 = yellow
        , _lasCycle = 0
        , _lasDamage = 11
        }
    , _itTweaks = Tweakable
        { _tweakParams = IM.fromList [(0,lasGunTweak)]
        , _tweakSel = 0
        }
    , _itDimension = ItemDimension
        { _dimRad = 10
        , _dimCenter = V3 15 0 0
        , _dimPortage = HeldItem 5 30
        , _dimSPic = lasGunPic
        }
    }
    & itType . iyBase .~ LASGUN

lasGunPic :: Item -> SPic
lasGunPic it = 
    ( colorSH blue $ 
           upperPrismPoly 4 (rectNESW 3    30   1  0)
        <> upperPrismPoly 4 (rectNESW (-1) 30 (-3) 0)
        <> upperPrismPoly 1 (rectNESW 3    30 (-3) 0)
    , setLayer BloomNoZWrite . color col . setDepth 1.1 . polygon $ rectNESW 1 30 (-1) 0
    )
  where
    amFrac = fractionLoadedAmmo it
    col = brightX 2 1.5 $ mixColors amFrac (1-amFrac) green red

lasGunTweak :: TweakParam
lasGunTweak = TweakParam
    { _doTweak = thetweak
    , _curTweak = 1
    , _maxTweak = 3
    , _showTweak = showPhaseV
    , _nameTweak = "PHASE VELOCITY"
    }
  where
    thetweak 0 = itParams . phaseV .~ 0.2
    thetweak 1 = itParams . phaseV .~ 1
    thetweak 2 = itParams . phaseV .~ 5
    thetweak _ = id
    showPhaseV 0 = "V"
    showPhaseV 1 = "/"
    showPhaseV 2 = "Z"
    showPhaseV i = "THIS SHOULD NOT BE POSSIBLE" ++ show i

tractorGun :: Item
tractorGun = lasGun 
    { _itConsumption = defaultLoadable
        & laMax .~ 10000
        & laCycle .~ [loadEject 10, loadInsert 10 , loadPrime 60]
    , _itUse = ruseInstant aTractorBeam NoHammer
        [ ammoCheckI
        ]
        & useAim . aimWeight .~ 6
        & useAim . aimRange .~ 1
        & useAim . aimStance .~ TwoHandTwist
    , _itDimension = ItemDimension
        { _dimRad = 10
        , _dimCenter = V3 15 0 0
        , _dimPortage = HeldItem 5 30
        , _dimSPic = tractorGunPic
        }
    , _itParams = Attracting  {_attractionPower = 1}
    , _itTweaks = Tweakable
        { _tweakParams = IM.fromList [(0,tractorGunTweak)]
        , _tweakSel = 0
        }
    }
    & itType . iyBase .~ TRACTORGUN

tractorGunTweak :: TweakParam
tractorGunTweak = TweakParam
    { _doTweak = thetweak
    , _curTweak = 1
    , _maxTweak = 4
    , _showTweak = showPower
    , _nameTweak = "TRACTION POWER"
    }
  where
    thetweak 0 = itParams . attractionPower .~ 1
    thetweak 1 = itParams . attractionPower .~ -1
    thetweak 2 = itParams . attractionPower .~ -10
    thetweak 3 = itParams . attractionPower .~ 0
    thetweak _ = id
    showPower 0 = "+"
    showPower 1 = "-"
    showPower 2 = "#"
    showPower 3 = "0" -- object should stay in the center of the beam
    showPower i = "THIS SHOULD NOT BE POSSIBLE" ++ show i

tractorGunPic :: Item -> SPic
tractorGunPic it = 
    ( colorSH red $ 
           upperPrismPoly 4 (rectNESW 3    30   1  0)
        <> upperPrismPoly 4 (rectNESW (-1) 30 (-3) 0)
        <> upperPrismPoly 1 (rectNESW 3    30 (-3) 0)
    , setLayer BloomNoZWrite . color col . setDepth 1.1 . polygon $ rectNESW 1 30 (-1) 0
    )
  where
    amFrac = fractionLoadedAmmo it
    col = brightX 2 1.5 $ mixColors amFrac (1-amFrac) green red
-- | assumes that the item is held
shootTeslaArc :: Item -> Creature -> World -> World
shootTeslaArc it cr w = w'
    & creatures . ix (_crID cr) . crInv . ix (crSel cr) . itParams .~ ip
  where 
    (w',ip) = shootTeslaArc' (_itParams it) pos dir w
    pos = _crPos cr +.+ aimingMuzzlePos cr it *.* unitVectorAtAngle dir
    dir = _crDir cr


shootLaser :: Item -> Creature -> World -> World
shootLaser it cr = instantParticles .:~ lasRayAt (_lasColor $ _itParams it) dam phasev pos dir
  where 
    pos = _crPos cr +.+ aimingMuzzlePos cr it *.* unitVectorAtAngle dir
    dir = _crDir cr
    phasev = _phaseV . _itParams $ _crInv cr IM.! crSel cr
    dam = _lasDamage $ _itParams it


shootDualLaser :: Item -> Creature -> World -> World
shootDualLaser it cr w = w'
    & newBeams . positronBeams .:~ dualRayAt (_lasBeam $ _itParams it) itid w (_lasColor $ _itParams it) dam phasev posl dirl
    & newBeams . electronBeams .:~ basicBeamAt itid w (_lasColor2 $ _itParams it) dam phasev posr dirr
  where 
    (w',itid) = getHeldItemLoc cr w
    dir = _crDir cr
    pos = _crPos cr +.+ aimlength *.* unitVectorAtAngle dir
    aimlength = aimingMuzzlePos cr it
    posl = pos +.+ (-10) * vNormal (unitVectorAtAngle dir)
    posr = pos +.+ 10 * vNormal (unitVectorAtAngle dir)
    mwp = mouseWorldPos w
    mwp' | dist mwp (_crPos cr) < aimlength + 5 = _crPos cr +.+ (aimlength + 5) *.* unitVectorAtAngle dir
        | otherwise = mwp
    dirl = argV $ mwp' -.- posl
    dirr = argV $ mwp' -.- posr
    phasev = _phaseV . _itParams $ _crInv cr IM.! crSel cr
    dam = _lasDamage $ _itParams it

drawBeam :: Beam -> Picture
drawBeam bm = setLayer BloomNoZWrite $ pictures
    [ setDepth 19   . color (brightX 0 0.5 col) $ thickLine 20 ps
    , setDepth 19.5 . color (brightX 10 1 col)  $ thickLine 3  ps
    ]
  where
    col = _bmColor bm
    ps = _bmFirstPoints bm

basicBeamAt :: Int -> World -> Color -> Int -> Float -> Point2 -> Float -> Beam
basicBeamAt itid w col dam phasev pos dir = Beam
    { _bmDraw = drawBeam
    , _bmPos = pos
    , _bmDir = dir
    , _bmDamage = dam
    , _bmRange = 800
    , _bmPhaseV = phasev
    , _bmColor = col
    , _bmPoints = pos:ps
    , _bmFirstPoints = []
    , _bmOrigin = Just itid
    , _bmType = BeamCombine $ const id
    }
  where
    (_,ps) = reflectLaserAlong phasev [] pos (pos +.+ 800 *.* unitVectorAtAngle dir) w

dualRayAt :: BeamType -> Int -> World -> Color -> Int -> Float -> Point2 -> Float -> Beam
dualRayAt bt itid w col dam phasev pos dir = basicBeamAt itid w col dam phasev pos dir
    & bmType .~ bt

aTractorBeam :: Item -> Creature -> World -> World
--aTractorBeam _ cr w = w & props %~  tractorBeamAt i spos outpos dir power
aTractorBeam _ cr w = w & plNew props pjID (tractorBeamAt spos outpos dir power)
  where 
    cpos = _crPos cr
    spos = cpos +.+ (_crRad cr + 10) *.* unitVectorAtAngle dir
    xpos = cpos +.+ 400 *.* unitVectorAtAngle dir
    dir = _crDir cr
    outpos = fst $ collidePointWallsFilterStream (const True) cpos xpos w
    power = _attractionPower . _itParams $ _crInv cr IM.! crSel cr

tractorBeamAt :: Point2 -> Point2 -> Float -> Point2 -> Prop
tractorBeamAt pos outpos dir power = ProjectileTimed
    { _prPos = pos
    , _pjStartPos = outpos
    , _pjVel = d
    , _prDraw = tractorSPic
    , _pjID = 0
    , _pjUpdate = updateTractor
    , _pjTime = 10
    }
  where 
    d  = unitVectorAtAngle dir * power
{- |
The interaction of this with objects, walls etc needs more thought.  -}
updateTractor :: Prop -> World -> World
updateTractor pj w
    | _pjTime pj <= 0 = w & props %~ IM.delete i
    | otherwise = w
        & props . ix i . pjTime -~ 1
        & creatures     %~ IM.map (tractCr   pullVel pos outpos)
        & floorItems    %~ IM.map (tractFlIt pullVel pos outpos)
  where 
    i = _pjID pj
    pullVel = _pjVel pj
    pos = _prPos pj
    outpos = _pjStartPos pj

tractFlIt :: Point2 -> Point2 -> Point2 -> FloorItem -> FloorItem
tractFlIt q p1 outpos it 
    | segOnCirc p1 outpos (_flItPos it) 10 = it & flItPos %~ tractorPullPos q p1
    | otherwise = it

tractCr :: Point2 -> Point2 -> Point2 -> Creature -> Creature
tractCr q p1 outpos cr
    | segOnCirc p1 outpos (_crPos cr) 10 = cr & crPos %~ tractorPullPos q p1
    | otherwise = cr

tractorPullPos :: Point2 -> Point2 -> Point2 -> Point2
tractorPullPos q p1 p = p -.- m *.* (0.3/ x' *.* q +.+ g y *.* p4)
  where
    x' = abs y + 1
    y = errorClosestPointOnLineParam 1 p1 p3 p
    m | dist p p1 < 350 = 1
      | otherwise        = (400 - dist p p1) / 50
    g x | x > 5 = (10 - x) / 250
        | x > 1 = 0.02
        | x > -1 = x * 0.02
        | x > -5 = -0.02
        | otherwise = (x - 10) / 250
    --p4 = vNormal p5
    p4 = vNormal $ squashNormalizeV q
    p3 = p1 +.+ p4

tractorSPic :: Prop -> SPic
tractorSPic pj = (,) mempty $ setLayer BloomNoZWrite $ setDepth 20 $ color (withAlpha 0.5 col) $ polygon 
    [ spos  -- not sure if this is anticlockwise...
    , spos +.+ size *.* (d +.+ n)
    , xpos +.+ size *.* n
    , xpos -.- size *.* n
    , spos +.+ size *.* (d -.- n)
    ]
  where
    size = fromIntegral (_pjTime pj)
    spos = _prPos pj
    xpos = _pjStartPos pj
    d = squashNormalizeV $ spos -.- xpos
    n = vNormal d
    col = mixColors 0.5 0.5 white blue
