{-# LANGUAGE ExistentialQuantification #-}
module Core.Types where

import Core.Constants
import Core.Geometry
import Core.Transform

data Intersection = Intersection {tHit         :: DFloat,
                                  diffGeom     :: DiffGeom,
                                  worldToInter :: Transform,
                                  interToWorld :: Transform,
                                  rayEpsilon   :: DFloat}

data DiffGeom = DiffGeom {
  p     :: Point,
  n     :: Normal,
  u     :: DFloat,
  v     :: DFloat,
  dpdu  :: Vector,
  dpdv  :: Vector,
  dndu  :: Normal,
  dndv  :: Normal,
  dudx  :: DFloat,
  dvdx  :: DFloat,
  dudy  :: DFloat,
  dvdy  :: DFloat,
  dpdx  :: Vector,
  dpdy  :: Vector,
  shape :: Shapeable
}

-- | Existentially quantified type to unify different instances of the 'Shape'
-- typeclass. Shapes must either be instances of 'Intersectable' or 'Refineable'
data Shapeable =
    forall a . (Shape a, Intersectable a) => Intersectable a
  | forall a . (Shape a, Refineable a)    => Refineable a

-- | Interface for Shapes
class Shape a where
  objectToWorld      :: a -> Transform
  worldToObject      :: a -> Transform
  objectBound        :: a -> BBox
  worldBound         :: a -> BBox
  reverseOrientation :: a -> Bool
  reverseNormals     :: a -> Bool -- TODO: Difference betweehn this and orientation?
  swapsHandedness    :: a -> Bool
  -- shadingGeometry :: a -> Transform -> DifferentialGeometry -> DifferentialGeometry
  area :: a -> DFloat
  -- sample :: a -> DFloat -> DFloat -> Normal -> Point
  -- pdf :: a -> Point -> DFloat

class Refineable a where
  refine :: a -> [Shapeable]

-- | Interface for intersecting with objects. Override 'IntersectP' with a
-- faster implementation, if possible. Note that using the intersect
class Intersectable a where
  intersect  :: a -> Ray -> Maybe Intersection
  intersectP :: a -> Ray -> Bool
