{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}

module Data.Foreign where

-- #if !defined(javascript_HOST_ARCH)
import GHC.Base (isTrue#, reallyUnsafePtrEquality, reallyUnsafePtrEquality#)
-- #endif

import HPrelude
import Unsafe.Coerce (unsafeCoerce)

#if defined(javascript_HOST_ARCH)
import GHC.Base (Int#)
import GHC.JS.Prim

type Foreign tag = JSVal

newtype Nullable tag = Nullable (Foreign tag)

foreign import javascript unsafe "(($1) => { return (!!$1 ? 1 : 0); })" foreignToBool' :: Foreign tag -> Int#

nullableToMaybe :: Nullable tag -> Maybe (Foreign tag)
nullableToMaybe (Nullable o) = if isNull o || isUndefined o then Nothing else Just o

toForeign :: a -> Foreign tag
toForeign = unsafeCoerce

unsafeFromForeign :: Foreign tag -> a
unsafeFromForeign = unsafeCoerce

readProp :: Text -> (Foreign tag -> Maybe a) -> Foreign tag' -> Maybe a
readProp key f o = nullableToMaybe (Nullable $ unsafeGetProp o (toS key)) >>= f

-- TODO this doesn't work for some reason
--foreign import javascript unsafe "js_unsafe_ref_eq" js_unsafe_ref_eq :: JSVal -> JSVal -> Bool

--unsafeRefEq' :: a -> b -> Bool
--unsafeRefEq' a b = js_unsafe_ref_eq (unsafeCoerce a) (unsafeCoerce b)

--unsafeRefEq :: a -> a -> Bool
--unsafeRefEq a b = unsafeRefEq' a b

foreignToString :: Foreign tag -> Text
foreignToString = toS . fromJSString

foreignToInt :: Foreign tag -> Int
foreignToInt = fromJSInt

foreignToBool :: Foreign tag -> Bool
foreignToBool x = isTrue# (foreignToBool' x)
#else

newtype Foreign tag = Foreign Any

type Nullable tag = Maybe (Foreign tag)

nullableToMaybe :: Nullable tag -> Maybe (Foreign tag)
nullableToMaybe :: forall {k} (tag :: k). Nullable tag -> Nullable tag
nullableToMaybe = Nullable tag -> Nullable tag
forall a. a -> a
identity

toForeign :: a -> Foreign tag
toForeign :: forall {k} a (tag :: k). a -> Foreign tag
toForeign = Any -> Foreign tag
forall {k} (tag :: k). Any -> Foreign tag
Foreign (Any -> Foreign tag) -> (a -> Any) -> a -> Foreign tag
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Any
forall a b. a -> b
unsafeCoerce

unsafeFromForeign :: Foreign tag -> a
unsafeFromForeign :: forall {k} (tag :: k) a. Foreign tag -> a
unsafeFromForeign (Foreign Any
o) = Any -> a
forall a b. a -> b
unsafeCoerce Any
o

readProp :: Text -> (Foreign tag -> Maybe a) -> Foreign tag' -> Maybe a
readProp :: forall {k} {k} (tag :: k) a (tag' :: k).
Text -> (Foreign tag -> Maybe a) -> Foreign tag' -> Maybe a
readProp = Text -> Text -> (Foreign tag -> Maybe a) -> Foreign tag' -> Maybe a
forall a. HasCallStack => Text -> a
panic Text
"Unavailable in GHC" -- TODO

foreignToString :: Foreign tag -> Text
foreignToString :: forall {k} (tag :: k). Foreign tag -> Text
foreignToString = Foreign tag -> Text
forall a b. a -> b
unsafeCoerce

foreignToInt :: Foreign tag -> Int
foreignToInt :: forall {k} (tag :: k). Foreign tag -> Int
foreignToInt = Foreign tag -> Int
forall a b. a -> b
unsafeCoerce

foreignToBool :: Foreign tag -> Bool
foreignToBool :: forall {k} (tag :: k). Foreign tag -> Bool
foreignToBool = Foreign tag -> Bool
forall {k} (tag :: k) a. Foreign tag -> a
unsafeFromForeign
#endif

unsafeRefEq :: a -> a -> Bool
unsafeRefEq :: forall a. a -> a -> Bool
unsafeRefEq a
p a
q = Int# -> Bool
isTrue# (a -> a -> Int#
forall a. a -> a -> Int#
reallyUnsafePtrEquality a
p a
q)

unsafeRefEq' :: a -> b -> Bool
unsafeRefEq' :: forall a b. a -> b -> Bool
unsafeRefEq' a
p b
q = Int# -> Bool
isTrue# (a -> b -> Int#
forall a b. a -> b -> Int#
reallyUnsafePtrEquality# a
p b
q)