module Waterfall.Offset 
( offset
) where 

import Waterfall.Internal.Solid (Solid (..), acquireSolid, solidFromAcquire)
import qualified OpenCascade.BRepOffsetAPI.MakeOffsetShape as MakeOffsetShape
import Control.Monad.IO.Class (liftIO)
import OpenCascade.Inheritance (SubTypeOf(upcast), unsafeDowncast)
import qualified OpenCascade.BRepBuilderAPI.MakeShape as MakeShape
import qualified OpenCascade.BRepOffset.Mode as Mode
import qualified OpenCascade.GeomAbs.JoinType as GeomAbs.JoinType
import qualified OpenCascade.BRepBuilderAPI.MakeSolid as MakeSolid
import qualified OpenCascade.TopoDS.Types as TopoDS
import qualified OpenCascade.TopExp.Explorer as TopExp.Explorer
import qualified OpenCascade.TopAbs.ShapeEnum as TopAbs.ShapeEnum
import Control.Monad (when)
import Foreign.Ptr (Ptr)
import Data.Acquire (Acquire)
import Linear.Epsilon (nearZero)

combineShellsToSolid :: Ptr TopoDS.Shape -> Acquire (Ptr TopoDS.Shape)
combineShellsToSolid :: Ptr Shape -> Acquire (Ptr Shape)
combineShellsToSolid Ptr Shape
s = do
    explorer <- Ptr Shape -> ShapeEnum -> Acquire (Ptr Explorer)
TopExp.Explorer.new Ptr Shape
s ShapeEnum
TopAbs.ShapeEnum.Shell
    makeSolid <- MakeSolid.new
    let go = do
            isMore <- IO Bool -> Acquire Bool
forall a. IO a -> Acquire a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Bool -> Acquire Bool) -> IO Bool -> Acquire Bool
forall a b. (a -> b) -> a -> b
$ Ptr Explorer -> IO Bool
TopExp.Explorer.more Ptr Explorer
explorer
            when isMore $ do
                shell <- liftIO $ unsafeDowncast =<< TopExp.Explorer.value explorer
                liftIO $ MakeSolid.add makeSolid shell
                liftIO $ TopExp.Explorer.next explorer
                go
    go
    upcast <$> MakeSolid.solid makeSolid

-- | Expand or contract a `Solid` by a certain amount.
-- 
-- This seems to be relatively fragile on Sweeps/Prisms
-- (as in, may fail to produce a result)
offset :: Double    -- ^ Amount to offset by, positive values expand, negative values contract
    -> Double       -- ^ Tolerance, this can be relatively small
    -> Solid        -- ^ the `Solid` to offset 
    -> Solid
offset :: Double -> Double -> Solid -> Solid
offset Double
value Double
tolerance Solid
solid
    | Double -> Bool
forall a. Epsilon a => a -> Bool
nearZero Double
value = Solid
solid
    | Bool
otherwise = 
  Acquire (Ptr Shape) -> Solid
solidFromAcquire (Acquire (Ptr Shape) -> Solid) -> Acquire (Ptr Shape) -> Solid
forall a b. (a -> b) -> a -> b
$ do
    builder <- Acquire (Ptr MakeOffsetShape)
MakeOffsetShape.new
    s <- acquireSolid solid 
    --liftIO $ MakeOffsetShape.performBySimple builder s value
    liftIO $ MakeOffsetShape.performByJoin builder s value tolerance Mode.Skin False False GeomAbs.JoinType.Arc False 
    shell <- MakeShape.shape (upcast builder)
    combineShellsToSolid shell