{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}

module Glazier.React.Notice.Internal
  ( Notice(..)  -- constructor is exported
  , preventDefault
  , isDefaultPrevented
  , stopPropagation
  , isPropagationStopped
  , unsafeGetProperty
  , unsafeGetModifierState
  )
where

import qualified GHC.Generics as G
import qualified GHCJS.Foreign as J
import qualified GHCJS.Marshal.Pure as J
import qualified GHCJS.Types as J
import qualified JavaScript.Extras as JE

-- | Every event in React is a synthetic event, a cross-browser wrapper around the native event.
-- which reused from a pool.
-- So it is dangerous to keep a reference to a 'Notice' since it may expire and contain
-- other things without you knowing.
-- All relevant data from the 'Notice' must be consumed as soon you get one.
-- That is, 'Notice' must only be used in the first part of 'handleEvent'.
-- It is not an instance of NFData and so cannot be returned into the second lazy part of 'handleEvent'
newtype Notice = Notice JE.JSRep -- not J.JSVal so the show instance is more useful
    deriving (G.Generic, Show)

instance JE.FromJS Notice where
    fromJS a | js_isNotice a = Just $ Notice $ JE.JSRep a
    fromJS _ = Nothing

preventDefault :: Notice -> IO ()
preventDefault = js_preventDefault

isDefaultPrevented :: Notice -> Bool
isDefaultPrevented = js_isDefaultPrevented

stopPropagation :: Notice -> IO ()
stopPropagation = js_stopPropagation

isPropagationStopped :: Notice -> Bool
isPropagationStopped = js_isPropagationStopped

-- | Within the strict part of 'handleEventM'
-- the Notice is effectively immutable.
-- We want to maintain this lie so that we can lazily parse only the
-- properties the event handler is interested in.
-- This will throw if J.JSVal is null, or not convertible to the desired type
-- so we are assuming that Notice will behave nicely.
unsafeGetProperty :: J.PFromJSVal a => J.JSVal -> J.JSString -> a
unsafeGetProperty v = J.pFromJSVal . js_unsafeGetProperty v

-- | See https://www.w3.org/TR/DOM-Level-3-Events-key/#keys-modifier
-- This will throw if J.JSVal is null, but shouldn't happen since we've
-- already check for a valid Notice
unsafeGetModifierState :: J.JSVal -> J.JSString -> Bool
unsafeGetModifierState obj k = J.fromJSBool $ js_unsafeGetModifierState obj k

#ifdef __GHCJS__

foreign import javascript unsafe
    "$1 && $1['nativeEvent'] && $1['nativeEvent'] instanceof Event"
    js_isNotice :: J.JSVal -> Bool

foreign import javascript unsafe
    "$1['preventDefault']()"
    js_preventDefault :: Notice -> IO ()

foreign import javascript unsafe
    "$1['isDefaultPrevented']()"
    js_isDefaultPrevented :: Notice -> Bool

foreign import javascript unsafe
    "$1['stopPropagation']()"
    js_stopPropagation :: Notice -> IO ()

foreign import javascript unsafe
    "$1['isPropagationStopped']()"
    js_isPropagationStopped :: Notice -> Bool

-- | unsafe and non-IO to enable lazy parsing. See handleEvent
foreign import javascript unsafe "$1[$2]"
  js_unsafeGetProperty :: J.JSVal -> J.JSString -> J.JSVal

-- | unsafe to enable lazy parsing. See handleEvent
foreign import javascript unsafe
    "$1['getModifierState']($2)"
    js_unsafeGetModifierState :: J.JSVal -> J.JSString -> J.JSVal

#else

js_isNotice :: J.JSVal -> Bool
js_isNotice _ = False

js_preventDefault :: Notice -> IO ()
js_preventDefault _ = pure ()

js_isDefaultPrevented :: Notice -> Bool
js_isDefaultPrevented _ = False

js_stopPropagation :: Notice -> IO ()
js_stopPropagation _ = pure ()

js_isPropagationStopped :: Notice -> Bool
js_isPropagationStopped _ = False

-- | unsafe and non-IO to enable lazy parsing. See handleEvent
js_unsafeGetProperty :: J.JSVal -> J.JSString -> J.JSVal
js_unsafeGetProperty _ _ = J.nullRef

-- | unsafe to enable lazy parsing. See handleEvent
js_unsafeGetModifierState :: J.JSVal -> J.JSString -> J.JSVal
js_unsafeGetModifierState _ _ = J.nullRef

#endif