{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase                 #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE QuasiQuotes                #-}


module Shpadoinkle.WebWorker where


import           Control.Monad               (void)
import           Data.Text
import           GHCJS.DOM
import           Language.Javascript.JSaddle
import           Text.RawString.QQ


newtype Worker = Worker { unWorker :: JSVal }
  deriving (ToJSVal)


createWorkerJS :: Text
createWorkerJS = [r|createWorker = function (workerUrl) {
  var worker = null;
  try {
    worker = new Worker(workerUrl);
  } catch (e) {
    try {
      var blob;
      try {
        blob = new Blob(["importScripts('" + workerUrl + "');"], { "type": 'application/javascript' });
      } catch (e1) {
        var blobBuilder = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder)();
        blobBuilder.append("importScripts('" + workerUrl + "');");
        blob = blobBuilder.getBlob('application/javascript');
      }
      var url = window.URL || window.webkitURL;
      var blobUrl = url.createObjectURL(blob);
      worker = new Worker(blobUrl);
    } catch (e2) {
      //if it still fails, there is nothing much we can do
    }
  }
  return worker;
}|]


createWorker :: MonadJSM m => Text -> m Worker
createWorker url = liftJSM $ do
  _ <- eval createWorkerJS
  w <- toJSVal =<< currentWindowUnchecked
  u <- toJSVal url
  Worker <$> (w # ("createWorker" :: Text) $ [u])


postMessage :: ToJSVal a => MonadJSM m => Worker -> a -> m ()
postMessage (Worker worker) msg = liftJSM $ do
  v <- toJSVal msg
  () <$ (worker # ("postMessage" :: Text) $ [v])


postMessage' :: ToJSVal a => MonadJSM m => a -> m ()
postMessage' msg = liftJSM $ do
  self <- jsg ("self" :: Text)
  m <- toJSVal msg
  () <$ (self # ("postMessage" :: Text) $ m)


hackWindow :: MonadJSM m => m ()
hackWindow = void . liftJSM $ eval ("window = self" :: Text)


onMessage :: ToJSVal mailbox => FromJSVal message => MonadJSM m => mailbox -> (Maybe message -> JSM ()) -> m ()
onMessage mailbox f = liftJSM $ do
  box <- toJSVal mailbox
  (box <# ("onmessage" :: Text)) =<< toJSVal (fun (\_ _ -> \case
    [v] -> f =<< fromJSVal =<< (v ! ("data" :: Text))
    _   -> return ()))