module Dodge.Zone
    ( zoneOfSight
    , zoneAroundPoint
    , aroundPoint
    , nearPoint
    , nearSeg
    , wlsNearPoint
    , crsNearPoint
    , clsNearPoint
    , wlsNearSeg
    , crsNearSeg
    , wlsInsideCirc
    , crsInsideCirc
    , clZoneOfPoint
    , crZoneOfPoint
    , wlZoneOfPoint
    , module Dodge.Zone.Size
    , module Dodge.Zone.Object
    , module Dodge.Zone.Update
    )
    where
import Dodge.Zone.Object
import Dodge.Zone.Update
import Dodge.Zone.Size
import Dodge.Data
--import Dodge.Base.Window
import Geometry
import Geometry.Zone

import qualified Streaming.Prelude as S
import StreamingHelp
import Data.Maybe
import Control.Lens

extractFromZone :: Zoning t a -> Int2 -> Maybe (t a)
{-# INLINE extractFromZone #-}
extractFromZone zn (V2 x y) = zn ^? znObjects . ix x . ix y

streamFromZone :: Foldable t => Zoning t a -> StreamOf Int2 -> StreamOf a
{-# INLINE streamFromZone #-}
streamFromZone zn = S.concat . S.mapMaybe (extractFromZone zn)

zoneAroundPoint :: Float -> Point2 -> StreamOf Int2
{-# INLINE zoneAroundPoint #-}
zoneAroundPoint s p = S.each [V2 a b | a <- [x-1..x+1] , b <- [y-1..y+1] ]
  where
    V2 x y = zoneOfPoint s p

-- this is ugly, might be better with zoneOfBounds or somesuch
zoneOfSight :: Float -> World -> Stream (Of (V2 Int)) Identity () --(Int,Int)]
{-# INLINE zoneOfSight #-}
zoneOfSight x' w = S.each
    [V2 a b 
        | a <- [minimum xs .. maximum xs]
        , b <- [minimum ys .. maximum ys]
    ]
  where 
    --(xs,ys) = unzip $ map zoneOfPoint $ screenPolygon cfig w -- ++ [_cameraViewFrom w]
    (xs,ys) = unzip $ map (sizeZoneOfPoint x') $ _boundBox w -- ++ [_cameraViewFrom w]
    sizeZoneOfPoint s (V2 x y) = (f x, f y)
      where
        f = floor . (/ s)

nearPoint :: (Foldable t,Monoid (t a)) 
    => (World -> Zoning t a) -> Point2 -> World -> StreamOf a
{-# INLINE nearPoint #-}
nearPoint f p w = S.each . fromMaybe mempty $ extractFromZone zn (zoneOfPoint (_znSize zn) p)
  where
    zn = f w

aroundPoint :: Foldable t => (World -> Zoning t a) -> Point2 -> World -> StreamOf a
{-# INLINE aroundPoint #-}
aroundPoint f p w = streamFromZone zn $ zoneAroundPoint (_znSize zn) p
  where
    zn = f w

clsNearPoint :: Point2 -> World -> StreamOf Cloud
{-# INLINE clsNearPoint #-}
clsNearPoint = nearPoint _clZoning

wlsNearPoint :: Point2 -> World -> StreamOf Wall
{-# INLINE wlsNearPoint #-}
wlsNearPoint = nearPoint _wlZoning

crsNearPoint :: Point2 -> World -> StreamOf Creature
{-# INLINE crsNearPoint #-}
crsNearPoint = nearPoint _crZoning

nearSeg :: Foldable t => (World -> Zoning t a) -> Point2 -> Point2 -> World -> StreamOf a
{-# INLINE nearSeg #-}
nearSeg f p r w = streamFromZone zn $ zoneOfSeg (_znSize zn) p r
  where
    zn = f w

wlsNearSeg :: Point2 -> Point2 -> World -> Stream (Of Wall) Identity ()
{-# INLINE wlsNearSeg #-}
wlsNearSeg = nearSeg _wlZoning

crsNearSeg :: Point2 -> Point2 -> World -> StreamOf Creature
{-# INLINE crsNearSeg #-}
crsNearSeg = nearSeg _crZoning

insideCirc :: Foldable t => (World -> Zoning t a) -> Point2 -> Float -> World -> StreamOf a
{-# INLINE insideCirc #-}
insideCirc f p r w = streamFromZone zn $ zoneInsideCirc (_znSize zn) p r
  where
    zn = f w

wlsInsideCirc :: Point2 -> Float -> World -> Stream (Of Wall) Identity ()
{-# INLINE wlsInsideCirc #-}
wlsInsideCirc = insideCirc _wlZoning

crsInsideCirc :: Point2 -> Float -> World -> StreamOf Creature
{-# INLINE crsInsideCirc #-}
crsInsideCirc = insideCirc _crZoning

wlZoneOfPoint :: Point2 -> Int2
{-# INLINE wlZoneOfPoint #-}
wlZoneOfPoint = zoneOfPoint wlZoneSize

clZoneOfPoint :: Point2 -> Int2
{-# INLINE clZoneOfPoint #-}
clZoneOfPoint = zoneOfPoint clZoneSize

crZoneOfPoint :: Point2 -> Int2
{-# INLINE crZoneOfPoint #-}
crZoneOfPoint = zoneOfPoint crZoneSize
