{-|
Copyright   : Written by David Himmelstrup
License     : Unlicense
Maintainer  : lemmih@gmail.com
Stability   : experimental
Portability : POSIX
-}
module Reanimate.ColorSpace
  ( Nanometer
  , coneSensitivity
  , bigXYZCoordinates
  , lightXYZCoordinates
  , nmToColor
  ) where

import qualified Data.ByteString.Lazy       as BS
import           Data.Colour.CIE
import           Data.Csv
import           Data.Map                   (Map)
import qualified Data.Map                   as Map
import           Data.Maybe
import qualified Data.Vector                as V
import           Paths_reanimate
import           System.IO.Unsafe

-- | Wavelengths in nanometers.
type Nanometer = Integer

{-# NOINLINE lightXYZCoordinates #-}
-- | (small) xyz values for each wavelength of light.
lightXYZCoordinates :: Map Nanometer (Double, Double, Double)
lightXYZCoordinates = unsafePerformIO $ do
  dat <- BS.readFile =<< getDataFileName "data/CIExyz.csv"
  case decode NoHeader dat of
    Left err -> error err
    Right vec -> return $ Map.fromList
      [ (nm, (x,y,z)) | (nm,x,y,z) <- V.toList vec, nm <= 700 ]

{-# NOINLINE bigXYZCoordinates #-}
-- | (big) XYZ values for each wavelength of light.
bigXYZCoordinates :: Map Nanometer (Double, Double, Double)
bigXYZCoordinates = unsafePerformIO $ do
  dat <- BS.readFile =<< getDataFileName "data/CIE_XYZ.csv"
  case decode NoHeader dat of
    Left err -> error err
    Right vec -> return $ Map.fromList
      [ (nm, (x,y,z)) | (nm,x,y,z) <- V.toList vec, nm <= 700 ]

-- | Helper function for converting a wavelength of light into the
--   perceived color.
nmToColor :: Nanometer -> Maybe (Colour Double)
nmToColor nm = do
  (x, y, z) <- Map.lookup nm bigXYZCoordinates
  return $ cieXYZ x y z

{-# NOINLINE coneSensitivity #-}
-- (Long, Medium, Short)
-- | Cone sensitivity by light wavelength.
coneSensitivity :: Map Nanometer (Double, Double, Double)
coneSensitivity = unsafePerformIO $ do
  dat <- BS.readFile =<< getDataFileName "data/cone_sensitivity_lms.csv"
  case decode NoHeader dat of
    Left err -> error err
    Right vec -> return $ Map.fromList
      [ (nm, (l,m,fromMaybe 0 s)) | (nm,l,m,s) <- V.toList vec, nm <= 700 ]