module BabyRays.Light where

import Core.Constants
import Core.Geometry

import BabyRays.Intersection
import BabyRays.Color
import BabyRays.Shape
import BabyRays.Types

import Debug.Trace (trace)

import Data.List (minimumBy, delete)
import Data.Function (on)

-- -| trace (show firstHit) False
-- Is the light at 'lightpos' visible from point?
point_is_lit :: Shape -> [Shape] -> Point -> Point -> Bool
point_is_lit shape shapes point lightpos = null times || minimum times > timeAtLight
  where path = lightpos .-. point
        timeAtLight = norm path
        ray = Ray {origin = point, direction = hat path}
        hits = filter (\(t, _) -> t > 0.01) . concatMap (intersect ray) $ delete shape shapes
        times = fst (unzip hits)

        firstHit = minimumBy (compare `on` fst) hits

-- Helper to calculate the diffuse light at the surface normal, given
-- the light direction (from light source to surface)
diffuse_coeff :: Vector -> Vector -> DFloat
diffuse_coeff light_dir normal = max 0.0 (negate ((hat light_dir) `dot` (hat normal)))

localLight :: [Shape] -> Intersection -> Light -> Color
localLight _ (normal,_,_,shape,(materialcol,_,kd)) (Directional dir lightcol) = diffuse
   where mixed_color = combine_col materialcol lightcol
         diffuse = scale_col mixed_color ((diffuse_coeff dir (vecOfNormal normal)) * kd)

-- Spotlight - shadowable
localLight shapes (normal, hitpoint,_, shape, (materialcol,_,kd)) (Spotlight lightpos lightcol) =
   let mixed_color = combine_col materialcol lightcol
       normal' = vecOfNormal normal
       diffuse = scale_col mixed_color (kd * (diffuse_coeff (hitpoint .-. lightpos) normal'))
   in if point_is_lit shape shapes hitpoint lightpos
      then diffuse
      else black
