module Graphics.UI.HaskGame.Rect
    (Rect(Rect)
    ,toVectors
    ,fromVectors
    ,make
    ,getTopLeft
    ,getSize
    ,getBottomRight
    ,vectorsToPVectors, pVectorsToVectors, toPVectors
    ,inRect
    ,pos, size
    ,x, y, w, h
    ,makeFromPos,makeFromSize
    ,posInside
    ,trunc
    ,union
    ,intersect)
where

import qualified Graphics.UI.SDL as SDL
import qualified Graphics.UI.HaskGame.Vector2 as Vector2
import Graphics.UI.SDL(Rect(Rect))
import Graphics.UI.HaskGame.Vector2(Vector2(..))
import Control.Arrow(first, second)
import Control.Applicative(liftA2)

type Endo a = a -> a
type Two a = (a, a)

toVectors :: Rect -> Two (Vector2 Int)
toVectors (SDL.Rect rx ry rw rh) = (Vector2 rx ry, Vector2 rw rh)

getTopLeft :: Rect -> Vector2 Int
getTopLeft = fst . toVectors

getSize :: Rect -> Vector2 Int
getSize = snd . toVectors

getBottomRight :: Rect -> Vector2 Int
getBottomRight = snd . vectorsToPVectors . toVectors

fromVectors :: Two (Vector2 Int) -> Rect
fromVectors (Vector2 rx ry, Vector2 rw rh) = SDL.Rect rx ry rw rh

make :: Vector2 Int -> Vector2 Int -> Rect
make = curry fromVectors

-- To Positional vectors
vectorsToPVectors :: Endo (Two (Vector2 Int))
vectorsToPVectors (p, s) = (p, p+s)

pVectorsToVectors :: Endo (Two (Vector2 Int))
pVectorsToVectors (p1, p2) = (p1, p2-p1)

toPVectors :: Rect -> Two (Vector2 Int)
toPVectors = vectorsToPVectors . toVectors

inRect :: Endo (Two (Vector2 Int)) -> Endo Rect
inRect f = fromVectors . f . toVectors

pos :: Endo (Vector2 Int) -> Endo Rect
pos = inRect . first

size :: Endo (Vector2 Int) -> Endo Rect
size = inRect . second

x :: Endo Int -> Endo Rect
x = pos . Vector2.first

y :: Endo Int -> Endo Rect
y = pos . Vector2.second

w :: Endo Int -> Endo Rect
w = size . Vector2.first

h :: Endo Int -> Endo Rect
h = size . Vector2.second

makeFromPos :: Vector2 Int -> Rect
makeFromPos (Vector2 rx ry) = SDL.Rect rx ry 0 0

makeFromSize :: Vector2 Int -> Rect
makeFromSize (Vector2 rw rh) = SDL.Rect 0 0 rw rh

posInside :: Vector2 Int -> Rect -> Bool
posInside point rect =
    let (rpoint, rsize) = toVectors rect
    in point-rpoint < rsize

-- Truncate negative sizes to 0
trunc :: Rect -> Rect
trunc = w (max 0) . h (max 0)

union :: Rect -> Rect -> Rect
union r1 r2 =
    let (tl1, br1) = toPVectors r1
        (tl2, br2) = toPVectors r2
    in fromVectors . pVectorsToVectors $
           ((liftA2 min tl1 tl2),
            (liftA2 max br1 br2))

intersect :: Rect -> Rect -> Rect
intersect r1 r2 =
    let (Vector2 left1 top1,
         Vector2 right1 bottom1) = toPVectors r1
        (Vector2 left2 top2,
         Vector2 right2 bottom2) = toPVectors r2
        left = max left1 left2
        top = max top1 top2
        right = min right1 right2
        bottom = min bottom1 bottom2
        width = right - left
        height = bottom - top
    in Rect left top width height