module Reanimate.Chiphunk
  ( simulate
  , BodyStore
  , newBodyStore
  , addToBodyStore
  , spaceFreeRecursive
  , polyShapesToBody
  , polygonsToBody
  ) where

import           Chiphunk.Low
import           Control.Monad
import           Data.IORef
import           Data.Map            (Map)
import qualified Data.Map            as Map
import qualified Data.Vector         as V
import qualified Data.Vector.Mutable as V
import           Foreign.Ptr
import           Graphics.SvgTree    (Tree)
import           Linear.V2 (V2(..))
import           Reanimate.Animation
import           Reanimate.PolyShape
import           Reanimate.Svg.Constructors

type BodyStore = IORef (Map WordPtr Tree)

newBodyStore :: IO BodyStore
newBodyStore = newIORef Map.empty

addToBodyStore :: BodyStore -> Body -> Tree -> IO ()
addToBodyStore store body svg = do
  key <- atomicModifyIORef' store $ \m ->
          case Map.maxViewWithKey m of
            Nothing -> (Map.singleton 1 svg, 1)
            Just ((maxKey,_),_) ->
              (Map.insert (maxKey+1) svg m, maxKey+1)
  bodyUserData body $= wordPtrToPtr key

renderBodyStore :: Space -> BodyStore -> IO Tree
renderBodyStore space store = do
  m <- readIORef store
  lst <- newIORef []
  spaceEachBody space (\body _dat -> do
    key <- get (bodyUserData body)
    case Map.lookup (ptrToWordPtr key) m of
      Nothing -> putStrLn "Body doesn't have an associated SVG"
      Just svg -> do
        Vect posX posY <- get $ bodyPosition body
        angle <- get $ bodyAngle body
        let bodySvg =
              translate posX posY $
              rotate (angle/pi*180) $
              svg
        modifyIORef lst $ (bodySvg:)
    ) nullPtr
  result <- readIORef lst
  return $ mkGroup result


simulate :: Space -> BodyStore -> Double -> Int -> Double -> IO Animation
simulate space store fps stepsPerFrame dur = do
  let timeStep = 1/(fps*fromIntegral stepsPerFrame)
      frames = round (dur * fps)
  v <- V.new frames
  forM_ [0..frames-1] $ \nth -> do
    svg <- renderBodyStore space store
    V.write v nth svg
    replicateM_ stepsPerFrame $ spaceStep space timeStep
  frozen <- V.unsafeFreeze v
  return $ mkAnimation dur $ \t ->
    let key = round (t * fromIntegral (frames-1))
    in frozen V.! key

polyShapesToBody :: Space -> [PolyShape] -> IO Body
polyShapesToBody space poly = do
    polygonsToBody space (map (map toVect) $ plDecompose poly)
  where
    toVect (V2 x y) = Vect x y

polygonsToBody :: Space -> [[Vect]] -> IO Body
polygonsToBody space polygons = do
  plBody <- bodyNew 0 0
  spaceAddBody space plBody

  forM_ polygons $ \vects -> do
    polyShape <- polyShapeNewRaw plBody vects 0.00
    shapeDensity polyShape $= 1
    spaceAddShape space polyShape
    shapeFriction polyShape $= 0.7
  return plBody

spaceFreeRecursive :: Space -> IO ()
spaceFreeRecursive space = do
  spaceEachBody space (\body _ -> bodyFree body) nullPtr
  spaceEachShape space (\shape _ -> shapeFree shape) nullPtr
  spaceFree space