module Gamgine.Math.Box where
#include "Gamgine/Utils.cpp"
import Data.Maybe
import qualified Data.List as L
import Gamgine.Math.Vect as V
import Gamgine.Math.Utils
import Gamgine.Utils
IMPORT_LENS_AS_LE


-- axis aligned bounding box
data Box = Box {
   minPt :: Vect,
   maxPt :: Vect
   } deriving (Show, Read)

LENS(minPt)
LENS(maxPt)

center :: Box -> Vect
center b = minPt b + halfs b

halfs :: Box -> Vect
halfs (Box minPt maxPt) = (maxPt - minPt) * 0.5

intersects :: Box -> Box -> Bool
Box min1 max1 `intersects` Box min2 max2 =
   not $ V.any (>) min2 max1 || V.any (<) max2 min1

inside :: Box -> Box -> Bool
Box min1 max1 `inside` Box min2 max2 =
   V.all (>=) min1 min2 && V.all (<=) max1 max2

moveBy :: Box -> Vect -> Box
Box min max `moveBy` v = Box (min + v) (max + v)

extendBy :: Box -> Box -> Box
Box min1 max1 `extendBy` Box min2 max2 =
   Box (V.minVec min1 min2) (V.maxVec max1 max2)

contains :: Box -> Vect -> Bool
contains (Box min max) v =
   V.all (>=) v min && V.all (<=) v max

bound :: [Box] -> Box
bound []     = Box (V.v3 0 0 0) (V.v3 0 0 0)
bound (b:bs) = L.foldr extendBy b bs

-- overlapping if distance negative in all dimensions
distance :: Box -> Box -> Vect
distance b1 b2 = (abs $ center b2 - center b1) - (halfs b1 + halfs b2)

-- If the boxes are overlapping, than minOverlap returns the minimal
-- distance in each dimension by which b1 has to be moved to resolve
-- the overlapping with b2. Otherwise, if not overlapping, for each
-- dimension 0 is returned.
minOverlap :: Box -> Box -> Vect
minOverlap (Box min1 max1) (Box min2 max2) = V.fromList $ L.map overlap [0..2]
   where
      overlap dim =
         let v1@(minv1, maxv1) = (getElem dim min1, getElem dim max1)
             v2@(minv2, maxv2) = (getElem dim min2, getElem dim max2)
             in if maxv1 < minv2 || minv1 > maxv2
                   then 0
                   else let minv1Outside     = minv1 < minv2
                            maxv1Outside     = maxv1 > maxv2
                            v1Inside         = not minv1Outside && not maxv1Outside
                            o | v1Inside     = insideOverlap v1 v2
                              | minv1Outside = minOutsideOverlap v1 v2
                              | maxv1Outside = maxOutsideOverlap v1 v2
                            in o

      insideOverlap (minv1, maxv1) (minv2, maxv2) =
         let leftDist  = maxv1 - minv2
             rightDist = maxv2 - minv1
             o | leftDist < rightDist = -leftDist
               | otherwise            =  rightDist
             in o

      minOutsideOverlap (_, maxv1) (minv2, _) = -(maxv1 - minv2)
      maxOutsideOverlap (minv1, _) (_, maxv2) = maxv2 - minv1


type Tuple3d = (Double,Double,Double)

fromTuples :: (Tuple3d, Tuple3d) -> Box
fromTuples (t1, t2) = Box (V.fromTuple t1) (V.fromTuple t2)

toTuples :: Box -> (Tuple3d, Tuple3d)
toTuples (Box minPt maxPt) = (V.toTuple minPt, V.toTuple maxPt)