{-# LANGUAGE CPP #-}
module Internal.API where

import           Internal.FFI
import           Internal.Type

#ifdef ghcjs_HOST_OS
import           Data.JSString
import           GHCJS.Foreign.Callback (Callback, asyncCallback1)
import           GHCJS.Marshal          (FromJSVal (..))
import           GHCJS.Types            (JSVal)
#else
data Callback a = Callback a
#endif
--------------------------------------------------------------------------------


--------------------------------------------------------------------------------
#ifndef ghcjs_HOST_OS
notImplemented :: a
notImplemented = error "Client side call not implemented on server side."
#endif

getDocument :: IO Elem
#ifdef ghcjs_HOST_OS
getDocument = Elem <$> js_document
#else
getDocument = notImplemented
#endif

getBody :: IO Elem
#ifdef ghcjs_HOST_OS
getBody = Elem <$> js_documentBody
#else
getBody = notImplemented
#endif

newElem :: JSString -> IO Elem
#ifdef ghcjs_HOST_OS
newElem = (Elem <$>) . js_documentCreateNode
#else
newElem = notImplemented
#endif

newTextElem :: JSString -> IO Elem
#ifdef ghcjs_HOST_OS
newTextElem = (Elem <$>) . js_createTextNode
#else
newTextElem = notImplemented
#endif

parent :: Elem -> IO Elem
#ifdef ghcjs_HOST_OS
parent (Elem c) = Elem <$> js_parentNode c
#else
parent = notImplemented
#endif

-- | Appends one element to another.
addChild :: Elem -- ^ child element to append
         -> Elem -- ^ parent element
         -> IO ()
#ifdef ghcjs_HOST_OS
addChild (Elem c) (Elem p) = js_appendChild p c
#else
addChild = notImplemented
#endif

-- | Remove child from parent.
removeChild :: Elem -- ^ child to remove
            -> Elem -- ^ parent node
            -> IO ()
#ifdef ghcjs_HOST_OS
removeChild (Elem c) (Elem p) = js_removeChild p c
#else
removeChild = notImplemented
#endif

clearChildren :: Elem -> IO ()
#ifdef ghcjs_HOST_OS
clearChildren (Elem e) = js_clearChildren e
#else
clearChildren = notImplemented
#endif

replace :: Elem -> Elem -> IO Elem
#ifdef ghcjs_HOST_OS
replace oe@(Elem o) (Elem n) =
  do (Elem par) <- parent oe
     js_replaceChild par o n
     return (Elem n)
#else
replace = notImplemented
#endif

setAttr :: Elem -> PropId -> JSString -> IO ()
#ifdef ghcjs_HOST_OS
setAttr (Elem e) p = js_setAttribute e p
#else
setAttr = notImplemented
#endif

setInnerHTML :: Elem -> JSString -> IO ()
#ifdef ghcjs_HOST_OS
setInnerHTML (Elem e) = js_setInnerHtml e
#else
setInnerHTML = notImplemented
#endif

getElemById :: JSString -> IO Elem
#ifdef ghcjs_HOST_OS
getElemById = (Elem <$>) . js_getElementById
#else
getElemById = notImplemented
#endif

queryAll :: JSString -> IO [Elem]
#ifdef ghcjs_HOST_OS
queryAll query =
  do res <- js_querySelectorAll query
     fromJSValUncheckedListOf res
#else
queryAll = notImplemented
#endif

-- | Attach an event listener to element.
--
-- Returns an action removing listener, though you still have to release
-- callback manually.
--
-- If you are sure that you do not want to remove handler consider using
-- 'onEvent''.
onEvent :: NamedEvent e => Elem -> e -> Callback (JSVal -> IO()) -> IO (IO ())

-- | Attach endless event listener to element.
--
-- Use this function to attach event handlers which supposed not to be removed
-- during application run.
onEvent' :: NamedEvent e => Elem -> e -> (JSVal -> IO()) -> IO ()

-- | Remove attached event listener.
--
-- Normally you can use action returned by 'onEvent' to detach event listener,
-- however you can also use this function directly.
removeEvent :: NamedEvent e => Elem -> e -> Callback (JSVal -> IO ()) -> IO ()
#ifdef ghcjs_HOST_OS
onEvent el'@(Elem el) et cb =
  do js_addEventListener el e cb
     return $ removeEvent el' e cb
  where
    e = pack (eventName et)

onEvent' (Elem el) et hnd =
  do cb <- asyncCallback1 hnd
     js_addEventListener el e cb
  where
    e = pack (eventName et)

removeEvent (Elem el) et cb = js_removeEventListener el (pack (eventName et)) cb
#else
onEvent = notImplemented
onEvent' = notImplemented
removeEvent = notImplemented
#endif
--------------------------------------------------------------------------------