--{-# LANGUAGE TupleSections #-}
module Geometry.Zone
    ( ddaStreamX
    , ddaStreamY
    , xIntercepts
    , yIntercepts
    , divTo
    , zoneOfPoint
    , zoneOfSeg
    , zoneInsideCirc
    ) where
import Geometry.Data
import Geometry.Vector

import StreamingHelp
import qualified Streaming.Prelude as S

divTo :: Float -> Float -> Int
{-# INLINE divTo #-}
divTo s = floor . (/s)

modTo :: Float -> Float -> Float
{-# INLINE modTo #-}
modTo s x = x - s * fromIntegral (divTo s x)

--remTo :: Float -> Float -> Float
--remTo s x = x - s * fromIntegral (quotTo s x)
--
--quotTo :: Float -> Float -> Int
--{-# INLINE quotTo #-}
--quotTo s = truncate . (/s)

zoneOfPoint :: Float -> Point2 -> V2 Int
{-# INLINE zoneOfPoint #-}
zoneOfPoint s = fmap (divTo s)

zoneOfRect :: Float -> Point2 -> Point2 -> Stream (Of (V2 Int)) Identity ()
{-# INLINE zoneOfRect #-}
zoneOfRect s sp ep = S.each [V2 x y | x <- makeInterval sx ex, y <- makeInterval sy ey]
  where
    V2 sx sy = zoneOfPoint s sp
    V2 ex ey = zoneOfPoint s ep

makeInterval :: Int -> Int -> [Int]
makeInterval x y
    | x < y = [x-1..y+1]
    | otherwise = [y-1..x+1]

zoneInsideCirc :: Float -> Point2 -> Float -> StreamOf Int2
{-# INLINE zoneInsideCirc #-}
zoneInsideCirc x p r = zoneOfRect x (p +.+ V2 r r) (p -.- V2 r r)

zoneOfSeg :: Float -> Point2 -> Point2 -> StreamOf Int2
{-# INLINE zoneOfSeg #-}
zoneOfSeg s sp ep = S.map (zoneOfPoint s)
    $  S.yield sp
    <> xIntercepts s sp ep
    <> yIntercepts s sp ep

ddaStreamX :: Float -> Point2 -> Point2 -> Stream (Of (V2 Int)) Identity ()
ddaStreamX s sp ep = S.map (zoneOfPoint s) $ xIntercepts s sp ep

ddaStreamY :: Float -> Point2 -> Point2 -> Stream (Of (V2 Int)) Identity ()
ddaStreamY s sp ep = S.map (zoneOfPoint s) $ yIntercepts s sp ep

xIntercepts :: Float -> Point2 -> Point2 -> Stream (Of Point2) Identity ()
{-# INLINE xIntercepts #-}
xIntercepts s (V2 sx sy) (V2 ex ey) 
    | xdx == 0  = mempty
    | xdx > 0   = S.each $ zipWith V2 [sx',sx'+xdx*50..ex] ([sy',sy'+ydx*50..ey] ++ repeat ey)
    | otherwise = S.each $ zipWith V2 [sx'-50,sx'+xdx*50-50..ex-50] ([sy',sy'+ydx*50..ey] ++ repeat ey)
  where
    xdx = signum (ex - sx)
    ydx = (ey - sy) / abs (ex - sx) -- carefull: if this is zero

    sy' = sy + ydx * abs (sx - sx')
    sx' | xdx < 0 = sx - modTo s sx
        | otherwise = s + sx - modTo s sx

yIntercepts :: Float -> Point2 -> Point2 -> Stream (Of Point2) Identity ()
{-# INLINE yIntercepts #-}
yIntercepts s sp ep = S.map f $ xIntercepts s (f sp) (f ep)
  where
    f (V2 x y) = V2 y x
