module Match.State.Actions(Action(..),
  PlAction,
  act,
  actP,
  plact)
where

import Control.Monad.State as State
import System.Random

import Utils
import FVector
import qualified Gen

import Match.Ball
import Match.Player

import Match.State.MatchState
import Match.State.MatchBase

data Action = Goto FRange | Kick FVector3 | HoldBall | Idle

type PlAction = (Player, Action)

plact :: PlAction -> Match ()
plact = uncurry act

act :: Player -> Action -> Match ()
act p (Goto t)    = goto t p
act p (Kick t)    = kick t p
act p (HoldBall)  = hold p
act p Idle        = idle p

actP :: PlayerID -> Action -> Match ()
actP p a = do
  s <- State.get
  case findPlayer p s of
    Nothing -> return ()
    Just pl  -> act pl a

playerControlCoeff :: Float -> Float -> Player -> Float
playerControlCoeff mn mx pl =
  mn + (mx - mn) * (Gen.controlskill $ plskills pl)

goto :: FRange -> Player -> Match ()
goto (x, y) pl = do
  s <- State.get
  let dt = frametime s
  let (curx,  cury)  = plposition pl
      (diffx, diffy) = (curx - x, cury - y)
      c              = playerid pl
      spd            = plspeed (plspeedcoeff (params s)) (plspeedmin (params s)) dt pl
      atpos          = abs diffx < spd && abs diffy < spd
      addvec = if atpos
                 then (-diffx, -diffy)
                 else
                   let ang = atan2 diffy diffx
                       xvel = cos ang * spd
                       yvel = sin ang * spd
                   in (-xvel, -yvel)
      dribbling = canDribble s pl && len2 addvec > 0.0000001
      (px, py) = pitchsize s
      runvec = if dribbling 
                 then addvec `mul2` playerControlCoeff (plcontrolmin (params s)) (plcontrolmax (params s)) pl 
                 else addvec
  when dribbling $ do
    sModBall $ modBallposition $ const $ to3D (plposition pl `add2` (addvec `mul2` 2)) 0
    sModBall $ modBallvelocity (const nullFVector3)
    sModPendingevents $ (BallKicked:)
    sModLasttouch $ const $ Just $ playerid pl
  doRot (Just (x, y)) pl
  let npos = clamp2 (-4, -0.2) (px + 4, py + 0.2) (plposition pl `add2` runvec)
  modify $ modPlayer c $ modPlposition $ const npos

getRandomR :: (Random a) => (a, a) -> State StdGen a
getRandomR v = state $ \s -> randomR v s

getKickVec :: FVector3 -> Player -> Match FVector3
getKickVec v p = do
  s <- State.get
  let (x, y, z) = capLen3 (maxkickveclen (params s)) v
      relskill = if len2 (x, y) < (stillpassveclen (params s)) && z < (stillpassvecheight (params s))
                   then Gen.passingskill $ plskills p
                   else Gen.shootingskill $ plskills p
      vlen = len3 (x, y, z)
  let xvarmax = vlen * ((maxkickvar (params s)) * (1 - relskill))
  let yvarmax = vlen * ((maxkickvar (params s)) * (1 - relskill))
  let zvarmax = abs z * ((maxkickvarz (params s)) * (1 - relskill))
  let (vc, g') = flip runState (randomgen s) $ do
                  x' <- getRandomR (-xvarmax, xvarmax)
                  y' <- getRandomR (-yvarmax, yvarmax)
                  z' <- getRandomR (0, zvarmax)
                  return (x + x', y + y', z + z')
  sModRandomgen $ const g'
  -- liftIO $ putStrLn $ "Orig: " ++ (show $ capLen3 40 v)
  -- liftIO $ putStrLn $ "New:  " ++ (show $ capLen3 40 vc)
  return $ capLen3 (maxkickveclen (params s)) vc

kick :: FVector3 -> Player -> Match ()
kick vec p = do
  s <- State.get
  if not (inKickDistance s p) || (kicktimer p > 0)
    then return ()
    else do
      kvec <- getKickVec vec p
      sModBall $ modBallvelocity $ const kvec
      sModPendingevents $ (BallKicked:)
      sModLasttouch $ const $ Just $ playerid p
      sModPlayer (playerid p) $ modKicktimer $ const (setkicktimer (params s))

hold :: Player -> Match ()
hold pl = do
  s <- State.get
  when (inCatchDistance s pl) $ do
    sModBall $ modBallvelocity $ const $ nullFVector3
    sModBall $ modBallposition $ const $ to3D (plposition pl) 0
    sModLasttouch $ const $ Just $ playerid pl

doRot :: Maybe FRange -> Player -> Match ()
doRot mtgt pl = do
  s <- State.get
  let (curx, cury)   = plposition pl
      bpos@(bx, by)  = to2D $ ballposition $ ball s
      gpos           = oppositeGoalAbs s pl
      atball         = abs (curx - bx) < 0.5 && abs (cury - by) < 0.5
      maintgtsimple  = if atball then gpos else bpos
      (ttx, tty)     = case mtgt of
        Nothing       -> maintgtsimple
        Just (tx, ty) -> let attgt = abs (curx - tx) < 0.5 && abs (cury - ty) < 0.5
                         in if attgt
                              then maintgtsimple
                              else (tx, ty)
  -- yup, this is correct (0 degrees = north)
  sModPlayer (playerid pl) $ modPlrotation $ const $ wrap 0 360 $ radToDeg $ atan2 (ttx - curx) (tty - cury)

idle :: Player -> Match ()
idle pl = doRot Nothing pl

