-- | Bit-level type casts and byte layout string typecasts.
module Sound.OpenSoundControl.Cast (f32_i32, i32_f32,
                                    f64_i64, i64_f64,
                                    str_cstr, cstr_str,
                                    str_pstr, pstr_str) where

import Control.Monad.ST
import Data.Array.ST
import Data.Char
import Data.Int
import Data.Word

-- | The IEEE byte representation of a float packed into an integer.
f32_i32 :: Float -> Int32
f32_i32 d = runST ((from_array =<< castSTUArray =<< unit_array d) :: ST s Int32)

-- | Inverse of 'f32_i32'.
i32_f32 :: Int32 -> Float
i32_f32 d = runST ((from_array =<< castSTUArray =<< unit_array d) :: ST s Float)

-- | The IEEE byte representation of a double packed into an integer.
f64_i64 :: Double -> Int64
f64_i64 d = runST ((from_array =<< castSTUArray =<< unit_array d) :: ST s Int64)

{-
http://www.haskell.org/pipermail/haskell-cafe/2008-February/040000.html

{-# LANGUAGE MagicHash #-}
import Data.Int
import GHC.Exts

f64_i64 :: Double -> Int64 --ghc only
f64_i64 (D# x) = I64# (unsafeCoerce# x)
-}

-- | Inverse of 'f64_i64'.
i64_f64 :: Int64 -> Double
i64_f64 d = runST ((from_array =<< castSTUArray =<< unit_array d) :: ST s Double)

-- | Transform a haskell string into a C string (a null suffixed byte string).
str_cstr :: String -> [Word8]
str_cstr s = map (fromIntegral . ord) s ++ [0]

-- | Inverse of 'str_cstr'.
cstr_str :: [Word8] -> String
cstr_str = map (chr . fromIntegral) . takeWhile (/= 0)

-- | Transform a haskell string to a pascal string (a length prefixed byte string).
str_pstr :: String -> [Word8]
str_pstr s = fromIntegral (length s) : map (fromIntegral . ord) s

-- | Inverse of 'str_pstr'.
pstr_str :: [Word8] -> String
pstr_str = map (chr . fromIntegral) . drop 1

-- One element marray.
unit_array :: (MArray a e m) => e -> m (a Int e)
unit_array = newArray (0, 0::Int)

-- Extract first element from array.
from_array :: (MArray a e m) => a Int e -> m e
from_array = flip readArray 0