{- |
  Module     : Data.Octree.BoundingBox.Internal
  Copyright  : Copyright (c) 2016 Michael Litchard
  License    : BSD3

  Maintainer : Michael Litchard
  Stability  : experimental
  Portability: not portable
  
  This module provides functions etc, for Data.Octree.BoundingBox.BoundingBox

  -- | DefInput, LeafValue a, DefOutput a, DefNodeValue a 
         Default types for BBoxConfig

  -- | filterNodes, points, result
         Default functions for BBoxConfig

  -- | inclusive
         boolean check for one BBox3 being inclusive of another
-}

module Data.Octree.BoundingBox.Internal
  ( filterNodes
  , points
  , result
  , DefInput
  , LeafValue 
  , DefOutput 
  , DefNodeValue
  , newBBox3
  , inclusive 
  ) where

import Data.BoundingBox.B3 (BBox3 (..), bound_corners, within_bounds, isect)

import Data.Octree.Internal (Vector3 (..), ODir (..)) 
import Data.List (foldl')

type DefInput       = Vector3
type Split          = Vector3
type LeafValue a    = (Vector3, a)
type DefOutput      = (BBox3, [LeafValue DefNodeValue ])
type DefNodeValue   = Int

-- |  newBBox3 creates a smaller BBox3
--    Given Node name, previous BBox3, and the split
newBBox3 :: BBox3 -> Split -> ODir -> BBox3
newBBox3 bbx split' SWD =
  bound_corners swdCorner neuCorner
    where
      swdCorner = Vector3 (minX bbx) (minY bbx) (minZ bbx)
      neuCorner = Vector3 (v3x split') (v3y split') (v3z split')

newBBox3 bbx split' SED =
  bound_corners swdCorner neuCorner
    where
      swdCorner = Vector3 (v3x split') (minY bbx) (minZ bbx)
      neuCorner = Vector3 (maxX bbx) (v3y split') (v3z split')

newBBox3 bbx split' NWD =
  bound_corners swdCorner neuCorner
    where
      swdCorner = Vector3 (minX bbx) (v3y split') (minZ bbx)
      neuCorner = Vector3 (v3x split') (maxY bbx) (v3z split')

newBBox3 bbx split' NED =
  bound_corners swdCorner neuCorner
    where
      swdCorner = Vector3 (v3x split') (v3y split') (minZ bbx)
      neuCorner = Vector3 (maxX bbx) (maxY bbx) (v3z split')

newBBox3 bbx split' SWU =
  bound_corners swdCorner neuCorner
    where
      swdCorner = Vector3 (minX bbx) (minY bbx) (v3z split')
      neuCorner = Vector3 (v3x split') (v3y split') (maxZ bbx)

newBBox3 bbx split' SEU =
  bound_corners swdCorner neuCorner
    where
      swdCorner = Vector3 (v3x split') (minY bbx) (v3z split')
      neuCorner = Vector3 (maxX bbx) (v3y split') (maxZ bbx)

newBBox3 bbx split' NWU =
  bound_corners swdCorner neuCorner
    where
      swdCorner = Vector3 (minX bbx) (v3y split') (v3z split')
      neuCorner = Vector3 (v3x split') (maxY bbx) (maxZ bbx)

newBBox3 bbx split' NEU =
  bound_corners swdCorner neuCorner
    where
      swdCorner = Vector3 (v3x split') (v3y split') (v3z split')
      neuCorner = Vector3 (maxX bbx) (maxY bbx) (maxZ bbx)

-- | filterNodes is default function for BBoxConfig 
--   used to recurse down octree identifying which BBox3s contain DefInput
filterNodes :: BBox3 -> DefInput -> Maybe DefInput
filterNodes bbox x = if within_bounds x bbox then Just x else Nothing

-- | points is default function for BBoxConfig
-- pre-processes Leaf
points :: BBox3 -> DefInput -> [LeafValue DefNodeValue ] -> DefOutput
points box _ leaf = (box, leaf)

-- | result reduces the list of all BBoxes containing the point to
--   the terminal BBox
result :: DefInput -> [DefOutput ] -> DefOutput 
result _ (x:xs) = foldl' findTerminal x xs
  where
    findTerminal :: DefOutput -> DefOutput -> DefOutput
    findTerminal bbox1@(bbox1',_) bbox2@(bbox2',_)
      | inclusive bbox1' bbox2' = bbox1
      | otherwise               = bbox2

-- | Supplied boolean test for result function
--   Returns True if Box b1 is contained within Box b2
inclusive :: BBox3 -> BBox3 -> Bool
inclusive b1 b2 =
  case isect b1 b2 of
    Just b3 -> b1 == b3
    Nothing -> False