module Dodge.PlacementSpot where
import Dodge.LevelGen.Data
import Geometry

import Data.Maybe
import Data.Bifunctor
import Control.Monad.State
import System.Random

setDirPS :: Float -> PlacementSpot -> PlacementSpot
setDirPS a ps = case ps of
    PS p _ -> PS p a
    PSPos f re fb -> PSPos (\rp r -> fmap (first (setDirPS a)) (f rp r)) re fb
    PSRoomRand i f -> PSRoomRand i (fmap (setDirPS a) f)

randDirPS :: RandomGen g => PlacementSpot -> State g PlacementSpot
randDirPS ps = do
    a <- state $ randomR (0,pi*2)
    return $ setDirPS a ps

unusedSpotAwayFromInLink :: Float -> PlacementSpot
unusedSpotAwayFromInLink x = PSPos f (const id) Nothing
  where
    f rp r = case rp of
        UnusedSpot p a | all ( (>x) . dist p ) inlinkposs -> Just (PS p a,UsedSpot p a)
        _ -> Nothing
      where
        inlinkposs = usedRoomInLinkPoss r

usedRoomInLinkPoss :: Room -> [Point2]
usedRoomInLinkPoss r = mapMaybe f $ _rmPos r
  where
    f rp = case rp of
        UsedInLink {} -> Just $ _rpPos rp
        _ -> Nothing

-- TODO rename to any unused link facing out
anyLnkOutPS :: PlacementSpot
anyLnkOutPS = PSPos f (const id) Nothing
  where
    f (UnusedLink p a) _ = Just (PS p a, UsedSpot p a)
    f _ _ = Nothing
    
atFstLnkOut :: PlacementSpot
atFstLnkOut = PSPos f (const id) Nothing
  where
    f (UsedOutLink 0 i p a) _ = Just (PS p a, UsedOutLink 0 i p a)
    f _                     _ = Nothing

atNthLnkOutShiftBy :: Int -> ((Point2,Float) -> (Point2,Float)) -> PlacementSpot
atNthLnkOutShiftBy n theshift = PSPos f (const id) Nothing
  where
    f (UsedOutLink i rmid p a) _ | n == i = Just (PS p' a', UsedOutLink n rmid p a)
      where
        (p',a') = theshift (p,a)
    f _ _ = Nothing

atFstLnkOutShiftBy :: ((Point2,Float) -> (Point2,Float)) -> PlacementSpot
atFstLnkOutShiftBy = atNthLnkOutShiftBy 0

atFstLnkOutShiftInward :: Float -> PlacementSpot
atFstLnkOutShiftInward = atNthLnkOutShiftInward 0

atNthLnkOutShiftInward ::Int -> Float -> PlacementSpot
atNthLnkOutShiftInward n x = atNthLnkOutShiftBy n f
  where
    f (p,a) = (p +.+ rotateV a (V2 0 (negate x)),a)

overFstLnkOut :: PlacementSpot
overFstLnkOut = PSPos f (const id) Nothing
  where
    f (UsedOutLink 0 _ p a) _ = Just (PS p a, UsedSpot p a)
    f _ _ = Nothing

anyLnkInPS :: Float -- ^ amount to shift inward
    -> PlacementSpot
anyLnkInPS x = PSPos f (const id) Nothing
  where
    f (UnusedLink v a) _ = Just (PS v' a' ,UsedSpot v' a')
      where
        v' = v -.- x *.* unitVectorAtAngle (a + 0.5 * pi)
        a' = a + pi
    f _ _ = Nothing

useLnkRoomPos :: RoomPos -> Room -> Maybe (PlacementSpot,RoomPos)
useLnkRoomPos rp _ = case rp of
    (UnusedLink (V2 x y) a) -> Just (PS (V2 x y) a , UsedSpot (V2 x y) a)
    _ -> Nothing

psRandRanges :: (Float,Float) -> (Float,Float) -> (Float,Float) -> State StdGen (Point2,Float)
psRandRanges xranges yranges aranges = do
    x <- state $ randomR xranges
    y <- state $ randomR yranges
    a <- state $ randomR aranges
    return (V2 x y,a)
